#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
{
///
/// Represents a block that contains a sequence of expressions where variables can be defined.
///
[DebuggerTypeProxy(typeof(BlockExpressionProxy))]
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()
{
}
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 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 EmptyCollection.Instance;
}
internal virtual BlockExpression Rewrite(ReadOnlyCollection variables, Expression[] args)
{
throw ContractUtils.Unreachable;
}
internal static ReadOnlyCollection 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(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 Specialized Subclasses
#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 (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 Members
#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 (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 Members
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
yield return _arg0;
for (var i = 1; i < _block.ExpressionCount; i++)
{
yield return _block.GetExpression(i);
}
}
#endregion IEnumerable 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
{
///
/// 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(EmptyCollection.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, EmptyCollection.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");
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 varList, string collectionName)
{
if (varList.Count == 0)
{
return;
}
var count = varList.Count;
var set = new Set(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