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.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Threading;
24 namespace Microsoft.Scripting.Ast {
26 namespace System.Linq.Expressions {
29 /// Represents a block that contains a sequence of expressions where variables can be defined.
31 [DebuggerTypeProxy(typeof(Expression.BlockExpressionProxy))]
32 public class BlockExpression : Expression {
34 /// Gets the expressions in this block.
36 public ReadOnlyCollection<Expression> Expressions {
37 get { return GetOrMakeExpressions(); }
41 /// Gets the variables defined in this block.
43 public ReadOnlyCollection<ParameterExpression> Variables {
45 return GetOrMakeVariables();
50 /// Gets the last expression in this block.
52 public Expression Result {
54 Debug.Assert(ExpressionCount > 0);
55 return GetExpression(ExpressionCount - 1);
59 internal BlockExpression() {
63 /// Dispatches to the specific visit method for this node type.
65 protected internal override Expression Accept(ExpressionVisitor visitor) {
66 return visitor.VisitBlock(this);
70 /// Returns the node type of this Expression. Extension nodes should return
71 /// ExpressionType.Extension when overriding this method.
73 /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
74 public sealed override ExpressionType NodeType {
75 get { return ExpressionType.Block; }
79 /// Gets the static type of the expression that this <see cref="Expression" /> represents.
81 /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
82 public override Type Type {
83 get { return GetExpression(ExpressionCount - 1).Type; }
87 /// Creates a new expression that is like this one, but using the
88 /// supplied children. If all of the children are the same, it will
89 /// return this expression.
91 /// <param name="variables">The <see cref="Variables" /> property of the result.</param>
92 /// <param name="expressions">The <see cref="Expressions" /> property of the result.</param>
93 /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
94 public BlockExpression Update(IEnumerable<ParameterExpression> variables, IEnumerable<Expression> expressions) {
95 if (variables == Variables && expressions == Expressions) {
99 return Expression.Block(Type, variables, expressions);
102 internal virtual Expression GetExpression(int index) {
103 throw ContractUtils.Unreachable;
106 internal virtual int ExpressionCount {
108 throw ContractUtils.Unreachable;
112 internal virtual ReadOnlyCollection<Expression> GetOrMakeExpressions() {
113 throw ContractUtils.Unreachable;
116 internal virtual ParameterExpression GetVariable(int index) {
117 throw ContractUtils.Unreachable;
120 internal virtual int VariableCount {
126 internal virtual ReadOnlyCollection<ParameterExpression> GetOrMakeVariables() {
127 return EmptyReadOnlyCollection<ParameterExpression>.Instance;
131 /// Makes a copy of this node replacing the parameters/args with the provided values. The
132 /// shape of the parameters/args needs to match the shape of the current block - in other
133 /// words there should be the same # of parameters and args.
135 /// parameters can be null in which case the existing parameters are used.
137 /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized
138 /// subclass of BlockExpression which is being used.
140 internal virtual BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
141 throw ContractUtils.Unreachable;
145 /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
147 /// This is similar to the ReturnReadOnly which only takes a single argument. This version
148 /// supports nodes which hold onto 5 Expressions and puts all of the arguments into the
149 /// ReadOnlyCollection.
151 /// Ultimately this means if we create the readonly collection we will be slightly more wasteful as we'll
152 /// have a readonly collection + some fields in the type. The DLR internally avoids accessing anything
153 /// which would force the readonly collection to be created.
155 /// This is used by BlockExpression5 and MethodCallExpression5.
157 internal static ReadOnlyCollection<Expression> ReturnReadOnlyExpressions(BlockExpression provider, ref object collection) {
158 Expression tObj = collection as Expression;
160 // otherwise make sure only one readonly collection ever gets exposed
161 Interlocked.CompareExchange(
163 new ReadOnlyCollection<Expression>(new BlockExpressionList(provider, tObj)),
168 // and return what is not guaranteed to be a readonly collection
169 return (ReadOnlyCollection<Expression>)collection;
173 #region Specialized Subclasses
175 internal sealed class Block2 : BlockExpression {
176 private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider
177 private readonly Expression _arg1; // storage for the 2nd argument.
179 internal Block2(Expression arg0, Expression arg1) {
184 internal override Expression GetExpression(int index) {
186 case 0: return ReturnObject<Expression>(_arg0);
187 case 1: return _arg1;
188 default: throw new InvalidOperationException();
192 internal override int ExpressionCount {
198 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
199 return ReturnReadOnlyExpressions(this, ref _arg0);
202 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
203 Debug.Assert(args.Length == 2);
204 Debug.Assert(variables == null || variables.Count == 0);
206 return new Block2(args[0], args[1]);
210 internal sealed class Block3 : BlockExpression {
211 private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider
212 private readonly Expression _arg1, _arg2; // storage for the 2nd and 3rd arguments.
214 internal Block3(Expression arg0, Expression arg1, Expression arg2) {
220 internal override Expression GetExpression(int index) {
222 case 0: return ReturnObject<Expression>(_arg0);
223 case 1: return _arg1;
224 case 2: return _arg2;
225 default: throw new InvalidOperationException();
229 internal override int ExpressionCount {
235 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
236 return ReturnReadOnlyExpressions(this, ref _arg0);
239 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
240 Debug.Assert(args.Length == 3);
241 Debug.Assert(variables == null || variables.Count == 0);
243 return new Block3(args[0], args[1], args[2]);
247 internal sealed class Block4 : BlockExpression {
248 private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider
249 private readonly Expression _arg1, _arg2, _arg3; // storarg for the 2nd, 3rd, and 4th arguments.
251 internal Block4(Expression arg0, Expression arg1, Expression arg2, Expression arg3) {
258 internal override Expression GetExpression(int index) {
260 case 0: return ReturnObject<Expression>(_arg0);
261 case 1: return _arg1;
262 case 2: return _arg2;
263 case 3: return _arg3;
264 default: throw new InvalidOperationException();
268 internal override int ExpressionCount {
274 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
275 return ReturnReadOnlyExpressions(this, ref _arg0);
278 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
279 Debug.Assert(args.Length == 4);
280 Debug.Assert(variables == null || variables.Count == 0);
282 return new Block4(args[0], args[1], args[2], args[3]);
286 internal sealed class Block5 : BlockExpression {
287 private object _arg0; // storage for the 1st argument or a readonly collection. See IArgumentProvider
288 private readonly Expression _arg1, _arg2, _arg3, _arg4; // storage for the 2nd - 5th args.
290 internal Block5(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) {
298 internal override Expression GetExpression(int index) {
300 case 0: return ReturnObject<Expression>(_arg0);
301 case 1: return _arg1;
302 case 2: return _arg2;
303 case 3: return _arg3;
304 case 4: return _arg4;
305 default: throw new InvalidOperationException();
309 internal override int ExpressionCount {
315 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
316 return ReturnReadOnlyExpressions(this, ref _arg0);
319 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
320 Debug.Assert(args.Length == 5);
321 Debug.Assert(variables == null || variables.Count == 0);
323 return new Block5(args[0], args[1], args[2], args[3], args[4]);
327 internal class BlockN : BlockExpression {
328 private IList<Expression> _expressions; // either the original IList<Expression> or a ReadOnlyCollection if the user has accessed it.
330 internal BlockN(IList<Expression> expressions) {
331 Debug.Assert(expressions.Count != 0);
333 _expressions = expressions;
336 internal override Expression GetExpression(int index) {
337 Debug.Assert(index >= 0 && index < _expressions.Count);
339 return _expressions[index];
342 internal override int ExpressionCount {
344 return _expressions.Count;
348 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
349 return ReturnReadOnly(ref _expressions);
352 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
353 Debug.Assert(variables == null || variables.Count == 0);
355 return new BlockN(args);
359 internal class ScopeExpression : BlockExpression {
360 private IList<ParameterExpression> _variables; // list of variables or ReadOnlyCollection if the user has accessed the readonly collection
362 internal ScopeExpression(IList<ParameterExpression> variables) {
363 _variables = variables;
366 internal override int VariableCount {
368 return _variables.Count;
372 internal override ParameterExpression GetVariable(int index) {
373 return _variables[index];
376 internal override ReadOnlyCollection<ParameterExpression> GetOrMakeVariables() {
377 return ReturnReadOnly(ref _variables);
380 protected IList<ParameterExpression> VariablesList {
386 // Used for rewrite of the nodes to either reuse existing set of variables if not rewritten.
387 internal IList<ParameterExpression> ReuseOrValidateVariables(ReadOnlyCollection<ParameterExpression> variables) {
388 if (variables != null && variables != VariablesList) {
389 // Need to validate the new variables (uniqueness, not byref)
390 ValidateVariables(variables, "variables");
393 return VariablesList;
398 internal sealed class Scope1 : ScopeExpression {
399 private object _body;
401 internal Scope1(IList<ParameterExpression> variables, Expression body)
406 internal override Expression GetExpression(int index) {
408 case 0: return ReturnObject<Expression>(_body);
409 default: throw new InvalidOperationException();
413 internal override int ExpressionCount {
419 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
420 return ReturnReadOnlyExpressions(this, ref _body);
423 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
424 Debug.Assert(args.Length == 1);
425 Debug.Assert(variables == null || variables.Count == VariableCount);
427 return new Scope1(ReuseOrValidateVariables(variables), args[0]);
431 internal class ScopeN : ScopeExpression {
432 private IList<Expression> _body;
434 internal ScopeN(IList<ParameterExpression> variables, IList<Expression> body)
439 internal override Expression GetExpression(int index) {
443 internal override int ExpressionCount {
449 internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() {
450 return ReturnReadOnly(ref _body);
453 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
454 Debug.Assert(args.Length == ExpressionCount);
455 Debug.Assert(variables == null || variables.Count == VariableCount);
457 return new ScopeN(ReuseOrValidateVariables(variables), args);
461 internal class ScopeWithType : ScopeN {
462 private readonly Type _type;
464 internal ScopeWithType(IList<ParameterExpression> variables, IList<Expression> expressions, Type type)
465 : base(variables, expressions) {
469 public sealed override Type Type {
470 get { return _type; }
473 internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) {
474 Debug.Assert(args.Length == ExpressionCount);
475 Debug.Assert(variables == null || variables.Count == VariableCount);
477 return new ScopeWithType(ReuseOrValidateVariables(variables), args, _type);
483 #region Block List Classes
486 /// Provides a wrapper around an IArgumentProvider which exposes the argument providers
487 /// members out as an IList of Expression. This is used to avoid allocating an array
488 /// which needs to be stored inside of a ReadOnlyCollection. Instead this type has
489 /// the same amount of overhead as an array without duplicating the storage of the
490 /// elements. This ensures that internally we can avoid creating and copying arrays
491 /// while users of the Expression trees also don't pay a size penalty for this internal
492 /// optimization. See IArgumentProvider for more general information on the Expression
493 /// tree optimizations being used here.
495 internal class BlockExpressionList : IList<Expression> {
496 private readonly BlockExpression _block;
497 private readonly Expression _arg0;
499 internal BlockExpressionList(BlockExpression provider, Expression arg0) {
504 #region IList<Expression> Members
506 public int IndexOf(Expression item) {
511 for (int i = 1; i < _block.ExpressionCount; i++) {
512 if (_block.GetExpression(i) == item) {
520 public void Insert(int index, Expression item) {
521 throw ContractUtils.Unreachable;
524 public void RemoveAt(int index) {
525 throw ContractUtils.Unreachable;
528 public Expression this[int index] {
534 return _block.GetExpression(index);
537 throw ContractUtils.Unreachable;
543 #region ICollection<Expression> Members
545 public void Add(Expression item) {
546 throw ContractUtils.Unreachable;
549 public void Clear() {
550 throw ContractUtils.Unreachable;
553 public bool Contains(Expression item) {
554 return IndexOf(item) != -1;
557 public void CopyTo(Expression[] array, int arrayIndex) {
558 array[arrayIndex++] = _arg0;
559 for (int i = 1; i < _block.ExpressionCount; i++) {
560 array[arrayIndex++] = _block.GetExpression(i);
565 get { return _block.ExpressionCount; }
568 public bool IsReadOnly {
572 public bool Remove(Expression item) {
573 throw ContractUtils.Unreachable;
578 #region IEnumerable<Expression> Members
580 public IEnumerator<Expression> GetEnumerator() {
583 for (int i = 1; i < _block.ExpressionCount; i++) {
584 yield return _block.GetExpression(i);
590 #region IEnumerable Members
592 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
595 for (int i = 1; i < _block.ExpressionCount; i++) {
596 yield return _block.GetExpression(i);
605 public partial class Expression {
608 /// Creates a <see cref="BlockExpression"/> that contains two expressions and has no variables.
610 /// <param name="arg0">The first expression in the block.</param>
611 /// <param name="arg1">The second expression in the block.</param>
612 /// <returns>The created <see cref="BlockExpression"/>.</returns>
613 public static BlockExpression Block(Expression arg0, Expression arg1) {
614 RequiresCanRead(arg0, "arg0");
615 RequiresCanRead(arg1, "arg1");
617 return new Block2(arg0, arg1);
620 /// Creates a <see cref="BlockExpression"/> that contains three expressions and has no variables.
622 /// <param name="arg0">The first expression in the block.</param>
623 /// <param name="arg1">The second expression in the block.</param>
624 /// <param name="arg2">The third expression in the block.</param>
625 /// <returns>The created <see cref="BlockExpression"/>.</returns>
626 public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2) {
627 RequiresCanRead(arg0, "arg0");
628 RequiresCanRead(arg1, "arg1");
629 RequiresCanRead(arg2, "arg2");
630 return new Block3(arg0, arg1, arg2);
634 /// Creates a <see cref="BlockExpression"/> that contains four expressions and has no variables.
636 /// <param name="arg0">The first expression in the block.</param>
637 /// <param name="arg1">The second expression in the block.</param>
638 /// <param name="arg2">The third expression in the block.</param>
639 /// <param name="arg3">The fourth expression in the block.</param>
640 /// <returns>The created <see cref="BlockExpression"/>.</returns>
641 public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3) {
642 RequiresCanRead(arg0, "arg0");
643 RequiresCanRead(arg1, "arg1");
644 RequiresCanRead(arg2, "arg2");
645 RequiresCanRead(arg3, "arg3");
646 return new Block4(arg0, arg1, arg2, arg3);
650 /// Creates a <see cref="BlockExpression"/> that contains five expressions and has no variables.
652 /// <param name="arg0">The first expression in the block.</param>
653 /// <param name="arg1">The second expression in the block.</param>
654 /// <param name="arg2">The third expression in the block.</param>
655 /// <param name="arg3">The fourth expression in the block.</param>
656 /// <param name="arg4">The fifth expression in the block.</param>
657 /// <returns>The created <see cref="BlockExpression"/>.</returns>
658 public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) {
659 RequiresCanRead(arg0, "arg0");
660 RequiresCanRead(arg1, "arg1");
661 RequiresCanRead(arg2, "arg2");
662 RequiresCanRead(arg3, "arg3");
663 RequiresCanRead(arg4, "arg4");
665 return new Block5(arg0, arg1, arg2, arg3, arg4);
669 /// Creates a <see cref="BlockExpression"/> that contains the given expressions and has no variables.
671 /// <param name="expressions">The expressions in the block.</param>
672 /// <returns>The created <see cref="BlockExpression"/>.</returns>
673 public static BlockExpression Block(params Expression[] expressions) {
674 ContractUtils.RequiresNotNull(expressions, "expressions");
676 switch (expressions.Length) {
677 case 2: return Block(expressions[0], expressions[1]);
678 case 3: return Block(expressions[0], expressions[1], expressions[2]);
679 case 4: return Block(expressions[0], expressions[1], expressions[2], expressions[3]);
680 case 5: return Block(expressions[0], expressions[1], expressions[2], expressions[3], expressions[4]);
682 ContractUtils.RequiresNotEmpty(expressions, "expressions");
683 RequiresCanRead(expressions, "expressions");
684 return new BlockN(expressions.Copy());
689 /// Creates a <see cref="BlockExpression"/> that contains the given expressions and has no variables.
691 /// <param name="expressions">The expressions in the block.</param>
692 /// <returns>The created <see cref="BlockExpression"/>.</returns>
693 public static BlockExpression Block(IEnumerable<Expression> expressions) {
694 return Block(EmptyReadOnlyCollection<ParameterExpression>.Instance, expressions);
698 /// Creates a <see cref="BlockExpression"/> that contains the given expressions, has no variables and has specific result type.
700 /// <param name="type">The result type of the block.</param>
701 /// <param name="expressions">The expressions in the block.</param>
702 /// <returns>The created <see cref="BlockExpression"/>.</returns>
703 public static BlockExpression Block(Type type, params Expression[] expressions) {
704 ContractUtils.RequiresNotNull(expressions, "expressions");
705 return Block(type, (IEnumerable<Expression>)expressions);
709 /// Creates a <see cref="BlockExpression"/> that contains the given expressions, has no variables and has specific result type.
711 /// <param name="type">The result type of the block.</param>
712 /// <param name="expressions">The expressions in the block.</param>
713 /// <returns>The created <see cref="BlockExpression"/>.</returns>
714 public static BlockExpression Block(Type type, IEnumerable<Expression> expressions) {
715 return Block(type, EmptyReadOnlyCollection<ParameterExpression>.Instance, expressions);
719 /// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions.
721 /// <param name="variables">The variables in the block.</param>
722 /// <param name="expressions">The expressions in the block.</param>
723 /// <returns>The created <see cref="BlockExpression"/>.</returns>
724 public static BlockExpression Block(IEnumerable<ParameterExpression> variables, params Expression[] expressions) {
725 return Block(variables, (IEnumerable<Expression>)expressions);
729 /// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions.
731 /// <param name="type">The result type of the block.</param>
732 /// <param name="variables">The variables in the block.</param>
733 /// <param name="expressions">The expressions in the block.</param>
734 /// <returns>The created <see cref="BlockExpression"/>.</returns>
735 public static BlockExpression Block(Type type, IEnumerable<ParameterExpression> variables, params Expression[] expressions) {
736 return Block(type, variables, (IEnumerable<Expression>)expressions);
740 /// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions.
742 /// <param name="variables">The variables in the block.</param>
743 /// <param name="expressions">The expressions in the block.</param>
744 /// <returns>The created <see cref="BlockExpression"/>.</returns>
745 public static BlockExpression Block(IEnumerable<ParameterExpression> variables, IEnumerable<Expression> expressions) {
746 ContractUtils.RequiresNotNull(expressions, "expressions");
747 var expressionList = expressions.ToReadOnly();
748 ContractUtils.RequiresNotEmpty(expressionList, "expressions");
749 RequiresCanRead(expressionList, "expressions");
751 return Block(expressionList.Last().Type, variables, expressionList);
755 /// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions.
757 /// <param name="type">The result type of the block.</param>
758 /// <param name="variables">The variables in the block.</param>
759 /// <param name="expressions">The expressions in the block.</param>
760 /// <returns>The created <see cref="BlockExpression"/>.</returns>
761 public static BlockExpression Block(Type type, IEnumerable<ParameterExpression> variables, IEnumerable<Expression> expressions) {
762 ContractUtils.RequiresNotNull(type, "type");
763 ContractUtils.RequiresNotNull(expressions, "expressions");
765 var expressionList = expressions.ToReadOnly();
766 var variableList = variables.ToReadOnly();
768 ContractUtils.RequiresNotEmpty(expressionList, "expressions");
769 RequiresCanRead(expressionList, "expressions");
770 ValidateVariables(variableList, "variables");
772 Expression last = expressionList.Last();
773 if (type != typeof(void)) {
774 if (!TypeUtils.AreReferenceAssignable(type, last.Type)) {
775 throw Error.ArgumentTypesMustMatch();
779 if (!TypeUtils.AreEquivalent(type, last.Type)) {
780 return new ScopeWithType(variableList, expressionList, type);
782 if (expressionList.Count == 1) {
783 return new Scope1(variableList, expressionList[0]);
785 return new ScopeN(variableList, expressionList);
790 // Checks that all variables are non-null, not byref, and unique.
791 internal static void ValidateVariables(ReadOnlyCollection<ParameterExpression> varList, string collectionName) {
792 if (varList.Count == 0) {
796 int count = varList.Count;
797 var set = new Set<ParameterExpression>(count);
798 for (int i = 0; i < count; i++) {
799 ParameterExpression v = varList[i];
801 throw new ArgumentNullException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}[{1}]", collectionName, set.Count));
804 throw Error.VariableMustNotBeByRef(v, v.Type);
806 if (set.Contains(v)) {
807 throw Error.DuplicateVariable(v);