/// <summary> /// Compares two trees. If the trees differ only by constants then the list of constants which differ /// is provided as a list via an out-param. The constants collected are the constants in the left /// side of the tree and only include constants which differ in value. /// </summary> public static bool Compare(Expression left, Expression right, out List <ConstantExpression> replacementNodes, out bool tooSpecific) { replacementNodes = null; tooSpecific = false; FlatTreeWalker walkLeft = new FlatTreeWalker(); FlatTreeWalker walkRight = new FlatTreeWalker(); walkLeft.Visit(left); Debug.Assert(walkLeft._templated == null); walkRight.Visit(right); // check the length first to see if the trees are obviously different if (walkLeft.Expressions.Count != walkRight.Expressions.Count) { return(false); } // then see if they differ by just constants which we could replace List <ConstantExpression> needsReplacement = new List <ConstantExpression>(); VariableInfo varInfo = new VariableInfo(); for (int i = 0; i < walkLeft.Expressions.Count; i++) { Expression currentLeft = walkLeft.Expressions[i], currentRight = walkRight.Expressions[i]; // ReductionRewriter should have removed these if (currentLeft.NodeType != currentRight.NodeType) { // different node types, they can't possibly be equal return(false); } else if (currentLeft.Type != currentRight.Type) { // they can't possibly be a match return(false); } if (!CompareTwoNodes(walkRight, needsReplacement, varInfo, currentLeft, currentRight, ref tooSpecific)) { return(false); } } replacementNodes = needsReplacement; return(true); }
private static bool CompareTwoNodes(FlatTreeWalker walkRight, List <ConstantExpression> needsReplacement, VariableInfo varInfo, Expression currentLeft, Expression currentRight, ref bool tooSpecific) { switch (currentLeft.NodeType) { case ExpressionType.Dynamic: var dynLeft = (DynamicExpression)currentLeft; var dynRight = (DynamicExpression)currentRight; if (!dynRight.Binder.CacheIdentity.Equals(dynLeft.Binder.CacheIdentity)) { return(false); } break; case ExpressionType.Constant: // check constant value ConstantExpression ceLeft = (ConstantExpression)currentLeft; ConstantExpression ceRight = (ConstantExpression)currentRight; object leftValue = ceLeft.Value; object rightValue = ceRight.Value; if (leftValue == null && rightValue == null) { // both are null, no need to template this param. break; } // See if they're both sites CallSite leftSite = ceLeft.Value as CallSite; CallSite rightSite = ceRight.Value as CallSite; if (leftSite != null) { if (rightSite == null) { return(false); } if (!leftSite.Binder.CacheIdentity.Equals(rightSite.Binder.CacheIdentity)) { return(false); } return(true); } else if (rightSite != null) { return(false); } // add if left is null and right's something else or // left and right aren't equal. We'll also add it if // the existing rule has hoisted this value into a template // parameter. if (leftValue == null || !leftValue.Equals(rightValue) || walkRight.IsTemplatedConstant(ceRight)) { if (walkRight._templated != null && !walkRight.IsTemplatedConstant(ceRight)) { // if we have template args on the right hand side and this isn't // one of them we need to re-compile a more general rule. tooSpecific = true; } needsReplacement.Add(ceLeft); } break; case ExpressionType.Equal: case ExpressionType.NotEqual: if (!CompareEquality((BinaryExpression)currentLeft, (BinaryExpression)currentRight)) { return(false); } break; case ExpressionType.Add: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: case ExpressionType.Divide: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.AddAssign: case ExpressionType.SubtractAssign: case ExpressionType.MultiplyAssign: case ExpressionType.AddAssignChecked: case ExpressionType.SubtractAssignChecked: case ExpressionType.MultiplyAssignChecked: case ExpressionType.DivideAssign: case ExpressionType.ModuloAssign: case ExpressionType.PowerAssign: case ExpressionType.AndAssign: case ExpressionType.OrAssign: case ExpressionType.RightShiftAssign: case ExpressionType.LeftShiftAssign: case ExpressionType.ExclusiveOrAssign: if (!Compare((BinaryExpression)currentLeft, (BinaryExpression)currentRight)) { return(false); } break; case ExpressionType.Call: if (!Compare((MethodCallExpression)currentLeft, (MethodCallExpression)currentRight)) { return(false); } break; case ExpressionType.New: // chcek ConstructorInfo and BindingInfo if (!Compare((NewExpression)currentLeft, (NewExpression)currentRight)) { return(false); } break; case ExpressionType.TypeIs: case ExpressionType.TypeEqual: // check type if (!Compare((TypeBinaryExpression)currentLeft, (TypeBinaryExpression)currentRight)) { return(false); } break; case ExpressionType.Block: // compare factory method if (!Compare(varInfo, (BlockExpression)currentLeft, (BlockExpression)currentRight)) { return(false); } break; case ExpressionType.MemberAccess: // compare member if (!Compare((MemberExpression)currentLeft, (MemberExpression)currentRight)) { return(false); } break; case ExpressionType.Try: // compare catch finally blocks and their handler types if (!Compare(varInfo, (TryExpression)currentLeft, (TryExpression)currentRight)) { return(false); } break; case ExpressionType.Parameter: if (!Compare(varInfo, (ParameterExpression)currentLeft, (ParameterExpression)currentRight)) { return(false); } break; case ExpressionType.Lambda: case ExpressionType.Assign: case ExpressionType.Goto: case ExpressionType.Throw: case ExpressionType.Loop: case ExpressionType.Default: case ExpressionType.Convert: case ExpressionType.TypeAs: case ExpressionType.Unbox: case ExpressionType.Negate: case ExpressionType.Not: case ExpressionType.Conditional: case ExpressionType.NewArrayInit: case ExpressionType.NewArrayBounds: case ExpressionType.Invoke: // these nodes children and types completely // define the node break; case ExpressionType.Label: // TODO: cache and compare labels case ExpressionType.Switch: // TODO: compare case values case ExpressionType.Extension: // we should have been reduced, but error on the side of being different. return(false); default: throw Assert.Unreachable; } return(true); }