-
Notifications
You must be signed in to change notification settings - Fork 128
Share intrinsic handling of GetMember and similar APIs #2639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,12 +22,15 @@ public class TrimAnalysisVisitor : LocalDataFlowVisitor<MultiValue, ValueSetLatt | |
| { | ||
| public readonly TrimAnalysisPatternStore TrimAnalysisPatterns; | ||
|
|
||
| readonly ValueSetLattice<SingleValue> _multiValueLattice; | ||
|
|
||
| public TrimAnalysisVisitor ( | ||
| LocalStateLattice<MultiValue, ValueSetLattice<SingleValue>> lattice, | ||
| OperationBlockAnalysisContext context | ||
| ) : base (lattice, context) | ||
| { | ||
| TrimAnalysisPatterns = new TrimAnalysisPatternStore (lattice.Lattice.ValueLattice); | ||
| _multiValueLattice = lattice.Lattice.ValueLattice; | ||
| TrimAnalysisPatterns = new TrimAnalysisPatternStore (_multiValueLattice); | ||
| } | ||
|
|
||
| // Override visitor methods to create tracked values when visiting operations | ||
|
|
@@ -36,6 +39,26 @@ OperationBlockAnalysisContext context | |
| // - 'this' parameter (for annotated methods) | ||
| // - field reference | ||
|
|
||
| public override MultiValue Visit (IOperation? operation, StateValue argument) | ||
| { | ||
| var returnValue = base.Visit (operation, argument); | ||
|
|
||
| // If the return value is empty (TopValue basically) and the Operation tree | ||
| // reports it as having a constant value, use that as it will automatically cover | ||
| // cases we don't need/want to handle. | ||
| if (operation != null && returnValue.IsEmpty () && operation.ConstantValue.HasValue) { | ||
| object? constantValue = operation.ConstantValue.Value; | ||
| if (constantValue == null) | ||
| return NullValue.Instance; | ||
| else if (operation.Type?.SpecialType == SpecialType.System_String && constantValue is string stringConstantValue) | ||
| return new KnownStringValue (stringConstantValue); | ||
| else if (operation.Type?.TypeKind == TypeKind.Enum && constantValue is int intConstantValue) | ||
| return new ConstIntValue (intConstantValue); | ||
| } | ||
|
|
||
| return returnValue; | ||
| } | ||
|
|
||
| public override MultiValue VisitConversion (IConversionOperation operation, StateValue state) | ||
| { | ||
| var value = base.VisitConversion (operation, state); | ||
|
|
@@ -70,14 +93,15 @@ public override MultiValue VisitInstanceReference (IInstanceReferenceOperation i | |
|
|
||
| public override MultiValue VisitFieldReference (IFieldReferenceOperation fieldRef, StateValue state) | ||
| { | ||
| if (!fieldRef.Field.Type.IsTypeInterestingForDataflow ()) | ||
| return TopValue; | ||
| if (fieldRef.Field.Type.IsTypeInterestingForDataflow ()) { | ||
| var field = fieldRef.Field; | ||
| if (field.Name is "Empty" && field.ContainingType.HasName ("System.String")) | ||
| return new KnownStringValue (string.Empty); | ||
|
|
||
| var field = fieldRef.Field; | ||
| if (field.Name is "Empty" && field.ContainingType.HasName ("System.String")) | ||
| return new KnownStringValue (string.Empty); | ||
| return new FieldValue (fieldRef.Field); | ||
| } | ||
|
|
||
| return new FieldValue (fieldRef.Field); | ||
| return TopValue; | ||
| } | ||
|
|
||
| public override MultiValue VisitTypeOf (ITypeOfOperation typeOfOperation, StateValue state) | ||
|
|
@@ -90,9 +114,34 @@ public override MultiValue VisitTypeOf (ITypeOfOperation typeOfOperation, StateV | |
| return TopValue; | ||
| } | ||
|
|
||
| public override MultiValue VisitLiteral (ILiteralOperation literalOperation, StateValue state) | ||
| public override MultiValue VisitBinaryOperator (IBinaryOperation operation, StateValue argument) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried this locally and it looks like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right - I was looking at the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could maybe put it in the top-level
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm kind of split on that - it's semi-dangerous. It would mean that the actual specific operation visitor method would not have final say in what the return value is. Maybe not a big deal... I'll probably do it.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went with that approach - Visit(IOperation) now handles constants (if the operation specific visit doesn't provide a value). It solves lot of easy cases and works generally for things like null and strings as well. |
||
| { | ||
| return literalOperation.ConstantValue.Value == null ? NullValue.Instance : TopValue; | ||
| if (!operation.ConstantValue.HasValue && // Optimization - if there is already a constant value available, rely on the Visit(IOperation) instead | ||
| operation.OperatorKind == BinaryOperatorKind.Or && | ||
| operation.OperatorMethod is null && | ||
| (operation.Type?.TypeKind == TypeKind.Enum || operation.Type?.SpecialType == SpecialType.System_Int32)) { | ||
| MultiValue leftValue = Visit (operation.LeftOperand, argument); | ||
| MultiValue rightValue = Visit (operation.RightOperand, argument); | ||
|
|
||
| MultiValue result = TopValue; | ||
| foreach (var left in leftValue) { | ||
| if (left is UnknownValue) | ||
| result = _multiValueLattice.Meet (result, left); | ||
| else if (left is ConstIntValue leftConstInt) { | ||
| foreach (var right in rightValue) { | ||
| if (right is UnknownValue) | ||
| result = _multiValueLattice.Meet (result, right); | ||
| else if (right is ConstIntValue rightConstInt) { | ||
| result = _multiValueLattice.Meet (result, new ConstIntValue (leftConstInt.Value | rightConstInt.Value)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| return base.VisitBinaryOperator (operation, argument); | ||
| } | ||
|
|
||
| // Override handlers for situations where annotated locations may be involved in reflection access: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is going to cause a quadratic number of calls in the depth of the tree. Since iterators are lazy, we'll walk all the way down the tree to fetch the first element, then walk n-1 levels to fetch the second, and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I don't understand. The GetMethodsOnTypeHierarchy helper we're calling here already uses iterator, so this is basically just a pass through...
I guess I could rewrite both to use ImmutableArray...
I must admit I don't understand what the problem is.