PageRenderTime 167ms CodeModel.GetById 81ms app.highlight 31ms RepoModel.GetById 49ms app.codeStats 1ms

/Microsoft.Scripting.Core/Ast/TreeComparer.cs

https://bitbucket.org/stefanrusek/xronos
C# | 585 lines | 435 code | 92 blank | 58 comment | 89 complexity | f7c492b91843625e3c782413dfc68a32 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15using System; using Microsoft;
 16
 17
 18using System.Collections.Generic;
 19#if CODEPLEX_40
 20using System.Dynamic.Utils;
 21#else
 22using Microsoft.Scripting.Utils;
 23#endif
 24using System.Runtime.CompilerServices;
 25#if !CODEPLEX_40
 26using Microsoft.Runtime.CompilerServices;
 27#endif
 28
 29
 30#if CODEPLEX_40
 31namespace System.Linq.Expressions {
 32#else
 33namespace Microsoft.Linq.Expressions {
 34#endif
 35
 36    internal enum TreeCompareResult {
 37        Incompatible,
 38        Compatible,
 39        TooSpecific,
 40    }
 41
 42    internal class TreeComparer {
 43        #region Tree Walker
 44
 45        /// <summary>
 46        /// Walks all of the nodes of a tree and puts all of the expressions into
 47        /// a list.
 48        /// </summary>
 49        private class FlatTreeWalker : ExpressionVisitor {
 50            internal List<Expression> Expressions = new List<Expression>();
 51
 52            protected internal override Expression VisitDynamic(DynamicExpression node) {
 53                Expressions.Add(node);
 54                return base.VisitDynamic(node);
 55            }
 56
 57            protected internal override Expression VisitBinary(BinaryExpression node) {
 58                Expressions.Add(node);
 59                return base.VisitBinary(node);
 60            }
 61
 62            protected internal override Expression VisitBlock(BlockExpression node) {
 63                Expressions.Add(node);
 64                return base.VisitBlock(node);
 65            }
 66
 67            protected internal override Expression VisitGoto(GotoExpression node) {
 68                Expressions.Add(node);
 69                return base.VisitGoto(node);
 70            }
 71
 72            protected internal override Expression VisitConditional(ConditionalExpression node) {
 73                Expressions.Add(node);
 74                return base.VisitConditional(node);
 75            }
 76
 77            protected internal override Expression VisitConstant(ConstantExpression node) {
 78                Expressions.Add(node);
 79                return base.VisitConstant(node);
 80            }
 81
 82            protected internal override Expression VisitDefault(DefaultExpression node) {
 83                Expressions.Add(node);
 84                return base.VisitDefault(node);
 85            }
 86
 87            protected internal override Expression VisitInvocation(InvocationExpression node) {
 88                Expressions.Add(node);
 89                return base.VisitInvocation(node);
 90            }
 91
 92            protected internal override Expression VisitLabel(LabelExpression node) {
 93                Expressions.Add(node);
 94                return base.VisitLabel(node);
 95            }
 96
 97            protected internal override Expression VisitLambda<T>(Expression<T> node) {
 98                Expressions.Add(node);
 99                return base.VisitLambda(node);
100            }
101
102            protected internal override Expression VisitLoop(LoopExpression node) {
103                Expressions.Add(node);
104                return base.VisitLoop(node);
105            }
106
107            protected internal override Expression VisitMember(MemberExpression node) {
108                Expressions.Add(node);
109                return base.VisitMember(node);
110            }
111
112            protected internal override Expression VisitMethodCall(MethodCallExpression node) {
113                Expressions.Add(node);
114                return base.VisitMethodCall(node);
115            }
116
117            protected internal override Expression VisitNewArray(NewArrayExpression node) {
118                Expressions.Add(node);
119                return base.VisitNewArray(node);
120            }
121
122            protected internal override Expression VisitNew(NewExpression node) {
123                Expressions.Add(node);
124                return base.VisitNew(node);
125            }
126
127            protected internal override Expression VisitParameter(ParameterExpression node) {
128                Expressions.Add(node);
129                return base.VisitParameter(node);
130            }
131
132            protected internal override Expression VisitSwitch(SwitchExpression node) {
133                Expressions.Add(node);
134                return base.VisitSwitch(node);
135            }
136
137            protected internal override Expression VisitTry(TryExpression node) {
138                Expressions.Add(node);
139                return base.VisitTry(node);
140            }
141
142            protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
143                Expressions.Add(node);
144                return base.VisitTypeBinary(node);
145            }
146
147            protected internal override Expression VisitUnary(UnaryExpression node) {
148                Expressions.Add(node);
149                return base.VisitUnary(node);
150            }
151
152            protected internal override Expression VisitExtension(Expression node) {
153                if (!node.CanReduce) {
154                    Expressions.Add(node);
155                } else {
156                    return Visit(node.ReduceExtensions());
157                }
158                return node;
159            }
160        }
161
162        #endregion
163
164        private class VariableInfo {
165            private Dictionary<ParameterExpression, int> _left = new Dictionary<ParameterExpression, int>();
166            private Dictionary<ParameterExpression, int> _right = new Dictionary<ParameterExpression, int>();
167            private int _curLeft, _curRight;
168
169            internal int GetLeftVariable(ParameterExpression ve) {
170                if (ve == null) {
171                    return -1;
172                }
173
174                int res;
175                if (!_left.TryGetValue(ve, out res)) {
176                    _left[ve] = res = _curLeft++;
177                }
178
179                return res;
180            }
181
182            internal int GetRightVariable(ParameterExpression ve) {
183                if (ve == null) {
184                    return -1;
185                }
186
187                int res;
188                if (!_right.TryGetValue(ve, out res)) {
189                    _right[ve] = res = _curRight++;
190                }
191
192                return res;
193            }
194        }
195
196        private VariableInfo _varInfo;
197        private int _curConstNum;
198
199        /// <summary>
200        /// Constants that were templated in original tree.
201        /// </summary>
202        private Set<int> _templated;
203
204        /// <summary>
205        /// New tree requires more general template.
206        /// </summary>
207        private bool _tooSpecific;
208        
209        /// <summary>
210        /// New tree is sufficiently similar to the old one.
211        /// </summary>
212        private bool _compatible;
213
214        /// <summary>
215        /// Constants that require parameterisation and their position 
216        /// The numbering is assumed as in traversal by ExpressionVisitor.
217        /// </summary>
218        private List<KeyValuePair<ConstantExpression, int>> _replacementList;
219
220        private TreeComparer(Set<int> templated) {
221            _templated = templated;
222        }
223
224        internal static TreeCompareResult CompareTrees(
225                Expression left, 
226                Expression right, 
227                Set<int> templated,
228                out List<KeyValuePair<ConstantExpression, int>> ReplacementList){
229
230
231            TreeComparer comparer = new TreeComparer(templated);
232            comparer.Compare(left, right);
233
234            if (!comparer._compatible) {
235                ReplacementList = null;
236                return TreeCompareResult.Incompatible;
237            }
238
239            ReplacementList = comparer._replacementList;
240            if (comparer._tooSpecific) {
241                return TreeCompareResult.TooSpecific;
242            } else {
243                return TreeCompareResult.Compatible;
244            }
245        }
246
247        /// <summary>
248        /// Compares two trees.
249        /// If trees differ only in constants, produces list of constants that should be parameterised.
250        /// Also verifies if existing template is sufficient and could be reused.
251        /// </summary>
252        private void Compare(Expression left, Expression right) {
253            FlatTreeWalker walkLeft = new FlatTreeWalker();
254            FlatTreeWalker walkRight = new FlatTreeWalker();
255            walkLeft.Visit(left);
256            walkRight.Visit(right);
257
258            // false untill proven compatible.
259            _compatible = false;
260
261            // check the length first to see if the trees are obviously different            
262            if (walkLeft.Expressions.Count != walkRight.Expressions.Count) {
263                return;
264            }
265
266            _varInfo = new VariableInfo();
267            _curConstNum = -1;
268            _replacementList = new List<KeyValuePair<ConstantExpression, int>>();           
269
270            // then see if they differ by just constants which we could replace
271            for (int i = 0; i < walkLeft.Expressions.Count; i++) {
272                Expression currentLeft = walkLeft.Expressions[i], currentRight = walkRight.Expressions[i];
273
274                if (currentLeft.NodeType != currentRight.NodeType) {
275                    // different node types, they can't possibly be equal
276                    return;
277                } else if (currentLeft.Type != currentRight.Type) {
278                    // they can't possibly be a match
279                    return;
280                }
281
282                if (!CompareTwoNodes(currentLeft, currentRight)) {
283                    return;
284                }
285            }
286
287            _compatible = true;
288            return;
289        }
290
291        private bool IsTemplatedConstant(int constantNum) {
292            return _templated != null && _templated.Contains(constantNum);
293        }
294
295        private void AddToReplacementList(ConstantExpression ce) {
296            _replacementList.Add(new KeyValuePair<ConstantExpression, int>(ce, _curConstNum));
297        }
298
299        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
300        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
301        private bool CompareTwoNodes(Expression currentLeft, Expression currentRight) {
302            switch (currentLeft.NodeType) {
303                case ExpressionType.Dynamic:
304                    var dynLeft = (DynamicExpression)currentLeft;
305                    var dynRight = (DynamicExpression)currentRight;
306
307                    if (!dynRight.Binder.Equals(dynLeft.Binder)) {
308                        return false;
309                    }
310                    break;
311                case ExpressionType.Constant:
312                    _curConstNum++;
313
314                    // check constant value                        
315                    ConstantExpression ceLeft = (ConstantExpression)currentLeft;
316                    ConstantExpression ceRight = (ConstantExpression)currentRight;
317
318                    object leftValue = ceLeft.Value;
319                    object rightValue = ceRight.Value;
320
321                    // See if they're both sites
322                    CallSite leftSite = leftValue as CallSite;
323                    CallSite rightSite = rightValue as CallSite;
324                    if (leftSite != null) {
325                        if (rightSite == null) {
326                            return false;
327                        }
328
329                        if (!leftSite.Binder.Equals(rightSite.Binder)) {
330                            return false;
331                        }
332
333                        return true;
334                    } else if (rightSite != null) {
335                        return false;
336                    }
337
338                    if (IsTemplatedConstant(_curConstNum)) {
339                        // always add already templated values
340                        AddToReplacementList(ceLeft);
341                    } else {
342                        // different constants should become parameters in the template.
343                        if (leftValue == null) {
344                            if (rightValue != null) {
345                                //new templated const
346                                _tooSpecific = true;
347                                AddToReplacementList(ceLeft);
348                            }
349                        } else {
350                            if (!leftValue.Equals(rightValue)){
351                                //new templated const
352                                _tooSpecific = true;
353                                AddToReplacementList(ceLeft);
354                            }
355                        }
356                    }
357
358                    break;
359                case ExpressionType.Equal:
360                case ExpressionType.NotEqual:
361                    if (!CompareEquality((BinaryExpression)currentLeft, (BinaryExpression)currentRight)) {
362                        return false;
363                    }
364                    break;
365                case ExpressionType.Add:
366                case ExpressionType.And:
367                case ExpressionType.AndAlso:
368                case ExpressionType.ArrayIndex:
369                case ExpressionType.Divide:
370                case ExpressionType.ExclusiveOr:
371                case ExpressionType.GreaterThan:
372                case ExpressionType.GreaterThanOrEqual:
373                case ExpressionType.LeftShift:
374                case ExpressionType.LessThan:
375                case ExpressionType.LessThanOrEqual:
376                case ExpressionType.Modulo:
377                case ExpressionType.Multiply:
378                case ExpressionType.Or:
379                case ExpressionType.OrElse:
380                case ExpressionType.RightShift:
381                case ExpressionType.Subtract:
382                case ExpressionType.AddAssign:
383                case ExpressionType.SubtractAssign:
384                case ExpressionType.MultiplyAssign:
385                case ExpressionType.AddAssignChecked:
386                case ExpressionType.SubtractAssignChecked:
387                case ExpressionType.MultiplyAssignChecked:
388                case ExpressionType.DivideAssign:
389                case ExpressionType.ModuloAssign:
390                case ExpressionType.PowerAssign:
391                case ExpressionType.AndAssign:
392                case ExpressionType.OrAssign:
393                case ExpressionType.RightShiftAssign:
394                case ExpressionType.LeftShiftAssign:
395                case ExpressionType.ExclusiveOrAssign:
396                    if (!Compare((BinaryExpression)currentLeft, (BinaryExpression)currentRight)) {
397                        return false;
398                    }
399                    break;
400                case ExpressionType.Call:
401                    if (!Compare((MethodCallExpression)currentLeft, (MethodCallExpression)currentRight)) {
402                        return false;
403                    }
404                    break;
405                case ExpressionType.New:
406                    // chcek ConstructorInfo and BindingInfo
407                    if (!Compare((NewExpression)currentLeft, (NewExpression)currentRight)) {
408                        return false;
409                    }
410                    break;
411                case ExpressionType.TypeIs:
412                case ExpressionType.TypeEqual:
413                    // check type
414                    if (!Compare((TypeBinaryExpression)currentLeft, (TypeBinaryExpression)currentRight)) {
415                        return false;
416                    }
417                    break;
418                case ExpressionType.Block:
419                    // compare factory method
420                    if (!Compare(_varInfo, (BlockExpression)currentLeft, (BlockExpression)currentRight)) {
421                        return false;
422                    }
423                    break;
424                case ExpressionType.MemberAccess:
425                    // compare member
426                    if (!Compare((MemberExpression)currentLeft, (MemberExpression)currentRight)) {
427                        return false;
428                    }
429                    break;
430                case ExpressionType.Try:
431                    // compare catch finally blocks and their handler types
432                    if (!Compare(_varInfo, (TryExpression)currentLeft, (TryExpression)currentRight)) {
433                        return false;
434                    }
435                    break;
436                case ExpressionType.Parameter:
437                    if (!Compare(_varInfo, (ParameterExpression)currentLeft, (ParameterExpression)currentRight)) {
438                        return false;
439                    }
440                    break;
441                case ExpressionType.Lambda:
442                case ExpressionType.Assign:
443                case ExpressionType.Goto:
444                case ExpressionType.Throw:
445                case ExpressionType.Loop:
446                case ExpressionType.Default:
447                case ExpressionType.Convert:
448                case ExpressionType.TypeAs:
449                case ExpressionType.Unbox:
450                case ExpressionType.Negate:
451                case ExpressionType.Not:
452                case ExpressionType.IsFalse:
453                case ExpressionType.IsTrue:
454                case ExpressionType.OnesComplement:
455                case ExpressionType.Conditional:
456                case ExpressionType.NewArrayInit:
457                case ExpressionType.NewArrayBounds:
458                case ExpressionType.Invoke:
459                    // these nodes children and types completely
460                    // define the node
461                    break;
462                case ExpressionType.Label:
463                case ExpressionType.Switch:
464                    // we could improve the compare to compare labels & switch,
465                    // but these are rarely used in rules.
466                    return false;
467                case ExpressionType.Extension:
468                    // we should have been reduced, but error on the side of being different.
469                    return false;
470                default:
471                    throw ContractUtils.Unreachable;
472            }
473            return true;
474        }
475
476        private static bool CompareEquality(BinaryExpression left, BinaryExpression right) {
477            if (left.Left.Type == typeof(object) && left.Right.Type == typeof(object)) {
478                // could be comparing object to runtime constant w/ identity semantics.
479                return CompareBinaryForEquality(GetConstantExpression(left.Left), GetConstantExpression(right.Left)) &&
480                       CompareBinaryForEquality(GetConstantExpression(left.Right), GetConstantExpression(right.Right));
481            }
482
483            return true;
484        }
485
486        private static ConstantExpression GetConstantExpression(Expression expression) {
487            if (expression.NodeType == ExpressionType.Convert) {
488                return GetConstantExpression(((UnaryExpression)expression).Operand);
489            }
490
491            return expression as ConstantExpression;
492        }
493
494        private static bool CompareBinaryForEquality(ConstantExpression left, ConstantExpression right) {
495            if (left == null || right == null) {
496                return true;
497            }
498
499            return left.Value == right.Value;
500        }
501
502        private static bool Compare(BinaryExpression left, BinaryExpression right) {
503            if (left.Method != right.Method) {
504                return false;
505            }
506
507            return true;
508        }
509
510        private static bool Compare(MethodCallExpression left, MethodCallExpression right) {
511            if (left.Method != right.Method) {
512                return false;
513            }
514
515            return true;
516        }
517
518        private static bool Compare(NewExpression left, NewExpression right) {
519            if (left.Constructor != right.Constructor) {
520                return false;
521            }
522
523            return true;
524        }
525
526
527        private static bool Compare(TypeBinaryExpression left, TypeBinaryExpression right) {
528            if (left.TypeOperand != right.TypeOperand) {
529                return false;
530            }
531
532            return true;
533        }
534
535        private static bool Compare(VariableInfo varInfo, BlockExpression left, BlockExpression right) {
536            if (left.Variables.Count != right.Variables.Count) {
537                return false;
538            }
539
540            for (int i = 0; i < left.Variables.Count; i++) {
541                Compare(varInfo, left.Variables[i], right.Variables[i]);
542            }
543            return true;
544        }
545
546        private static bool Compare(MemberExpression left, MemberExpression right) {
547            if (left.Member != right.Member) {
548                return false;
549            }
550
551            return true;
552        }
553
554        private static bool Compare(VariableInfo varInfo, TryExpression left, TryExpression right) {
555            if ((left.Finally == null && right.Finally != null) ||
556                (left.Finally != null && right.Finally == null)) {
557                return false;
558            }
559
560            if (left.Handlers.Count != right.Handlers.Count) {
561                return false;
562            }
563
564            for (int i = 0; i < left.Handlers.Count; i++) {
565                if (left.Handlers[i].Test != right.Handlers[i].Test) {
566                    return false;
567                }
568
569                if (varInfo.GetLeftVariable(left.Handlers[i].Variable) != varInfo.GetRightVariable(right.Handlers[i].Variable)) {
570                    return false;
571                }
572            }
573
574            return true;
575        }
576
577        private static bool Compare(VariableInfo varInfo, ParameterExpression left, ParameterExpression right) {
578            if (varInfo.GetLeftVariable(left) != varInfo.GetRightVariable(right)) {
579                return false;
580            }
581
582            return true;
583        }
584    }
585}