ExpressionVisitor.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Collections.ObjectModel;
  18. using System.Linq.Expressions;
  19. namespace MongoDB.Bson.Serialization
  20. {
  21. // TODO: this class duplicates a similar class in MongoDB.Driver.dll
  22. // when we move to .NET Framework 4 we can use .NET's version of ExpressionVisitor and eliminate the duplication
  23. /// <summary>
  24. /// An abstract base class for an Expression visitor.
  25. /// </summary>
  26. public abstract class ExpressionVisitor
  27. {
  28. // constructors
  29. /// <summary>
  30. /// Initializes a new instance of the ExpressionVisitor class.
  31. /// </summary>
  32. protected ExpressionVisitor()
  33. {
  34. }
  35. // protected methods
  36. /// <summary>
  37. /// Visits an Expression.
  38. /// </summary>
  39. /// <param name="node">The Expression.</param>
  40. /// <returns>The Expression (posibly modified).</returns>
  41. protected virtual Expression Visit(Expression node)
  42. {
  43. if (node == null)
  44. {
  45. return node;
  46. }
  47. switch (node.NodeType)
  48. {
  49. case ExpressionType.Negate:
  50. case ExpressionType.NegateChecked:
  51. case ExpressionType.Not:
  52. case ExpressionType.Convert:
  53. case ExpressionType.ConvertChecked:
  54. case ExpressionType.ArrayLength:
  55. case ExpressionType.Quote:
  56. case ExpressionType.TypeAs:
  57. return this.VisitUnary((UnaryExpression)node);
  58. case ExpressionType.Add:
  59. case ExpressionType.AddChecked:
  60. case ExpressionType.Subtract:
  61. case ExpressionType.SubtractChecked:
  62. case ExpressionType.Multiply:
  63. case ExpressionType.MultiplyChecked:
  64. case ExpressionType.Divide:
  65. case ExpressionType.Modulo:
  66. case ExpressionType.And:
  67. case ExpressionType.AndAlso:
  68. case ExpressionType.Or:
  69. case ExpressionType.OrElse:
  70. case ExpressionType.LessThan:
  71. case ExpressionType.LessThanOrEqual:
  72. case ExpressionType.GreaterThan:
  73. case ExpressionType.GreaterThanOrEqual:
  74. case ExpressionType.Equal:
  75. case ExpressionType.NotEqual:
  76. case ExpressionType.Coalesce:
  77. case ExpressionType.ArrayIndex:
  78. case ExpressionType.RightShift:
  79. case ExpressionType.LeftShift:
  80. case ExpressionType.ExclusiveOr:
  81. return this.VisitBinary((BinaryExpression)node);
  82. case ExpressionType.TypeIs:
  83. return this.VisitTypeBinary((TypeBinaryExpression)node);
  84. case ExpressionType.Conditional:
  85. return this.VisitConditional((ConditionalExpression)node);
  86. case ExpressionType.Constant:
  87. return this.VisitConstant((ConstantExpression)node);
  88. case ExpressionType.Parameter:
  89. return this.VisitParameter((ParameterExpression)node);
  90. case ExpressionType.MemberAccess:
  91. return this.VisitMember((MemberExpression)node);
  92. case ExpressionType.Call:
  93. return this.VisitMethodCall((MethodCallExpression)node);
  94. case ExpressionType.Lambda:
  95. return this.VisitLambda((LambdaExpression)node);
  96. case ExpressionType.New:
  97. return this.VisitNew((NewExpression)node);
  98. case ExpressionType.NewArrayInit:
  99. case ExpressionType.NewArrayBounds:
  100. return this.VisitNewArray((NewArrayExpression)node);
  101. case ExpressionType.Invoke:
  102. return this.VisitInvocation((InvocationExpression)node);
  103. case ExpressionType.MemberInit:
  104. return this.VisitMemberInit((MemberInitExpression)node);
  105. case ExpressionType.ListInit:
  106. return this.VisitListInit((ListInitExpression)node);
  107. default:
  108. throw new Exception(string.Format("Unhandled expression type: '{0}'", node.NodeType));
  109. }
  110. }
  111. /// <summary>
  112. /// Visits an Expression list.
  113. /// </summary>
  114. /// <param name="nodes">The Expression list.</param>
  115. /// <returns>The Expression list (possibly modified).</returns>
  116. protected ReadOnlyCollection<Expression> Visit(ReadOnlyCollection<Expression> nodes)
  117. {
  118. List<Expression> list = null;
  119. for (int i = 0, n = nodes.Count; i < n; i++)
  120. {
  121. Expression node = this.Visit(nodes[i]);
  122. if (list != null)
  123. {
  124. list.Add(node);
  125. }
  126. else if (node != nodes[i])
  127. {
  128. list = new List<Expression>(n);
  129. for (int j = 0; j < i; j++)
  130. {
  131. list.Add(nodes[j]);
  132. }
  133. list.Add(node);
  134. }
  135. }
  136. if (list != null)
  137. {
  138. return list.AsReadOnly();
  139. }
  140. return nodes;
  141. }
  142. /// <summary>
  143. /// Visits a BinaryExpression.
  144. /// </summary>
  145. /// <param name="node">The BinaryExpression.</param>
  146. /// <returns>The BinaryExpression (possibly modified).</returns>
  147. protected virtual Expression VisitBinary(BinaryExpression node)
  148. {
  149. Expression left = this.Visit(node.Left);
  150. Expression right = this.Visit(node.Right);
  151. Expression conversion = this.Visit(node.Conversion);
  152. if (left != node.Left || right != node.Right || conversion != node.Conversion)
  153. {
  154. if (node.NodeType == ExpressionType.Coalesce && node.Conversion != null)
  155. {
  156. return Expression.Coalesce(left, right, conversion as LambdaExpression);
  157. }
  158. else
  159. {
  160. return Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method);
  161. }
  162. }
  163. return node;
  164. }
  165. /// <summary>
  166. /// Visits a ConditionalExpression.
  167. /// </summary>
  168. /// <param name="node">The ConditionalExpression.</param>
  169. /// <returns>The ConditionalExpression (possibly modified).</returns>
  170. protected virtual Expression VisitConditional(ConditionalExpression node)
  171. {
  172. Expression test = this.Visit(node.Test);
  173. Expression ifTrue = this.Visit(node.IfTrue);
  174. Expression ifFalse = this.Visit(node.IfFalse);
  175. if (test != node.Test || ifTrue != node.IfTrue || ifFalse != node.IfFalse)
  176. {
  177. return Expression.Condition(test, ifTrue, ifFalse);
  178. }
  179. return node;
  180. }
  181. /// <summary>
  182. /// Visits a ConstantExpression.
  183. /// </summary>
  184. /// <param name="node">The ConstantExpression.</param>
  185. /// <returns>The ConstantExpression (possibly modified).</returns>
  186. protected virtual Expression VisitConstant(ConstantExpression node)
  187. {
  188. return node;
  189. }
  190. /// <summary>
  191. /// Visits an ElementInit.
  192. /// </summary>
  193. /// <param name="node">The ElementInit.</param>
  194. /// <returns>The ElementInit (possibly modified).</returns>
  195. protected virtual ElementInit VisitElementInit(ElementInit node)
  196. {
  197. ReadOnlyCollection<Expression> arguments = this.Visit(node.Arguments);
  198. if (arguments != node.Arguments)
  199. {
  200. return Expression.ElementInit(node.AddMethod, arguments);
  201. }
  202. return node;
  203. }
  204. // TODO: the .NET Framework 4 version of ExpressionVisitor does not have a method called VisitElementInitializerList
  205. // leaving this method for now, though perhaps it could be replaced with Visit(ReadOnlyCollection<Expression>)?
  206. /// <summary>
  207. /// Visits an ElementInit list.
  208. /// </summary>
  209. /// <param name="nodes">The ElementInit list.</param>
  210. /// <returns>The ElementInit list (possibly modified).</returns>
  211. protected virtual IEnumerable<ElementInit> VisitElementInitList(
  212. ReadOnlyCollection<ElementInit> nodes)
  213. {
  214. List<ElementInit> list = null;
  215. for (int i = 0, n = nodes.Count; i < n; i++)
  216. {
  217. ElementInit node = this.VisitElementInit(nodes[i]);
  218. if (list != null)
  219. {
  220. list.Add(node);
  221. }
  222. else if (node != nodes[i])
  223. {
  224. list = new List<ElementInit>(n);
  225. for (int j = 0; j < i; j++)
  226. {
  227. list.Add(nodes[j]);
  228. }
  229. list.Add(node);
  230. }
  231. }
  232. if (list != null)
  233. {
  234. return list;
  235. }
  236. return nodes;
  237. }
  238. /// <summary>
  239. /// Visits an InvocationExpression.
  240. /// </summary>
  241. /// <param name="node">The InvocationExpression.</param>
  242. /// <returns>The InvocationExpression (possibly modified).</returns>
  243. protected virtual Expression VisitInvocation(InvocationExpression node)
  244. {
  245. IEnumerable<Expression> args = this.Visit(node.Arguments);
  246. Expression expr = this.Visit(node.Expression);
  247. if (args != node.Arguments || expr != node.Expression)
  248. {
  249. return Expression.Invoke(expr, args);
  250. }
  251. return node;
  252. }
  253. // TODO: in .NET Framework 4 VisitLambda takes an Expression<T> instead of Lambda
  254. // probably not worthing changing in our version of ExpressionVisitor
  255. /// <summary>
  256. /// Visits a LambdaExpression.
  257. /// </summary>
  258. /// <param name="node">The LambdaExpression.</param>
  259. /// <returns>The LambdaExpression (possibly modified).</returns>
  260. protected virtual Expression VisitLambda(LambdaExpression node)
  261. {
  262. Expression body = this.Visit(node.Body);
  263. if (body != node.Body)
  264. {
  265. return Expression.Lambda(node.Type, body, node.Parameters);
  266. }
  267. return node;
  268. }
  269. /// <summary>
  270. /// Visits a ListInitExpression.
  271. /// </summary>
  272. /// <param name="node">The ListInitExpression.</param>
  273. /// <returns>The ListInitExpression (possibly modified).</returns>
  274. protected virtual Expression VisitListInit(ListInitExpression node)
  275. {
  276. NewExpression n = this.VisitNew(node.NewExpression);
  277. IEnumerable<ElementInit> initializers = this.VisitElementInitList(node.Initializers);
  278. if (n != node.NewExpression || initializers != node.Initializers)
  279. {
  280. return Expression.ListInit(n, initializers);
  281. }
  282. return node;
  283. }
  284. /// <summary>
  285. /// Visits a MemberExpression.
  286. /// </summary>
  287. /// <param name="node">The MemberExpression.</param>
  288. /// <returns>The MemberExpression (possibly modified).</returns>
  289. protected virtual Expression VisitMember(MemberExpression node)
  290. {
  291. Expression exp = this.Visit(node.Expression);
  292. if (exp != node.Expression)
  293. {
  294. return Expression.MakeMemberAccess(exp, node.Member);
  295. }
  296. return node;
  297. }
  298. /// <summary>
  299. /// Visits a MemberAssignment.
  300. /// </summary>
  301. /// <param name="node">The MemberAssignment.</param>
  302. /// <returns>The MemberAssignment (possibly modified).</returns>
  303. protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment node)
  304. {
  305. Expression e = this.Visit(node.Expression);
  306. if (e != node.Expression)
  307. {
  308. return Expression.Bind(node.Member, e);
  309. }
  310. return node;
  311. }
  312. /// <summary>
  313. /// Visits a MemberBinding.
  314. /// </summary>
  315. /// <param name="node">The MemberBinding.</param>
  316. /// <returns>The MemberBinding (possibly modified).</returns>
  317. protected virtual MemberBinding VisitMemberBinding(MemberBinding node)
  318. {
  319. switch (node.BindingType)
  320. {
  321. case MemberBindingType.Assignment:
  322. return this.VisitMemberAssignment((MemberAssignment)node);
  323. case MemberBindingType.MemberBinding:
  324. return this.VisitMemberMemberBinding((MemberMemberBinding)node);
  325. case MemberBindingType.ListBinding:
  326. return this.VisitMemberListBinding((MemberListBinding)node);
  327. default:
  328. throw new Exception(string.Format("Unhandled binding type '{0}'", node.BindingType));
  329. }
  330. }
  331. // TODO: the .NET Framework 4 version of ExpressionVisitor does not have a method called VisitMemberBindingList
  332. // leaving this method for now, though perhaps it could be replaced with Visit(ReadOnlyCollection<Expression>)?
  333. /// <summary>
  334. /// Visits a MemberBinding list.
  335. /// </summary>
  336. /// <param name="nodes">The MemberBinding list.</param>
  337. /// <returns>The MemberBinding list (possibly modified).</returns>
  338. protected virtual IEnumerable<MemberBinding> VisitMemberBindingList(ReadOnlyCollection<MemberBinding> nodes)
  339. {
  340. List<MemberBinding> list = null;
  341. for (int i = 0, n = nodes.Count; i < n; i++)
  342. {
  343. MemberBinding node = this.VisitMemberBinding(nodes[i]);
  344. if (list != null)
  345. {
  346. list.Add(node);
  347. }
  348. else if (node != nodes[i])
  349. {
  350. list = new List<MemberBinding>(n);
  351. for (int j = 0; j < i; j++)
  352. {
  353. list.Add(nodes[j]);
  354. }
  355. list.Add(node);
  356. }
  357. }
  358. if (list != null)
  359. {
  360. return list;
  361. }
  362. return nodes;
  363. }
  364. /// <summary>
  365. /// Visits a MemberInitExpression.
  366. /// </summary>
  367. /// <param name="node">The MemberInitExpression.</param>
  368. /// <returns>The MemberInitExpression (possibly modified).</returns>
  369. protected virtual Expression VisitMemberInit(MemberInitExpression node)
  370. {
  371. NewExpression n = this.VisitNew(node.NewExpression);
  372. IEnumerable<MemberBinding> bindings = this.VisitMemberBindingList(node.Bindings);
  373. if (n != node.NewExpression || bindings != node.Bindings)
  374. {
  375. return Expression.MemberInit(n, bindings);
  376. }
  377. return node;
  378. }
  379. /// <summary>
  380. /// Visits a MemberListBinding.
  381. /// </summary>
  382. /// <param name="node">The MemberListBinding.</param>
  383. /// <returns>The MemberListBinding (possibly modified).</returns>
  384. protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding node)
  385. {
  386. IEnumerable<ElementInit> initializers = this.VisitElementInitList(node.Initializers);
  387. if (initializers != node.Initializers)
  388. {
  389. return Expression.ListBind(node.Member, initializers);
  390. }
  391. return node;
  392. }
  393. /// <summary>
  394. /// Visits a MemberMemberBinding.
  395. /// </summary>
  396. /// <param name="node">The MemberMemberBinding.</param>
  397. /// <returns>The MemberMemberBinding (possibly modified).</returns>
  398. protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node)
  399. {
  400. IEnumerable<MemberBinding> bindings = this.VisitMemberBindingList(node.Bindings);
  401. if (bindings != node.Bindings)
  402. {
  403. return Expression.MemberBind(node.Member, bindings);
  404. }
  405. return node;
  406. }
  407. /// <summary>
  408. /// Visits a MethodCallExpression.
  409. /// </summary>
  410. /// <param name="node">The MethodCallExpression.</param>
  411. /// <returns>The MethodCallExpression (possibly modified).</returns>
  412. protected virtual Expression VisitMethodCall(MethodCallExpression node)
  413. {
  414. Expression obj = this.Visit(node.Object);
  415. IEnumerable<Expression> args = this.Visit(node.Arguments);
  416. if (obj != node.Object || args != node.Arguments)
  417. {
  418. return Expression.Call(obj, node.Method, args);
  419. }
  420. return node;
  421. }
  422. /// <summary>
  423. /// Visits a NewExpression.
  424. /// </summary>
  425. /// <param name="node">The NewExpression.</param>
  426. /// <returns>The NewExpression (possibly modified).</returns>
  427. protected virtual NewExpression VisitNew(NewExpression node)
  428. {
  429. IEnumerable<Expression> args = this.Visit(node.Arguments);
  430. if (args != node.Arguments)
  431. {
  432. if (node.Members != null)
  433. {
  434. return Expression.New(node.Constructor, args, node.Members);
  435. }
  436. else
  437. {
  438. return Expression.New(node.Constructor, args);
  439. }
  440. }
  441. return node;
  442. }
  443. /// <summary>
  444. /// Visits a NewArrayExpression.
  445. /// </summary>
  446. /// <param name="node">The NewArrayExpression.</param>
  447. /// <returns>The NewArrayExpression (possibly modified).</returns>
  448. protected virtual Expression VisitNewArray(NewArrayExpression node)
  449. {
  450. IEnumerable<Expression> exprs = this.Visit(node.Expressions);
  451. if (exprs != node.Expressions)
  452. {
  453. if (node.NodeType == ExpressionType.NewArrayInit)
  454. {
  455. return Expression.NewArrayInit(node.Type.GetElementType(), exprs);
  456. }
  457. else
  458. {
  459. return Expression.NewArrayBounds(node.Type.GetElementType(), exprs);
  460. }
  461. }
  462. return node;
  463. }
  464. /// <summary>
  465. /// Visits a ParameterExpression.
  466. /// </summary>
  467. /// <param name="node">The ParameterExpression.</param>
  468. /// <returns>The ParameterExpression (possibly modified).</returns>
  469. protected virtual Expression VisitParameter(ParameterExpression node)
  470. {
  471. return node;
  472. }
  473. /// <summary>
  474. /// Visits a TypeBinaryExpression.
  475. /// </summary>
  476. /// <param name="node">The TypeBinaryExpression.</param>
  477. /// <returns>The TypeBinaryExpression (possibly modified).</returns>
  478. protected virtual Expression VisitTypeBinary(TypeBinaryExpression node)
  479. {
  480. Expression expr = this.Visit(node.Expression);
  481. if (expr != node.Expression)
  482. {
  483. return Expression.TypeIs(expr, node.TypeOperand);
  484. }
  485. return node;
  486. }
  487. /// <summary>
  488. /// Visits a UnaryExpression.
  489. /// </summary>
  490. /// <param name="node">The UnaryExpression.</param>
  491. /// <returns>The UnaryExpression (possibly modified).</returns>
  492. protected virtual Expression VisitUnary(UnaryExpression node)
  493. {
  494. Expression operand = this.Visit(node.Operand);
  495. if (operand != node.Operand)
  496. {
  497. return Expression.MakeUnary(node.NodeType, operand, node.Type, node.Method);
  498. }
  499. return node;
  500. }
  501. }
  502. }