You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
927 lines
31 KiB
927 lines
31 KiB
#if NET20 || NET30 || !NET_4_6 |
|
|
|
// Copyright (c) Microsoft. All rights reserved. |
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information. |
|
|
|
using System.Collections.Generic; |
|
using System.Collections.ObjectModel; |
|
using System.Diagnostics; |
|
using System.Dynamic.Utils; |
|
using System.Threading; |
|
using LinqInternal.Collections; |
|
using LinqInternal.Core; |
|
|
|
namespace System.Linq.Expressions.Reimplement |
|
{ |
|
/// <summary> |
|
/// Represents a block that contains a sequence of expressions where variables can be defined. |
|
/// </summary> |
|
[DebuggerTypeProxy(typeof(BlockExpressionProxy))] |
|
public class BlockExpression : Expression |
|
{ |
|
/// <summary> |
|
/// Gets the expressions in this block. |
|
/// </summary> |
|
public ReadOnlyCollection<Expression> Expressions |
|
{ |
|
get { return GetOrMakeExpressions(); } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the variables defined in this block. |
|
/// </summary> |
|
public ReadOnlyCollection<ParameterExpression> Variables |
|
{ |
|
get { return GetOrMakeVariables(); } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the last expression in this block. |
|
/// </summary> |
|
public Expression Result |
|
{ |
|
get |
|
{ |
|
Debug.Assert(ExpressionCount > 0); |
|
return GetExpression(ExpressionCount - 1); |
|
} |
|
} |
|
|
|
internal BlockExpression() |
|
{ |
|
} |
|
|
|
protected internal override Expression Accept(ExpressionVisitor visitor) |
|
{ |
|
return visitor.VisitBlock(this); |
|
} |
|
|
|
/// <summary> |
|
/// Returns the node type of this Expression. Extension nodes should return |
|
/// ExpressionType.Extension when overriding this method. |
|
/// </summary> |
|
/// <returns>The <see cref="ExpressionType"/> of the expression.</returns> |
|
public sealed override ExpressionType NodeType |
|
{ |
|
get { return ExpressionType.Block; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the static type of the expression that this <see cref="Expression" /> represents. |
|
/// </summary> |
|
/// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns> |
|
public override Type Type |
|
{ |
|
get { return GetExpression(ExpressionCount - 1).Type; } |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
/// <param name="variables">The <see cref="Variables" /> property of the result.</param> |
|
/// <param name="expressions">The <see cref="Expressions" /> property of the result.</param> |
|
/// <returns>This expression if no children changed, or an expression with the updated children.</returns> |
|
public BlockExpression Update(IEnumerable<ParameterExpression> variables, IEnumerable<Expression> expressions) |
|
{ |
|
if (variables == Variables && expressions == Expressions) |
|
{ |
|
return this; |
|
} |
|
|
|
return Block(Type, variables, expressions); |
|
} |
|
|
|
internal virtual Expression GetExpression(int index) |
|
{ |
|
throw ContractUtils.Unreachable; |
|
} |
|
|
|
internal virtual int ExpressionCount |
|
{ |
|
get { throw ContractUtils.Unreachable; } |
|
} |
|
|
|
internal virtual ReadOnlyCollection<Expression> GetOrMakeExpressions() |
|
{ |
|
throw ContractUtils.Unreachable; |
|
} |
|
|
|
internal virtual ParameterExpression GetVariable(int index) |
|
{ |
|
throw ContractUtils.Unreachable; |
|
} |
|
|
|
internal virtual int VariableCount |
|
{ |
|
get { return 0; } |
|
} |
|
|
|
internal virtual ReadOnlyCollection<ParameterExpression> GetOrMakeVariables() |
|
{ |
|
return EmptyCollection<ParameterExpression>.Instance; |
|
} |
|
|
|
internal virtual BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) |
|
{ |
|
throw ContractUtils.Unreachable; |
|
} |
|
|
|
internal static ReadOnlyCollection<Expression> ReturnReadOnlyExpressions(BlockExpression provider, ref object collection) |
|
{ |
|
var tObj = collection as Expression; |
|
if (tObj != null) |
|
{ |
|
// otherwise make sure only one readonly collection ever gets exposed |
|
Interlocked.CompareExchange( |
|
ref collection, |
|
new ReadOnlyCollection<Expression>(new BlockExpressionList(provider, tObj)), |
|
tObj |
|
); |
|
} |
|
|
|
// and return what is not guaranteed to be a readonly collection |
|
return (ReadOnlyCollection<Expression>)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<Expression>(_arg0); |
|
|
|
case 1: |
|
return _arg1; |
|
|
|
default: |
|
throw new InvalidOperationException(); |
|
} |
|
} |
|
|
|
internal override int ExpressionCount |
|
{ |
|
get { return 2; } |
|
} |
|
|
|
internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnlyExpressions(this, ref _arg0); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> 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<Expression>(_arg0); |
|
|
|
case 1: |
|
return _arg1; |
|
|
|
case 2: |
|
return _arg2; |
|
|
|
default: |
|
throw new InvalidOperationException(); |
|
} |
|
} |
|
|
|
internal override int ExpressionCount |
|
{ |
|
get { return 3; } |
|
} |
|
|
|
internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnlyExpressions(this, ref _arg0); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> 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<Expression>(_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<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnlyExpressions(this, ref _arg0); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> 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<Expression>(_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<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnlyExpressions(this, ref _arg0); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> 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<Expression> _expressions; // either the original IList<Expression> or a ReadOnlyCollection if the user has accessed it. |
|
|
|
internal BlockN(IList<Expression> 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<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnly(ref _expressions); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) |
|
{ |
|
Debug.Assert(variables == null || variables.Count == 0); |
|
|
|
return new BlockN(args); |
|
} |
|
} |
|
|
|
internal class ScopeExpression : BlockExpression |
|
{ |
|
private IList<ParameterExpression> _variables; // list of variables or ReadOnlyCollection if the user has accessed the readonly collection |
|
|
|
internal ScopeExpression(IList<ParameterExpression> variables) |
|
{ |
|
_variables = variables; |
|
} |
|
|
|
internal override int VariableCount |
|
{ |
|
get { return _variables.Count; } |
|
} |
|
|
|
internal override ParameterExpression GetVariable(int index) |
|
{ |
|
return _variables[index]; |
|
} |
|
|
|
internal override ReadOnlyCollection<ParameterExpression> GetOrMakeVariables() |
|
{ |
|
return ReturnReadOnly(ref _variables); |
|
} |
|
|
|
protected IList<ParameterExpression> VariablesList |
|
{ |
|
get { return _variables; } |
|
} |
|
|
|
// Used for rewrite of the nodes to either reuse existing set of variables if not rewritten. |
|
internal IList<ParameterExpression> ReuseOrValidateVariables(ReadOnlyCollection<ParameterExpression> 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<ParameterExpression> variables, Expression body) |
|
: base(variables) |
|
{ |
|
_body = body; |
|
} |
|
|
|
internal override Expression GetExpression(int index) |
|
{ |
|
switch (index) |
|
{ |
|
case 0: |
|
return ReturnObject<Expression>(_body); |
|
|
|
default: |
|
throw new InvalidOperationException(); |
|
} |
|
} |
|
|
|
internal override int ExpressionCount |
|
{ |
|
get { return 1; } |
|
} |
|
|
|
internal override ReadOnlyCollection<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnlyExpressions(this, ref _body); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> 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<Expression> _body; |
|
|
|
internal ScopeN(IList<ParameterExpression> variables, IList<Expression> 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<Expression> GetOrMakeExpressions() |
|
{ |
|
return ReturnReadOnly(ref _body); |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> 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<ParameterExpression> variables, IList<Expression> expressions, Type type) |
|
: base(variables, expressions) |
|
{ |
|
_type = type; |
|
} |
|
|
|
public sealed override Type Type |
|
{ |
|
get { return _type; } |
|
} |
|
|
|
internal override BlockExpression Rewrite(ReadOnlyCollection<ParameterExpression> variables, Expression[] args) |
|
{ |
|
Debug.Assert(args.Length == ExpressionCount); |
|
Debug.Assert(variables == null || variables.Count == VariableCount); |
|
|
|
return new ScopeWithType(ReuseOrValidateVariables(variables), args, _type); |
|
} |
|
} |
|
|
|
#endregion Specialized Subclasses |
|
|
|
#region Block List Classes |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
internal class BlockExpressionList : IList<Expression> |
|
{ |
|
private readonly BlockExpression _block; |
|
private readonly Expression _arg0; |
|
|
|
internal BlockExpressionList(BlockExpression provider, Expression arg0) |
|
{ |
|
_block = provider; |
|
_arg0 = arg0; |
|
} |
|
|
|
#region IList<Expression> Members |
|
|
|
public int IndexOf(Expression item) |
|
{ |
|
if (_arg0 == item) |
|
{ |
|
return 0; |
|
} |
|
|
|
for (var 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 IList<Expression> Members |
|
|
|
#region ICollection<Expression> 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 (var 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 ICollection<Expression> Members |
|
|
|
#region IEnumerable<Expression> Members |
|
|
|
public IEnumerator<Expression> GetEnumerator() |
|
{ |
|
yield return _arg0; |
|
|
|
for (var i = 1; i < _block.ExpressionCount; i++) |
|
{ |
|
yield return _block.GetExpression(i); |
|
} |
|
} |
|
|
|
#endregion IEnumerable<Expression> Members |
|
|
|
#region IEnumerable Members |
|
|
|
Collections.IEnumerator Collections.IEnumerable.GetEnumerator() |
|
{ |
|
yield return _arg0; |
|
|
|
for (var i = 1; i < _block.ExpressionCount; i++) |
|
{ |
|
yield return _block.GetExpression(i); |
|
} |
|
} |
|
|
|
#endregion IEnumerable Members |
|
} |
|
|
|
#endregion Block List Classes |
|
|
|
public partial class Expression |
|
{ |
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains two expressions and has no variables. |
|
/// </summary> |
|
/// <param name="arg0">The first expression in the block.</param> |
|
/// <param name="arg1">The second expression in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(Expression arg0, Expression arg1) |
|
{ |
|
RequiresCanRead(arg0, "arg0"); |
|
RequiresCanRead(arg1, "arg1"); |
|
|
|
return new Block2(arg0, arg1); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains three expressions and has no variables. |
|
/// </summary> |
|
/// <param name="arg0">The first expression in the block.</param> |
|
/// <param name="arg1">The second expression in the block.</param> |
|
/// <param name="arg2">The third expression in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
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); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains four expressions and has no variables. |
|
/// </summary> |
|
/// <param name="arg0">The first expression in the block.</param> |
|
/// <param name="arg1">The second expression in the block.</param> |
|
/// <param name="arg2">The third expression in the block.</param> |
|
/// <param name="arg3">The fourth expression in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
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); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains five expressions and has no variables. |
|
/// </summary> |
|
/// <param name="arg0">The first expression in the block.</param> |
|
/// <param name="arg1">The second expression in the block.</param> |
|
/// <param name="arg2">The third expression in the block.</param> |
|
/// <param name="arg3">The fourth expression in the block.</param> |
|
/// <param name="arg4">The fifth expression in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
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); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given expressions and has no variables. |
|
/// </summary> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
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()); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given expressions and has no variables. |
|
/// </summary> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(IEnumerable<Expression> expressions) |
|
{ |
|
return Block(EmptyCollection<ParameterExpression>.Instance, expressions); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given expressions, has no variables and has specific result type. |
|
/// </summary> |
|
/// <param name="type">The result type of the block.</param> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(Type type, params Expression[] expressions) |
|
{ |
|
ContractUtils.RequiresNotNull(expressions, "expressions"); |
|
return Block(type, (IEnumerable<Expression>)expressions); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given expressions, has no variables and has specific result type. |
|
/// </summary> |
|
/// <param name="type">The result type of the block.</param> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(Type type, IEnumerable<Expression> expressions) |
|
{ |
|
return Block(type, EmptyCollection<ParameterExpression>.Instance, expressions); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions. |
|
/// </summary> |
|
/// <param name="variables">The variables in the block.</param> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(IEnumerable<ParameterExpression> variables, params Expression[] expressions) |
|
{ |
|
return Block(variables, (IEnumerable<Expression>)expressions); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions. |
|
/// </summary> |
|
/// <param name="type">The result type of the block.</param> |
|
/// <param name="variables">The variables in the block.</param> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(Type type, IEnumerable<ParameterExpression> variables, params Expression[] expressions) |
|
{ |
|
return Block(type, variables, (IEnumerable<Expression>)expressions); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions. |
|
/// </summary> |
|
/// <param name="variables">The variables in the block.</param> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(IEnumerable<ParameterExpression> variables, IEnumerable<Expression> expressions) |
|
{ |
|
ContractUtils.RequiresNotNull(expressions, "expressions"); |
|
var expressionList = expressions.ToReadOnly(); |
|
ContractUtils.RequiresNotEmpty(expressionList, "expressions"); |
|
RequiresCanRead(expressionList, "expressions"); |
|
|
|
return Block(expressionList.Last().Type, variables, expressionList); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions. |
|
/// </summary> |
|
/// <param name="type">The result type of the block.</param> |
|
/// <param name="variables">The variables in the block.</param> |
|
/// <param name="expressions">The expressions in the block.</param> |
|
/// <returns>The created <see cref="BlockExpression"/>.</returns> |
|
public static BlockExpression Block(Type type, IEnumerable<ParameterExpression> variables, IEnumerable<Expression> 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"); |
|
|
|
var last = expressionList.Last(); |
|
if (type != typeof(void)) |
|
{ |
|
if (!TypeHelper.AreReferenceAssignable(type, last.Type)) |
|
{ |
|
throw Error.ArgumentTypesMustMatch(); |
|
} |
|
} |
|
|
|
if (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<ParameterExpression> varList, string collectionName) |
|
{ |
|
if (varList.Count == 0) |
|
{ |
|
return; |
|
} |
|
|
|
var count = varList.Count; |
|
var set = new Set<ParameterExpression>(count); |
|
for (var i = 0; i < count; i++) |
|
{ |
|
var v = varList[i]; |
|
if (v == null) |
|
{ |
|
throw new ArgumentNullException(string.Format(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); |
|
} |
|
} |
|
} |
|
} |
|
|
|
#endif |