1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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 Apache License, Version 2.0, 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 Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.ObjectModel;
18 using System.Diagnostics;
19 using System.Dynamic.Utils;
20 using System.Runtime.CompilerServices;
27 namespace Microsoft.Scripting.Ast {
28 using Microsoft.Scripting.Utils;
30 namespace System.Linq.Expressions {
34 /// Represents a visitor or rewriter for expression trees.
37 /// This class is designed to be inherited to create more specialized
38 /// classes whose functionality requires traversing, examining or copying
39 /// an expression tree.
41 public abstract class ExpressionVisitor {
44 /// Initializes a new instance of <see cref="ExpressionVisitor"/>.
46 protected ExpressionVisitor() {
50 /// Dispatches the expression to one of the more specialized visit methods in this class.
52 /// <param name="node">The expression to visit.</param>
53 /// <returns>The modified expression, if it or any subexpression was modified;
54 /// otherwise, returns the original expression.</returns>
55 public virtual Expression Visit(Expression node) {
57 return node.Accept(this);
63 /// Dispatches the list of expressions to one of the more specialized visit methods in this class.
65 /// <param name="nodes">The expressions to visit.</param>
66 /// <returns>The modified expression list, if any of the elements were modified;
67 /// otherwise, returns the original expression list.</returns>
68 public ReadOnlyCollection<Expression> Visit(ReadOnlyCollection<Expression> nodes) {
69 Expression[] newNodes = null;
70 for (int i = 0, n = nodes.Count; i < n; i++) {
71 Expression node = Visit(nodes[i]);
73 if (newNodes != null) {
75 } else if (!object.ReferenceEquals(node, nodes[i])) {
76 newNodes = new Expression[n];
77 for (int j = 0; j < i; j++) {
78 newNodes[j] = nodes[j];
83 if (newNodes == null) {
86 return new TrueReadOnlyCollection<Expression>(newNodes);
89 internal Expression[] VisitArguments(IArgumentProvider nodes) {
90 Expression[] newNodes = null;
91 for (int i = 0, n = nodes.ArgumentCount; i < n; i++) {
92 Expression curNode = nodes.GetArgument(i);
93 Expression node = Visit(curNode);
95 if (newNodes != null) {
97 } else if (!object.ReferenceEquals(node, curNode)) {
98 newNodes = new Expression[n];
99 for (int j = 0; j < i; j++) {
100 newNodes[j] = nodes.GetArgument(j);
109 /// Visits all nodes in the collection using a specified element visitor.
111 /// <typeparam name="T">The type of the nodes.</typeparam>
112 /// <param name="nodes">The nodes to visit.</param>
113 /// <param name="elementVisitor">A delegate that visits a single element,
114 /// optionally replacing it with a new element.</param>
115 /// <returns>The modified node list, if any of the elements were modified;
116 /// otherwise, returns the original node list.</returns>
117 public static ReadOnlyCollection<T> Visit<T>(ReadOnlyCollection<T> nodes, Func<T, T> elementVisitor) {
119 for (int i = 0, n = nodes.Count; i < n; i++) {
120 T node = elementVisitor(nodes[i]);
121 if (newNodes != null) {
123 } else if (!object.ReferenceEquals(node, nodes[i])) {
125 for (int j = 0; j < i; j++) {
126 newNodes[j] = nodes[j];
131 if (newNodes == null) {
134 return new TrueReadOnlyCollection<T>(newNodes);
138 /// Visits an expression, casting the result back to the original expression type.
140 /// <typeparam name="T">The type of the expression.</typeparam>
141 /// <param name="node">The expression to visit.</param>
142 /// <param name="callerName">The name of the calling method; used to report to report a better error message.</param>
143 /// <returns>The modified expression, if it or any subexpression was modified;
144 /// otherwise, returns the original expression.</returns>
145 /// <exception cref="InvalidOperationException">The visit method for this node returned a different type.</exception>
146 public T VisitAndConvert<T>(T node, string callerName) where T : Expression {
150 node = Visit(node) as T;
152 throw Error.MustRewriteToSameNode(callerName, typeof(T), callerName);
158 /// Visits an expression, casting the result back to the original expression type.
160 /// <typeparam name="T">The type of the expression.</typeparam>
161 /// <param name="nodes">The expression to visit.</param>
162 /// <param name="callerName">The name of the calling method; used to report to report a better error message.</param>
163 /// <returns>The modified expression, if it or any subexpression was modified;
164 /// otherwise, returns the original expression.</returns>
165 /// <exception cref="InvalidOperationException">The visit method for this node returned a different type.</exception>
166 public ReadOnlyCollection<T> VisitAndConvert<T>(ReadOnlyCollection<T> nodes, string callerName) where T : Expression {
168 for (int i = 0, n = nodes.Count; i < n; i++) {
169 T node = Visit(nodes[i]) as T;
171 throw Error.MustRewriteToSameNode(callerName, typeof(T), callerName);
174 if (newNodes != null) {
176 } else if (!object.ReferenceEquals(node, nodes[i])) {
178 for (int j = 0; j < i; j++) {
179 newNodes[j] = nodes[j];
184 if (newNodes == null) {
187 return new TrueReadOnlyCollection<T>(newNodes);
191 /// Visits the children of the <see cref="BinaryExpression" />.
193 /// <param name="node">The expression to visit.</param>
194 /// <returns>The modified expression, if it or any subexpression was modified;
195 /// otherwise, returns the original expression.</returns>
196 protected internal virtual Expression VisitBinary(BinaryExpression node) {
197 // Walk children in evaluation order: left, conversion, right
198 return ValidateBinary(
202 VisitAndConvert(node.Conversion, "VisitBinary"),
209 /// Visits the children of the <see cref="BlockExpression" />.
211 /// <param name="node">The expression to visit.</param>
212 /// <returns>The modified expression, if it or any subexpression was modified;
213 /// otherwise, returns the original expression.</returns>
214 protected internal virtual Expression VisitBlock(BlockExpression node) {
215 int count = node.ExpressionCount;
216 Expression[] nodes = null;
217 for (int i = 0; i < count; i++) {
218 Expression oldNode = node.GetExpression(i);
219 Expression newNode = Visit(oldNode);
221 if (oldNode != newNode) {
223 nodes = new Expression[count];
228 var v = VisitAndConvert(node.Variables, "VisitBlock");
230 if (v == node.Variables && nodes == null) {
233 for (int i = 0; i < count; i++) {
234 if (nodes[i] == null) {
235 nodes[i] = node.GetExpression(i);
240 return node.Rewrite(v, nodes);
244 /// Visits the children of the <see cref="ConditionalExpression" />.
246 /// <param name="node">The expression to visit.</param>
247 /// <returns>The modified expression, if it or any subexpression was modified;
248 /// otherwise, returns the original expression.</returns>
249 protected internal virtual Expression VisitConditional(ConditionalExpression node) {
250 return node.Update(Visit(node.Test), Visit(node.IfTrue), Visit(node.IfFalse));
254 /// Visits the <see cref="ConstantExpression" />.
256 /// <param name="node">The expression to visit.</param>
257 /// <returns>The modified expression, if it or any subexpression was modified;
258 /// otherwise, returns the original expression.</returns>
259 protected internal virtual Expression VisitConstant(ConstantExpression node) {
264 /// Visits the <see cref="DebugInfoExpression" />.
266 /// <param name="node">The expression to visit.</param>
267 /// <returns>The modified expression, if it or any subexpression was modified;
268 /// otherwise, returns the original expression.</returns>
269 protected internal virtual Expression VisitDebugInfo(DebugInfoExpression node) {
274 /// Visits the children of the <see cref="DynamicExpression" />.
276 /// <param name="node">The expression to visit.</param>
277 /// <returns>The modified expression, if it or any subexpression was modified;
278 /// otherwise, returns the original expression.</returns>
279 protected internal virtual Expression VisitDynamic(DynamicExpression node) {
280 Expression[] a = VisitArguments((IArgumentProvider)node);
285 return node.Rewrite(a);
289 /// Visits the <see cref="DefaultExpression" />.
291 /// <param name="node">The expression to visit.</param>
292 /// <returns>The modified expression, if it or any subexpression was modified;
293 /// otherwise, returns the original expression.</returns>
294 protected internal virtual Expression VisitDefault(DefaultExpression node) {
299 /// Visits the children of the extension expression.
301 /// <param name="node">The expression to visit.</param>
302 /// <returns>The modified expression, if it or any subexpression was modified;
303 /// otherwise, returns the original expression.</returns>
305 /// This can be overridden to visit or rewrite specific extension nodes.
306 /// If it is not overridden, this method will call <see cref="Expression.VisitChildren" />,
307 /// which gives the node a chance to walk its children. By default,
308 /// <see cref="Expression.VisitChildren" /> will try to reduce the node.
310 protected internal virtual Expression VisitExtension(Expression node) {
311 return node.VisitChildren(this);
315 /// Visits the children of the <see cref="GotoExpression" />.
317 /// <param name="node">The expression to visit.</param>
318 /// <returns>The modified expression, if it or any subexpression was modified;
319 /// otherwise, returns the original expression.</returns>
320 protected internal virtual Expression VisitGoto(GotoExpression node) {
321 return node.Update(VisitLabelTarget(node.Target), Visit(node.Value));
325 /// Visits the children of the <see cref="InvocationExpression" />.
327 /// <param name="node">The expression to visit.</param>
328 /// <returns>The modified expression, if it or any subexpression was modified;
329 /// otherwise, returns the original expression.</returns>
330 protected internal virtual Expression VisitInvocation(InvocationExpression node) {
331 Expression e = Visit(node.Expression);
332 Expression[] a = VisitArguments(node);
333 if (e == node.Expression && a == null) {
337 return node.Rewrite(e, a);
341 /// Visits the <see cref="LabelTarget" />.
343 /// <param name="node">The expression to visit.</param>
344 /// <returns>The modified expression, if it or any subexpression was modified;
345 /// otherwise, returns the original expression.</returns>
346 protected virtual LabelTarget VisitLabelTarget(LabelTarget node) {
351 /// Visits the children of the <see cref="LabelExpression" />.
353 /// <param name="node">The expression to visit.</param>
354 /// <returns>The modified expression, if it or any subexpression was modified;
355 /// otherwise, returns the original expression.</returns>
356 protected internal virtual Expression VisitLabel(LabelExpression node) {
357 return node.Update(VisitLabelTarget(node.Target), Visit(node.DefaultValue));
361 /// Visits the children of the <see cref="Expression<T>" />.
363 /// <typeparam name="T">The type of the delegate.</typeparam>
364 /// <param name="node">The expression to visit.</param>
365 /// <returns>The modified expression, if it or any subexpression was modified;
366 /// otherwise, returns the original expression.</returns>
367 protected internal virtual Expression VisitLambda<T>(Expression<T> node) {
368 return node.Update(Visit(node.Body), VisitAndConvert(node.Parameters, "VisitLambda"));
372 /// Visits the children of the <see cref="LoopExpression" />.
374 /// <param name="node">The expression to visit.</param>
375 /// <returns>The modified expression, if it or any subexpression was modified;
376 /// otherwise, returns the original expression.</returns>
377 protected internal virtual Expression VisitLoop(LoopExpression node) {
378 return node.Update(VisitLabelTarget(node.BreakLabel), VisitLabelTarget(node.ContinueLabel), Visit(node.Body));
382 /// Visits the children of the <see cref="MemberExpression" />.
384 /// <param name="node">The expression to visit.</param>
385 /// <returns>The modified expression, if it or any subexpression was modified;
386 /// otherwise, returns the original expression.</returns>
387 protected internal virtual Expression VisitMember(MemberExpression node) {
388 return node.Update(Visit(node.Expression));
392 /// Visits the children of the <see cref="IndexExpression" />.
394 /// <param name="node">The expression to visit.</param>
395 /// <returns>The modified expression, if it or any subexpression was modified;
396 /// otherwise, returns the original expression.</returns>
397 protected internal virtual Expression VisitIndex(IndexExpression node) {
398 Expression o = Visit(node.Object);
399 Expression[] a = VisitArguments(node);
400 if (o == node.Object && a == null) {
404 return node.Rewrite(o, a);
408 /// Visits the children of the <see cref="MethodCallExpression" />.
410 /// <param name="node">The expression to visit.</param>
411 /// <returns>The modified expression, if it or any subexpression was modified;
412 /// otherwise, returns the original expression.</returns>
413 protected internal virtual Expression VisitMethodCall(MethodCallExpression node) {
414 Expression o = Visit(node.Object);
415 Expression[] a = VisitArguments((IArgumentProvider)node);
416 if (o == node.Object && a == null) {
420 return node.Rewrite(o, a);
424 /// Visits the children of the <see cref="NewArrayExpression" />.
426 /// <param name="node">The expression to visit.</param>
427 /// <returns>The modified expression, if it or any subexpression was modified;
428 /// otherwise, returns the original expression.</returns>
429 protected internal virtual Expression VisitNewArray(NewArrayExpression node) {
430 return node.Update(Visit(node.Expressions));
434 /// Visits the children of the <see cref="NewExpression" />.
436 /// <param name="node">The expression to visit.</param>
437 /// <returns>The modified expression, if it or any subexpression was modified;
438 /// otherwise, returns the original expression.</returns>
439 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
440 protected internal virtual Expression VisitNew(NewExpression node) {
441 return node.Update(Visit(node.Arguments));
445 /// Visits the <see cref="ParameterExpression" />.
447 /// <param name="node">The expression to visit.</param>
448 /// <returns>The modified expression, if it or any subexpression was modified;
449 /// otherwise, returns the original expression.</returns>
450 protected internal virtual Expression VisitParameter(ParameterExpression node) {
455 /// Visits the children of the <see cref="RuntimeVariablesExpression" />.
457 /// <param name="node">The expression to visit.</param>
458 /// <returns>The modified expression, if it or any subexpression was modified;
459 /// otherwise, returns the original expression.</returns>
460 protected internal virtual Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
461 return node.Update(VisitAndConvert(node.Variables, "VisitRuntimeVariables"));
465 /// Visits the children of the <see cref="SwitchCase" />.
467 /// <param name="node">The expression to visit.</param>
468 /// <returns>The modified expression, if it or any subexpression was modified;
469 /// otherwise, returns the original expression.</returns>
470 protected virtual SwitchCase VisitSwitchCase(SwitchCase node) {
471 return node.Update(Visit(node.TestValues), Visit(node.Body));
475 /// Visits the children of the <see cref="SwitchExpression" />.
477 /// <param name="node">The expression to visit.</param>
478 /// <returns>The modified expression, if it or any subexpression was modified;
479 /// otherwise, returns the original expression.</returns>
480 protected internal virtual Expression VisitSwitch(SwitchExpression node) {
481 return ValidateSwitch(
484 Visit(node.SwitchValue),
485 Visit(node.Cases, VisitSwitchCase),
486 Visit(node.DefaultBody)
492 /// Visits the children of the <see cref="CatchBlock" />.
494 /// <param name="node">The expression to visit.</param>
495 /// <returns>The modified expression, if it or any subexpression was modified;
496 /// otherwise, returns the original expression.</returns>
497 protected virtual CatchBlock VisitCatchBlock(CatchBlock node) {
498 return node.Update(VisitAndConvert(node.Variable, "VisitCatchBlock"), Visit(node.Filter), Visit(node.Body));
502 /// Visits the children of the <see cref="TryExpression" />.
504 /// <param name="node">The expression to visit.</param>
505 /// <returns>The modified expression, if it or any subexpression was modified;
506 /// otherwise, returns the original expression.</returns>
507 protected internal virtual Expression VisitTry(TryExpression node) {
510 Visit(node.Handlers, VisitCatchBlock),
517 /// Visits the children of the <see cref="TypeBinaryExpression" />.
519 /// <param name="node">The expression to visit.</param>
520 /// <returns>The modified expression, if it or any subexpression was modified;
521 /// otherwise, returns the original expression.</returns>
522 protected internal virtual Expression VisitTypeBinary(TypeBinaryExpression node) {
523 return node.Update(Visit(node.Expression));
527 /// Visits the children of the <see cref="UnaryExpression" />.
529 /// <param name="node">The expression to visit.</param>
530 /// <returns>The modified expression, if it or any subexpression was modified;
531 /// otherwise, returns the original expression.</returns>
532 protected internal virtual Expression VisitUnary(UnaryExpression node) {
533 return ValidateUnary(node, node.Update(Visit(node.Operand)));
537 /// Visits the children of the <see cref="MemberInitExpression" />.
539 /// <param name="node">The expression to visit.</param>
540 /// <returns>The modified expression, if it or any subexpression was modified;
541 /// otherwise, returns the original expression.</returns>
542 protected internal virtual Expression VisitMemberInit(MemberInitExpression node) {
544 VisitAndConvert(node.NewExpression, "VisitMemberInit"),
545 Visit(node.Bindings, VisitMemberBinding)
550 /// Visits the children of the <see cref="ListInitExpression" />.
552 /// <param name="node">The expression to visit.</param>
553 /// <returns>The modified expression, if it or any subexpression was modified;
554 /// otherwise, returns the original expression.</returns>
555 protected internal virtual Expression VisitListInit(ListInitExpression node) {
557 VisitAndConvert(node.NewExpression, "VisitListInit"),
558 Visit(node.Initializers, VisitElementInit)
563 /// Visits the children of the <see cref="ElementInit" />.
565 /// <param name="node">The expression to visit.</param>
566 /// <returns>The modified expression, if it or any subexpression was modified;
567 /// otherwise, returns the original expression.</returns>
568 protected virtual ElementInit VisitElementInit(ElementInit node) {
569 return node.Update(Visit(node.Arguments));
573 /// Visits the children of the <see cref="MemberBinding" />.
575 /// <param name="node">The expression to visit.</param>
576 /// <returns>The modified expression, if it or any subexpression was modified;
577 /// otherwise, returns the original expression.</returns>
578 protected virtual MemberBinding VisitMemberBinding(MemberBinding node) {
579 switch (node.BindingType) {
580 case MemberBindingType.Assignment:
581 return VisitMemberAssignment((MemberAssignment)node);
582 case MemberBindingType.MemberBinding:
583 return VisitMemberMemberBinding((MemberMemberBinding)node);
584 case MemberBindingType.ListBinding:
585 return VisitMemberListBinding((MemberListBinding)node);
587 throw Error.UnhandledBindingType(node.BindingType);
592 /// Visits the children of the <see cref="MemberAssignment" />.
594 /// <param name="node">The expression to visit.</param>
595 /// <returns>The modified expression, if it or any subexpression was modified;
596 /// otherwise, returns the original expression.</returns>
597 protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment node) {
598 return node.Update(Visit(node.Expression));
602 /// Visits the children of the <see cref="MemberMemberBinding" />.
604 /// <param name="node">The expression to visit.</param>
605 /// <returns>The modified expression, if it or any subexpression was modified;
606 /// otherwise, returns the original expression.</returns>
607 protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) {
608 return node.Update(Visit(node.Bindings, VisitMemberBinding));
612 /// Visits the children of the <see cref="MemberListBinding" />.
614 /// <param name="node">The expression to visit.</param>
615 /// <returns>The modified expression, if it or any subexpression was modified;
616 /// otherwise, returns the original expression.</returns>
617 protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding node) {
618 return node.Update(Visit(node.Initializers, VisitElementInit));
623 // Prevent some common cases of invalid rewrites.
625 // Essentially, we don't want the rewritten node to be semantically
626 // bound by the factory, which may do the wrong thing. Instead we
627 // require derived classes to be explicit about what they want to do if
630 private static UnaryExpression ValidateUnary(UnaryExpression before, UnaryExpression after) {
631 if (before != after && before.Method == null) {
632 if (after.Method != null) {
633 throw Error.MustRewriteWithoutMethod(after.Method, "VisitUnary");
636 // rethrow has null operand
637 if (before.Operand != null && after.Operand != null) {
638 ValidateChildType(before.Operand.Type, after.Operand.Type, "VisitUnary");
644 private static BinaryExpression ValidateBinary(BinaryExpression before, BinaryExpression after) {
645 if (before != after && before.Method == null) {
646 if (after.Method != null) {
647 throw Error.MustRewriteWithoutMethod(after.Method, "VisitBinary");
650 ValidateChildType(before.Left.Type, after.Left.Type, "VisitBinary");
651 ValidateChildType(before.Right.Type, after.Right.Type, "VisitBinary");
656 // We wouldn't need this if switch didn't infer the method.
657 private static SwitchExpression ValidateSwitch(SwitchExpression before, SwitchExpression after) {
658 // If we did not have a method, we don't want to bind to one,
659 // it might not be the right thing.
660 if (before.Comparison == null && after.Comparison != null) {
661 throw Error.MustRewriteWithoutMethod(after.Comparison, "VisitSwitch");
666 // Value types must stay as the same type, otherwise it's now a
667 // different operation, e.g. adding two doubles vs adding two ints.
668 private static void ValidateChildType(Type before, Type after, string methodName) {
669 if (before.IsValueType) {
670 if (TypeUtils.AreEquivalent(before, after)) {
671 // types are the same value type
674 } else if (!after.IsValueType) {
675 // both are reference types
679 // Otherwise, it's an invalid type change.
680 throw Error.MustRewriteChildToSameType(before, after, methodName);