BlockExpression.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / Microsoft / Scripting / Ast / BlockExpression.cs / 1305376 / BlockExpression.cs

                            /* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Diagnostics;
using System.Dynamic.Utils; 
using System.Threading;

#if SILVERLIGHT
using System.Core; 
#endif
 
namespace System.Linq.Expressions { 
    /// 
    /// Represents a block that contains a sequence of expressions where variables can be defined. 
    /// 
#if !SILVERLIGHT
    [DebuggerTypeProxy(typeof(Expression.BlockExpressionProxy))]
#endif 
    public class BlockExpression : Expression {
        ///  
        /// Gets the expressions in this block. 
        /// 
        public ReadOnlyCollection Expressions { 
            get { return GetOrMakeExpressions(); }
        }

        ///  
        /// Gets the variables defined in this block.
        ///  
        public ReadOnlyCollection Variables { 
            get {
                return GetOrMakeVariables(); 
            }
        }

        ///  
        /// Gets the last expression in this block.
        ///  
        public Expression Result { 
            get {
                Debug.Assert(ExpressionCount > 0); 
                return GetExpression(ExpressionCount - 1);
            }
        }
 
        internal BlockExpression() {
        } 
 
        /// 
        /// Dispatches to the specific visit method for this node type. 
        /// 
        protected internal override Expression Accept(ExpressionVisitor visitor) {
            return visitor.VisitBlock(this);
        } 

        ///  
        /// Returns the node type of this Expression. Extension nodes should return 
        /// ExpressionType.Extension when overriding this method.
        ///  
        /// The  of the expression.
        public sealed override ExpressionType NodeType {
            get { return ExpressionType.Block; }
        } 

        ///  
        /// Gets the static type of the expression that this  represents. 
        /// 
        /// The  that represents the static type of the expression. 
        public override Type Type {
            get { return GetExpression(ExpressionCount - 1).Type; }
        }
 
        /// 
        /// Creates a new expression that is like this one, but using the 
        /// supplied children. If all of the children are the same, it will 
        /// return this expression.
        ///  
        /// The  property of the result.
        /// The  property of the result.
        /// This expression if no children changed, or an expression with the updated children.
        public BlockExpression Update(IEnumerable variables, IEnumerable expressions) { 
            if (variables == Variables && expressions == Expressions) {
                return this; 
            } 

            return Expression.Block(Type, variables, expressions); 
        }

        internal virtual Expression GetExpression(int index) {
            throw ContractUtils.Unreachable; 
        }
 
        internal virtual int ExpressionCount { 
            get {
                throw ContractUtils.Unreachable; 
            }
        }

        internal virtual ReadOnlyCollection GetOrMakeExpressions() { 
            throw ContractUtils.Unreachable;
        } 
 
        internal virtual ParameterExpression GetVariable(int index) {
            throw ContractUtils.Unreachable; 
        }

        internal virtual int VariableCount {
            get { 
                return 0;
            } 
        } 

        internal virtual ReadOnlyCollection GetOrMakeVariables() { 
            return EmptyReadOnlyCollection.Instance;
        }

        ///  
        /// Makes a copy of this node replacing the parameters/args with the provided values.  The
        /// shape of the parameters/args needs to match the shape of the current block - in other 
        /// words there should be the same # of parameters and args. 
        ///
        /// parameters can be null in which case the existing parameters are used. 
        ///
        /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized
        /// subclass of BlockExpression which is being used.
        ///  
        internal virtual BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            throw ContractUtils.Unreachable; 
        } 

        ///  
        /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
        ///
        /// This is similar to the ReturnReadOnly which only takes a single argument. This version
        /// supports nodes which hold onto 5 Expressions and puts all of the arguments into the 
        /// ReadOnlyCollection.
        /// 
        /// Ultimately this means if we create the readonly collection we will be slightly more wasteful as we'll 
        /// have a readonly collection + some fields in the type.  The DLR internally avoids accessing anything
        /// which would force the readonly collection to be created. 
        ///
        /// This is used by BlockExpression5 and MethodCallExpression5.
        /// 
        internal static ReadOnlyCollection ReturnReadOnlyExpressions(BlockExpression provider, ref object collection) { 
            Expression tObj = collection as Expression;
            if (tObj != null) { 
                // otherwise make sure only one readonly collection ever gets exposed 
                Interlocked.CompareExchange(
                    ref collection, 
                    new ReadOnlyCollection(new BlockExpressionList(provider, tObj)),
                    tObj
                );
            } 

            // and return what is not guaranteed to be a readonly collection 
            return (ReadOnlyCollection)collection; 
        }
    } 

    #region Specialized Subclasses

    internal sealed class Block2 : BlockExpression { 
        private object _arg0;                   // storage for the 1st argument or a readonly collection.  See IArgumentProvider
        private readonly Expression _arg1;      // storage for the 2nd argument. 
 
        internal Block2(Expression arg0, Expression arg1) {
            _arg0 = arg0; 
            _arg1 = arg1;
        }

        internal override Expression GetExpression(int index) { 
            switch (index) {
                case 0: return ReturnObject(_arg0); 
                case 1: return _arg1; 
                default: throw new InvalidOperationException();
            } 
        }

        internal override int ExpressionCount {
            get { 
                return 2;
            } 
        } 

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnlyExpressions(this, ref _arg0);
        }

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) { 
            Debug.Assert(args.Length == 2);
            Debug.Assert(variables == null || variables.Count == 0); 
 
            return new Block2(args[0], args[1]);
        } 
    }

    internal sealed class Block3 : BlockExpression {
        private object _arg0;                       // storage for the 1st argument or a readonly collection.  See IArgumentProvider 
        private readonly Expression _arg1, _arg2;   // storage for the 2nd and 3rd arguments.
 
        internal Block3(Expression arg0, Expression arg1, Expression arg2) { 
            _arg0 = arg0;
            _arg1 = arg1; 
            _arg2 = arg2;
        }

        internal override Expression GetExpression(int index) { 
            switch (index) {
                case 0: return ReturnObject(_arg0); 
                case 1: return _arg1; 
                case 2: return _arg2;
                default: throw new InvalidOperationException(); 
            }
        }

        internal override int ExpressionCount { 
            get {
                return 3; 
            } 
        }
 
        internal override ReadOnlyCollection GetOrMakeExpressions() {
            return ReturnReadOnlyExpressions(this, ref _arg0);
        }
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == 3); 
            Debug.Assert(variables == null || variables.Count == 0); 

            return new Block3(args[0], args[1], args[2]); 
        }
    }

    internal sealed class Block4 : BlockExpression { 
        private object _arg0;                               // storage for the 1st argument or a readonly collection.  See IArgumentProvider
        private readonly Expression _arg1, _arg2, _arg3;    // storarg for the 2nd, 3rd, and 4th arguments. 
 
        internal Block4(Expression arg0, Expression arg1, Expression arg2, Expression arg3) {
            _arg0 = arg0; 
            _arg1 = arg1;
            _arg2 = arg2;
            _arg3 = arg3;
        } 

        internal override Expression GetExpression(int index) { 
            switch (index) { 
                case 0: return ReturnObject(_arg0);
                case 1: return _arg1; 
                case 2: return _arg2;
                case 3: return _arg3;
                default: throw new InvalidOperationException();
            } 
        }
 
        internal override int ExpressionCount { 
            get {
                return 4; 
            }
        }

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnlyExpressions(this, ref _arg0);
        } 
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == 4); 
            Debug.Assert(variables == null || variables.Count == 0);

            return new Block4(args[0], args[1], args[2], args[3]);
        } 
    }
 
    internal sealed class Block5 : BlockExpression { 
        private object _arg0;                                       // storage for the 1st argument or a readonly collection.  See IArgumentProvider
        private readonly Expression _arg1, _arg2, _arg3, _arg4;     // storage for the 2nd - 5th args. 

        internal Block5(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) {
            _arg0 = arg0;
            _arg1 = arg1; 
            _arg2 = arg2;
            _arg3 = arg3; 
            _arg4 = arg4; 
        }
 
        internal override Expression GetExpression(int index) {
            switch (index) {
                case 0: return ReturnObject(_arg0);
                case 1: return _arg1; 
                case 2: return _arg2;
                case 3: return _arg3; 
                case 4: return _arg4; 
                default: throw new InvalidOperationException();
            } 
        }

        internal override int ExpressionCount {
            get { 
                return 5;
            } 
        } 

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnlyExpressions(this, ref _arg0);
        }

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) { 
            Debug.Assert(args.Length == 5);
            Debug.Assert(variables == null || variables.Count == 0); 
 
            return new Block5(args[0], args[1], args[2], args[3], args[4]);
        } 
    }

    internal class BlockN : BlockExpression {
        private IList _expressions;         // either the original IList or a ReadOnlyCollection if the user has accessed it. 

        internal BlockN(IList expressions) { 
            Debug.Assert(expressions.Count != 0); 

            _expressions = expressions; 
        }

        internal override Expression GetExpression(int index) {
            Debug.Assert(index >= 0 && index < _expressions.Count); 

            return _expressions[index]; 
        } 

        internal override int ExpressionCount { 
            get {
                return _expressions.Count;
            }
        } 

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnly(ref _expressions); 
        }
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(variables == null || variables.Count == 0);

            return new BlockN(args); 
        }
    } 
 
    internal class ScopeExpression : BlockExpression {
        private IList _variables;      // list of variables or ReadOnlyCollection if the user has accessed the readonly collection 

        internal ScopeExpression(IList variables) {
            _variables = variables;
        } 

        internal override int VariableCount { 
            get { 
                return _variables.Count;
            } 
        }

        internal override ParameterExpression GetVariable(int index) {
            return _variables[index]; 
        }
 
        internal override ReadOnlyCollection GetOrMakeVariables() { 
            return ReturnReadOnly(ref _variables);
        } 

        protected IList VariablesList {
            get {
                return _variables; 
            }
        } 
 
        // Used for rewrite of the nodes to either reuse existing set of variables if not rewritten.
        internal IList ReuseOrValidateVariables(ReadOnlyCollection variables) { 
            if (variables != null && variables != VariablesList) {
                // Need to validate the new variables (uniqueness, not byref)
                ValidateVariables(variables, "variables");
                return variables; 
            } else {
                return VariablesList; 
            } 
        }
    } 

    internal sealed class Scope1 : ScopeExpression {
        private object _body;
 
        internal Scope1(IList variables, Expression body)
            : base(variables) { 
            _body = body; 
        }
 
        internal override Expression GetExpression(int index) {
            switch (index) {
                case 0: return ReturnObject(_body);
                default: throw new InvalidOperationException(); 
            }
        } 
 
        internal override int ExpressionCount {
            get { 
                return 1;
            }
        }
 
        internal override ReadOnlyCollection GetOrMakeExpressions() {
            return ReturnReadOnlyExpressions(this, ref _body); 
        } 

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) { 
            Debug.Assert(args.Length == 1);
            Debug.Assert(variables == null || variables.Count == VariableCount);

            return new Scope1(ReuseOrValidateVariables(variables), args[0]); 
        }
    } 
 
    internal class ScopeN : ScopeExpression {
        private IList _body; 

        internal ScopeN(IList variables, IList body)
            : base(variables) {
            _body = body; 
        }
 
        internal override Expression GetExpression(int index) { 
            return _body[index];
        } 

        internal override int ExpressionCount {
            get {
                return _body.Count; 
            }
        } 
 
        internal override ReadOnlyCollection GetOrMakeExpressions() {
            return ReturnReadOnly(ref _body); 
        }

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == ExpressionCount); 
            Debug.Assert(variables == null || variables.Count == VariableCount);
 
            return new ScopeN(ReuseOrValidateVariables(variables), args); 
        }
    } 

    internal class ScopeWithType : ScopeN {
        private readonly Type _type;
 
        internal ScopeWithType(IList variables, IList expressions, Type type)
            : base(variables, expressions) { 
            _type = type; 
        }
 
        public sealed override Type Type {
            get { return _type; }
        }
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == ExpressionCount); 
            Debug.Assert(variables == null || variables.Count == VariableCount); 

            return new ScopeWithType(ReuseOrValidateVariables(variables), args, _type); 
        }
    }

    #endregion 

    #region Block List Classes 
 
    /// 
    /// Provides a wrapper around an IArgumentProvider which exposes the argument providers 
    /// members out as an IList of Expression.  This is used to avoid allocating an array
    /// which needs to be stored inside of a ReadOnlyCollection.  Instead this type has
    /// the same amount of overhead as an array without duplicating the storage of the
    /// elements.  This ensures that internally we can avoid creating and copying arrays 
    /// while users of the Expression trees also don't pay a size penalty for this internal
    /// optimization.  See IArgumentProvider for more general information on the Expression 
    /// tree optimizations being used here. 
    /// 
    internal class BlockExpressionList : IList { 
        private readonly BlockExpression _block;
        private readonly Expression _arg0;

        internal BlockExpressionList(BlockExpression provider, Expression arg0) { 
            _block = provider;
            _arg0 = arg0; 
        } 

        #region IList Members 

        public int IndexOf(Expression item) {
            if (_arg0 == item) {
                return 0; 
            }
 
            for (int i = 1; i < _block.ExpressionCount; i++) { 
                if (_block.GetExpression(i) == item) {
                    return i; 
                }
            }

            return -1; 
        }
 
        public void Insert(int index, Expression item) { 
            throw ContractUtils.Unreachable;
        } 

        public void RemoveAt(int index) {
            throw ContractUtils.Unreachable;
        } 

        public Expression this[int index] { 
            get { 
                if (index == 0) {
                    return _arg0; 
                }

                return _block.GetExpression(index);
            } 
            set {
                throw ContractUtils.Unreachable; 
            } 
        }
 
        #endregion

        #region ICollection Members
 
        public void Add(Expression item) {
            throw ContractUtils.Unreachable; 
        } 

        public void Clear() { 
            throw ContractUtils.Unreachable;
        }

        public bool Contains(Expression item) { 
            return IndexOf(item) != -1;
        } 
 
        public void CopyTo(Expression[] array, int arrayIndex) {
            array[arrayIndex++] = _arg0; 
            for (int i = 1; i < _block.ExpressionCount; i++) {
                array[arrayIndex++] = _block.GetExpression(i);
            }
        } 

        public int Count { 
            get { return _block.ExpressionCount; } 
        }
 
        public bool IsReadOnly {
            get { return true; }
        }
 
        public bool Remove(Expression item) {
            throw ContractUtils.Unreachable; 
        } 

        #endregion 

        #region IEnumerable Members

        public IEnumerator GetEnumerator() { 
            yield return _arg0;
 
            for (int i = 1; i < _block.ExpressionCount; i++) { 
                yield return _block.GetExpression(i);
            } 
        }

        #endregion
 
        #region IEnumerable Members
 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 
            yield return _arg0;
 
            for (int i = 1; i < _block.ExpressionCount; i++) {
                yield return _block.GetExpression(i);
            }
        } 

        #endregion 
    } 

    #endregion 

    public partial class Expression {

        ///  
        /// Creates a  that contains two expressions and has no variables.
        ///  
        /// The first expression in the block. 
        /// The second expression in the block.
        /// The created . 
        public static BlockExpression Block(Expression arg0, Expression arg1) {
            RequiresCanRead(arg0, "arg0");
            RequiresCanRead(arg1, "arg1");
 
            return new Block2(arg0, arg1);
        } 
        ///  
        /// Creates a  that contains three expressions and has no variables.
        ///  
        /// The first expression in the block.
        /// The second expression in the block.
        /// The third expression in the block.
        /// The created . 
        public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2) {
            RequiresCanRead(arg0, "arg0"); 
            RequiresCanRead(arg1, "arg1"); 
            RequiresCanRead(arg2, "arg2");
            return new Block3(arg0, arg1, arg2); 
        }

        /// 
        /// Creates a  that contains four expressions and has no variables. 
        /// 
        /// The first expression in the block. 
        /// The second expression in the block. 
        /// The third expression in the block.
        /// The fourth expression in the block. 
        /// The created .
        public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3) {
            RequiresCanRead(arg0, "arg0");
            RequiresCanRead(arg1, "arg1"); 
            RequiresCanRead(arg2, "arg2");
            RequiresCanRead(arg3, "arg3"); 
            return new Block4(arg0, arg1, arg2, arg3); 
        }
 
        /// 
        /// Creates a  that contains five expressions and has no variables.
        /// 
        /// The first expression in the block. 
        /// The second expression in the block.
        /// The third expression in the block. 
        /// The fourth expression in the block. 
        /// The fifth expression in the block.
        /// The created . 
        public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) {
            RequiresCanRead(arg0, "arg0");
            RequiresCanRead(arg1, "arg1");
            RequiresCanRead(arg2, "arg2"); 
            RequiresCanRead(arg3, "arg3");
            RequiresCanRead(arg4, "arg4"); 
 
            return new Block5(arg0, arg1, arg2, arg3, arg4);
        } 

        /// 
        /// Creates a  that contains the given expressions and has no variables.
        ///  
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(params Expression[] expressions) { 
            ContractUtils.RequiresNotNull(expressions, "expressions");
 
            switch (expressions.Length) {
                case 2: return Block(expressions[0], expressions[1]);
                case 3: return Block(expressions[0], expressions[1], expressions[2]);
                case 4: return Block(expressions[0], expressions[1], expressions[2], expressions[3]); 
                case 5: return Block(expressions[0], expressions[1], expressions[2], expressions[3], expressions[4]);
                default: 
                    ContractUtils.RequiresNotEmpty(expressions, "expressions"); 
                    RequiresCanRead(expressions, "expressions");
                    return new BlockN(expressions.Copy()); 
            }
        }

        ///  
        /// Creates a  that contains the given expressions and has no variables.
        ///  
        /// The expressions in the block. 
        /// The created .
        public static BlockExpression Block(IEnumerable expressions) { 
            return Block(EmptyReadOnlyCollection.Instance, expressions);
        }

        ///  
        /// Creates a  that contains the given expressions, has no variables and has specific result type.
        ///  
        /// The result type of the block. 
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(Type type, params Expression[] expressions) {
            ContractUtils.RequiresNotNull(expressions, "expressions");
            return Block(type, (IEnumerable)expressions);
        } 

        ///  
        /// Creates a  that contains the given expressions, has no variables and has specific result type. 
        /// 
        /// The result type of the block. 
        /// The expressions in the block.
        /// The created .
        public static BlockExpression Block(Type type, IEnumerable expressions) {
            return Block(type, EmptyReadOnlyCollection.Instance, expressions); 
        }
 
        ///  
        /// Creates a  that contains the given variables and expressions.
        ///  
        /// The variables in the block.
        /// The expressions in the block.
        /// The created .
        public static BlockExpression Block(IEnumerable variables, params Expression[] expressions) { 
            return Block(variables, (IEnumerable)expressions);
        } 
 
        /// 
        /// Creates a  that contains the given variables and expressions. 
        /// 
        /// The result type of the block.
        /// The variables in the block.
        /// The expressions in the block. 
        /// The created .
        public static BlockExpression Block(Type type, IEnumerable variables, params Expression[] expressions) { 
            return Block(type, variables, (IEnumerable)expressions); 
        }
 
        /// 
        /// Creates a  that contains the given variables and expressions.
        /// 
        /// The variables in the block. 
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(IEnumerable variables, IEnumerable expressions) { 
            ContractUtils.RequiresNotNull(expressions, "expressions");
            var expressionList = expressions.ToReadOnly(); 
            ContractUtils.RequiresNotEmpty(expressionList, "expressions");
            RequiresCanRead(expressionList, "expressions");

            return Block(expressionList.Last().Type, variables, expressionList); 
        }
 
        ///  
        /// Creates a  that contains the given variables and expressions.
        ///  
        /// The result type of the block.
        /// The variables in the block.
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(Type type, IEnumerable variables, IEnumerable expressions) {
            ContractUtils.RequiresNotNull(type, "type"); 
            ContractUtils.RequiresNotNull(expressions, "expressions"); 

            var expressionList = expressions.ToReadOnly(); 
            var variableList = variables.ToReadOnly();

            ContractUtils.RequiresNotEmpty(expressionList, "expressions");
            RequiresCanRead(expressionList, "expressions"); 
            ValidateVariables(variableList, "variables");
 
            Expression last = expressionList.Last(); 
            if (type != typeof(void)) {
                if (!TypeUtils.AreReferenceAssignable(type, last.Type)) { 
                    throw Error.ArgumentTypesMustMatch();
                }
            }
 
            if (!TypeUtils.AreEquivalent(type, last.Type)) {
                return new ScopeWithType(variableList, expressionList, type); 
            } else { 
                if (expressionList.Count == 1) {
                    return new Scope1(variableList, expressionList[0]); 
                } else {
                    return new ScopeN(variableList, expressionList);
                }
            } 
        }
 
        // Checks that all variables are non-null, not byref, and unique. 
        internal static void ValidateVariables(ReadOnlyCollection varList, string collectionName) {
            if (varList.Count == 0) { 
                return;
            }

            int count = varList.Count; 
            var set = new Set(count);
            for (int i = 0; i < count; i++) { 
                ParameterExpression v = varList[i]; 
                if (v == null) {
                    throw new ArgumentNullException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}[{1}]", collectionName, set.Count)); 
                }
                if (v.IsByRef) {
                    throw Error.VariableMustNotBeByRef(v, v.Type);
                } 
                if (set.Contains(v)) {
                    throw Error.DuplicateVariable(v); 
                } 
                set.Add(v);
            } 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Diagnostics;
using System.Dynamic.Utils; 
using System.Threading;

#if SILVERLIGHT
using System.Core; 
#endif
 
namespace System.Linq.Expressions { 
    /// 
    /// Represents a block that contains a sequence of expressions where variables can be defined. 
    /// 
#if !SILVERLIGHT
    [DebuggerTypeProxy(typeof(Expression.BlockExpressionProxy))]
#endif 
    public class BlockExpression : Expression {
        ///  
        /// Gets the expressions in this block. 
        /// 
        public ReadOnlyCollection Expressions { 
            get { return GetOrMakeExpressions(); }
        }

        ///  
        /// Gets the variables defined in this block.
        ///  
        public ReadOnlyCollection Variables { 
            get {
                return GetOrMakeVariables(); 
            }
        }

        ///  
        /// Gets the last expression in this block.
        ///  
        public Expression Result { 
            get {
                Debug.Assert(ExpressionCount > 0); 
                return GetExpression(ExpressionCount - 1);
            }
        }
 
        internal BlockExpression() {
        } 
 
        /// 
        /// Dispatches to the specific visit method for this node type. 
        /// 
        protected internal override Expression Accept(ExpressionVisitor visitor) {
            return visitor.VisitBlock(this);
        } 

        ///  
        /// Returns the node type of this Expression. Extension nodes should return 
        /// ExpressionType.Extension when overriding this method.
        ///  
        /// The  of the expression.
        public sealed override ExpressionType NodeType {
            get { return ExpressionType.Block; }
        } 

        ///  
        /// Gets the static type of the expression that this  represents. 
        /// 
        /// The  that represents the static type of the expression. 
        public override Type Type {
            get { return GetExpression(ExpressionCount - 1).Type; }
        }
 
        /// 
        /// Creates a new expression that is like this one, but using the 
        /// supplied children. If all of the children are the same, it will 
        /// return this expression.
        ///  
        /// The  property of the result.
        /// The  property of the result.
        /// This expression if no children changed, or an expression with the updated children.
        public BlockExpression Update(IEnumerable variables, IEnumerable expressions) { 
            if (variables == Variables && expressions == Expressions) {
                return this; 
            } 

            return Expression.Block(Type, variables, expressions); 
        }

        internal virtual Expression GetExpression(int index) {
            throw ContractUtils.Unreachable; 
        }
 
        internal virtual int ExpressionCount { 
            get {
                throw ContractUtils.Unreachable; 
            }
        }

        internal virtual ReadOnlyCollection GetOrMakeExpressions() { 
            throw ContractUtils.Unreachable;
        } 
 
        internal virtual ParameterExpression GetVariable(int index) {
            throw ContractUtils.Unreachable; 
        }

        internal virtual int VariableCount {
            get { 
                return 0;
            } 
        } 

        internal virtual ReadOnlyCollection GetOrMakeVariables() { 
            return EmptyReadOnlyCollection.Instance;
        }

        ///  
        /// Makes a copy of this node replacing the parameters/args with the provided values.  The
        /// shape of the parameters/args needs to match the shape of the current block - in other 
        /// words there should be the same # of parameters and args. 
        ///
        /// parameters can be null in which case the existing parameters are used. 
        ///
        /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized
        /// subclass of BlockExpression which is being used.
        ///  
        internal virtual BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            throw ContractUtils.Unreachable; 
        } 

        ///  
        /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
        ///
        /// This is similar to the ReturnReadOnly which only takes a single argument. This version
        /// supports nodes which hold onto 5 Expressions and puts all of the arguments into the 
        /// ReadOnlyCollection.
        /// 
        /// Ultimately this means if we create the readonly collection we will be slightly more wasteful as we'll 
        /// have a readonly collection + some fields in the type.  The DLR internally avoids accessing anything
        /// which would force the readonly collection to be created. 
        ///
        /// This is used by BlockExpression5 and MethodCallExpression5.
        /// 
        internal static ReadOnlyCollection ReturnReadOnlyExpressions(BlockExpression provider, ref object collection) { 
            Expression tObj = collection as Expression;
            if (tObj != null) { 
                // otherwise make sure only one readonly collection ever gets exposed 
                Interlocked.CompareExchange(
                    ref collection, 
                    new ReadOnlyCollection(new BlockExpressionList(provider, tObj)),
                    tObj
                );
            } 

            // and return what is not guaranteed to be a readonly collection 
            return (ReadOnlyCollection)collection; 
        }
    } 

    #region Specialized Subclasses

    internal sealed class Block2 : BlockExpression { 
        private object _arg0;                   // storage for the 1st argument or a readonly collection.  See IArgumentProvider
        private readonly Expression _arg1;      // storage for the 2nd argument. 
 
        internal Block2(Expression arg0, Expression arg1) {
            _arg0 = arg0; 
            _arg1 = arg1;
        }

        internal override Expression GetExpression(int index) { 
            switch (index) {
                case 0: return ReturnObject(_arg0); 
                case 1: return _arg1; 
                default: throw new InvalidOperationException();
            } 
        }

        internal override int ExpressionCount {
            get { 
                return 2;
            } 
        } 

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnlyExpressions(this, ref _arg0);
        }

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) { 
            Debug.Assert(args.Length == 2);
            Debug.Assert(variables == null || variables.Count == 0); 
 
            return new Block2(args[0], args[1]);
        } 
    }

    internal sealed class Block3 : BlockExpression {
        private object _arg0;                       // storage for the 1st argument or a readonly collection.  See IArgumentProvider 
        private readonly Expression _arg1, _arg2;   // storage for the 2nd and 3rd arguments.
 
        internal Block3(Expression arg0, Expression arg1, Expression arg2) { 
            _arg0 = arg0;
            _arg1 = arg1; 
            _arg2 = arg2;
        }

        internal override Expression GetExpression(int index) { 
            switch (index) {
                case 0: return ReturnObject(_arg0); 
                case 1: return _arg1; 
                case 2: return _arg2;
                default: throw new InvalidOperationException(); 
            }
        }

        internal override int ExpressionCount { 
            get {
                return 3; 
            } 
        }
 
        internal override ReadOnlyCollection GetOrMakeExpressions() {
            return ReturnReadOnlyExpressions(this, ref _arg0);
        }
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == 3); 
            Debug.Assert(variables == null || variables.Count == 0); 

            return new Block3(args[0], args[1], args[2]); 
        }
    }

    internal sealed class Block4 : BlockExpression { 
        private object _arg0;                               // storage for the 1st argument or a readonly collection.  See IArgumentProvider
        private readonly Expression _arg1, _arg2, _arg3;    // storarg for the 2nd, 3rd, and 4th arguments. 
 
        internal Block4(Expression arg0, Expression arg1, Expression arg2, Expression arg3) {
            _arg0 = arg0; 
            _arg1 = arg1;
            _arg2 = arg2;
            _arg3 = arg3;
        } 

        internal override Expression GetExpression(int index) { 
            switch (index) { 
                case 0: return ReturnObject(_arg0);
                case 1: return _arg1; 
                case 2: return _arg2;
                case 3: return _arg3;
                default: throw new InvalidOperationException();
            } 
        }
 
        internal override int ExpressionCount { 
            get {
                return 4; 
            }
        }

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnlyExpressions(this, ref _arg0);
        } 
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == 4); 
            Debug.Assert(variables == null || variables.Count == 0);

            return new Block4(args[0], args[1], args[2], args[3]);
        } 
    }
 
    internal sealed class Block5 : BlockExpression { 
        private object _arg0;                                       // storage for the 1st argument or a readonly collection.  See IArgumentProvider
        private readonly Expression _arg1, _arg2, _arg3, _arg4;     // storage for the 2nd - 5th args. 

        internal Block5(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) {
            _arg0 = arg0;
            _arg1 = arg1; 
            _arg2 = arg2;
            _arg3 = arg3; 
            _arg4 = arg4; 
        }
 
        internal override Expression GetExpression(int index) {
            switch (index) {
                case 0: return ReturnObject(_arg0);
                case 1: return _arg1; 
                case 2: return _arg2;
                case 3: return _arg3; 
                case 4: return _arg4; 
                default: throw new InvalidOperationException();
            } 
        }

        internal override int ExpressionCount {
            get { 
                return 5;
            } 
        } 

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnlyExpressions(this, ref _arg0);
        }

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) { 
            Debug.Assert(args.Length == 5);
            Debug.Assert(variables == null || variables.Count == 0); 
 
            return new Block5(args[0], args[1], args[2], args[3], args[4]);
        } 
    }

    internal class BlockN : BlockExpression {
        private IList _expressions;         // either the original IList or a ReadOnlyCollection if the user has accessed it. 

        internal BlockN(IList expressions) { 
            Debug.Assert(expressions.Count != 0); 

            _expressions = expressions; 
        }

        internal override Expression GetExpression(int index) {
            Debug.Assert(index >= 0 && index < _expressions.Count); 

            return _expressions[index]; 
        } 

        internal override int ExpressionCount { 
            get {
                return _expressions.Count;
            }
        } 

        internal override ReadOnlyCollection GetOrMakeExpressions() { 
            return ReturnReadOnly(ref _expressions); 
        }
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(variables == null || variables.Count == 0);

            return new BlockN(args); 
        }
    } 
 
    internal class ScopeExpression : BlockExpression {
        private IList _variables;      // list of variables or ReadOnlyCollection if the user has accessed the readonly collection 

        internal ScopeExpression(IList variables) {
            _variables = variables;
        } 

        internal override int VariableCount { 
            get { 
                return _variables.Count;
            } 
        }

        internal override ParameterExpression GetVariable(int index) {
            return _variables[index]; 
        }
 
        internal override ReadOnlyCollection GetOrMakeVariables() { 
            return ReturnReadOnly(ref _variables);
        } 

        protected IList VariablesList {
            get {
                return _variables; 
            }
        } 
 
        // Used for rewrite of the nodes to either reuse existing set of variables if not rewritten.
        internal IList ReuseOrValidateVariables(ReadOnlyCollection variables) { 
            if (variables != null && variables != VariablesList) {
                // Need to validate the new variables (uniqueness, not byref)
                ValidateVariables(variables, "variables");
                return variables; 
            } else {
                return VariablesList; 
            } 
        }
    } 

    internal sealed class Scope1 : ScopeExpression {
        private object _body;
 
        internal Scope1(IList variables, Expression body)
            : base(variables) { 
            _body = body; 
        }
 
        internal override Expression GetExpression(int index) {
            switch (index) {
                case 0: return ReturnObject(_body);
                default: throw new InvalidOperationException(); 
            }
        } 
 
        internal override int ExpressionCount {
            get { 
                return 1;
            }
        }
 
        internal override ReadOnlyCollection GetOrMakeExpressions() {
            return ReturnReadOnlyExpressions(this, ref _body); 
        } 

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) { 
            Debug.Assert(args.Length == 1);
            Debug.Assert(variables == null || variables.Count == VariableCount);

            return new Scope1(ReuseOrValidateVariables(variables), args[0]); 
        }
    } 
 
    internal class ScopeN : ScopeExpression {
        private IList _body; 

        internal ScopeN(IList variables, IList body)
            : base(variables) {
            _body = body; 
        }
 
        internal override Expression GetExpression(int index) { 
            return _body[index];
        } 

        internal override int ExpressionCount {
            get {
                return _body.Count; 
            }
        } 
 
        internal override ReadOnlyCollection GetOrMakeExpressions() {
            return ReturnReadOnly(ref _body); 
        }

        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == ExpressionCount); 
            Debug.Assert(variables == null || variables.Count == VariableCount);
 
            return new ScopeN(ReuseOrValidateVariables(variables), args); 
        }
    } 

    internal class ScopeWithType : ScopeN {
        private readonly Type _type;
 
        internal ScopeWithType(IList variables, IList expressions, Type type)
            : base(variables, expressions) { 
            _type = type; 
        }
 
        public sealed override Type Type {
            get { return _type; }
        }
 
        internal override BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args) {
            Debug.Assert(args.Length == ExpressionCount); 
            Debug.Assert(variables == null || variables.Count == VariableCount); 

            return new ScopeWithType(ReuseOrValidateVariables(variables), args, _type); 
        }
    }

    #endregion 

    #region Block List Classes 
 
    /// 
    /// Provides a wrapper around an IArgumentProvider which exposes the argument providers 
    /// members out as an IList of Expression.  This is used to avoid allocating an array
    /// which needs to be stored inside of a ReadOnlyCollection.  Instead this type has
    /// the same amount of overhead as an array without duplicating the storage of the
    /// elements.  This ensures that internally we can avoid creating and copying arrays 
    /// while users of the Expression trees also don't pay a size penalty for this internal
    /// optimization.  See IArgumentProvider for more general information on the Expression 
    /// tree optimizations being used here. 
    /// 
    internal class BlockExpressionList : IList { 
        private readonly BlockExpression _block;
        private readonly Expression _arg0;

        internal BlockExpressionList(BlockExpression provider, Expression arg0) { 
            _block = provider;
            _arg0 = arg0; 
        } 

        #region IList Members 

        public int IndexOf(Expression item) {
            if (_arg0 == item) {
                return 0; 
            }
 
            for (int i = 1; i < _block.ExpressionCount; i++) { 
                if (_block.GetExpression(i) == item) {
                    return i; 
                }
            }

            return -1; 
        }
 
        public void Insert(int index, Expression item) { 
            throw ContractUtils.Unreachable;
        } 

        public void RemoveAt(int index) {
            throw ContractUtils.Unreachable;
        } 

        public Expression this[int index] { 
            get { 
                if (index == 0) {
                    return _arg0; 
                }

                return _block.GetExpression(index);
            } 
            set {
                throw ContractUtils.Unreachable; 
            } 
        }
 
        #endregion

        #region ICollection Members
 
        public void Add(Expression item) {
            throw ContractUtils.Unreachable; 
        } 

        public void Clear() { 
            throw ContractUtils.Unreachable;
        }

        public bool Contains(Expression item) { 
            return IndexOf(item) != -1;
        } 
 
        public void CopyTo(Expression[] array, int arrayIndex) {
            array[arrayIndex++] = _arg0; 
            for (int i = 1; i < _block.ExpressionCount; i++) {
                array[arrayIndex++] = _block.GetExpression(i);
            }
        } 

        public int Count { 
            get { return _block.ExpressionCount; } 
        }
 
        public bool IsReadOnly {
            get { return true; }
        }
 
        public bool Remove(Expression item) {
            throw ContractUtils.Unreachable; 
        } 

        #endregion 

        #region IEnumerable Members

        public IEnumerator GetEnumerator() { 
            yield return _arg0;
 
            for (int i = 1; i < _block.ExpressionCount; i++) { 
                yield return _block.GetExpression(i);
            } 
        }

        #endregion
 
        #region IEnumerable Members
 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 
            yield return _arg0;
 
            for (int i = 1; i < _block.ExpressionCount; i++) {
                yield return _block.GetExpression(i);
            }
        } 

        #endregion 
    } 

    #endregion 

    public partial class Expression {

        ///  
        /// Creates a  that contains two expressions and has no variables.
        ///  
        /// The first expression in the block. 
        /// The second expression in the block.
        /// The created . 
        public static BlockExpression Block(Expression arg0, Expression arg1) {
            RequiresCanRead(arg0, "arg0");
            RequiresCanRead(arg1, "arg1");
 
            return new Block2(arg0, arg1);
        } 
        ///  
        /// Creates a  that contains three expressions and has no variables.
        ///  
        /// The first expression in the block.
        /// The second expression in the block.
        /// The third expression in the block.
        /// The created . 
        public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2) {
            RequiresCanRead(arg0, "arg0"); 
            RequiresCanRead(arg1, "arg1"); 
            RequiresCanRead(arg2, "arg2");
            return new Block3(arg0, arg1, arg2); 
        }

        /// 
        /// Creates a  that contains four expressions and has no variables. 
        /// 
        /// The first expression in the block. 
        /// The second expression in the block. 
        /// The third expression in the block.
        /// The fourth expression in the block. 
        /// The created .
        public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3) {
            RequiresCanRead(arg0, "arg0");
            RequiresCanRead(arg1, "arg1"); 
            RequiresCanRead(arg2, "arg2");
            RequiresCanRead(arg3, "arg3"); 
            return new Block4(arg0, arg1, arg2, arg3); 
        }
 
        /// 
        /// Creates a  that contains five expressions and has no variables.
        /// 
        /// The first expression in the block. 
        /// The second expression in the block.
        /// The third expression in the block. 
        /// The fourth expression in the block. 
        /// The fifth expression in the block.
        /// The created . 
        public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) {
            RequiresCanRead(arg0, "arg0");
            RequiresCanRead(arg1, "arg1");
            RequiresCanRead(arg2, "arg2"); 
            RequiresCanRead(arg3, "arg3");
            RequiresCanRead(arg4, "arg4"); 
 
            return new Block5(arg0, arg1, arg2, arg3, arg4);
        } 

        /// 
        /// Creates a  that contains the given expressions and has no variables.
        ///  
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(params Expression[] expressions) { 
            ContractUtils.RequiresNotNull(expressions, "expressions");
 
            switch (expressions.Length) {
                case 2: return Block(expressions[0], expressions[1]);
                case 3: return Block(expressions[0], expressions[1], expressions[2]);
                case 4: return Block(expressions[0], expressions[1], expressions[2], expressions[3]); 
                case 5: return Block(expressions[0], expressions[1], expressions[2], expressions[3], expressions[4]);
                default: 
                    ContractUtils.RequiresNotEmpty(expressions, "expressions"); 
                    RequiresCanRead(expressions, "expressions");
                    return new BlockN(expressions.Copy()); 
            }
        }

        ///  
        /// Creates a  that contains the given expressions and has no variables.
        ///  
        /// The expressions in the block. 
        /// The created .
        public static BlockExpression Block(IEnumerable expressions) { 
            return Block(EmptyReadOnlyCollection.Instance, expressions);
        }

        ///  
        /// Creates a  that contains the given expressions, has no variables and has specific result type.
        ///  
        /// The result type of the block. 
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(Type type, params Expression[] expressions) {
            ContractUtils.RequiresNotNull(expressions, "expressions");
            return Block(type, (IEnumerable)expressions);
        } 

        ///  
        /// Creates a  that contains the given expressions, has no variables and has specific result type. 
        /// 
        /// The result type of the block. 
        /// The expressions in the block.
        /// The created .
        public static BlockExpression Block(Type type, IEnumerable expressions) {
            return Block(type, EmptyReadOnlyCollection.Instance, expressions); 
        }
 
        ///  
        /// Creates a  that contains the given variables and expressions.
        ///  
        /// The variables in the block.
        /// The expressions in the block.
        /// The created .
        public static BlockExpression Block(IEnumerable variables, params Expression[] expressions) { 
            return Block(variables, (IEnumerable)expressions);
        } 
 
        /// 
        /// Creates a  that contains the given variables and expressions. 
        /// 
        /// The result type of the block.
        /// The variables in the block.
        /// The expressions in the block. 
        /// The created .
        public static BlockExpression Block(Type type, IEnumerable variables, params Expression[] expressions) { 
            return Block(type, variables, (IEnumerable)expressions); 
        }
 
        /// 
        /// Creates a  that contains the given variables and expressions.
        /// 
        /// The variables in the block. 
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(IEnumerable variables, IEnumerable expressions) { 
            ContractUtils.RequiresNotNull(expressions, "expressions");
            var expressionList = expressions.ToReadOnly(); 
            ContractUtils.RequiresNotEmpty(expressionList, "expressions");
            RequiresCanRead(expressionList, "expressions");

            return Block(expressionList.Last().Type, variables, expressionList); 
        }
 
        ///  
        /// Creates a  that contains the given variables and expressions.
        ///  
        /// The result type of the block.
        /// The variables in the block.
        /// The expressions in the block.
        /// The created . 
        public static BlockExpression Block(Type type, IEnumerable variables, IEnumerable expressions) {
            ContractUtils.RequiresNotNull(type, "type"); 
            ContractUtils.RequiresNotNull(expressions, "expressions"); 

            var expressionList = expressions.ToReadOnly(); 
            var variableList = variables.ToReadOnly();

            ContractUtils.RequiresNotEmpty(expressionList, "expressions");
            RequiresCanRead(expressionList, "expressions"); 
            ValidateVariables(variableList, "variables");
 
            Expression last = expressionList.Last(); 
            if (type != typeof(void)) {
                if (!TypeUtils.AreReferenceAssignable(type, last.Type)) { 
                    throw Error.ArgumentTypesMustMatch();
                }
            }
 
            if (!TypeUtils.AreEquivalent(type, last.Type)) {
                return new ScopeWithType(variableList, expressionList, type); 
            } else { 
                if (expressionList.Count == 1) {
                    return new Scope1(variableList, expressionList[0]); 
                } else {
                    return new ScopeN(variableList, expressionList);
                }
            } 
        }
 
        // Checks that all variables are non-null, not byref, and unique. 
        internal static void ValidateVariables(ReadOnlyCollection varList, string collectionName) {
            if (varList.Count == 0) { 
                return;
            }

            int count = varList.Count; 
            var set = new Set(count);
            for (int i = 0; i < count; i++) { 
                ParameterExpression v = varList[i]; 
                if (v == null) {
                    throw new ArgumentNullException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}[{1}]", collectionName, set.Count)); 
                }
                if (v.IsByRef) {
                    throw Error.VariableMustNotBeByRef(v, v.Type);
                } 
                if (set.Contains(v)) {
                    throw Error.DuplicateVariable(v); 
                } 
                set.Add(v);
            } 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK