PageRenderTime 43ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

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