#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.Reflection;
using System.Runtime.CompilerServices;
using LinqInternal.Collections;
using LinqInternal.Core;
namespace System.Linq.Expressions.Reimplement
{
///
/// Represents a constructor call.
///
[DebuggerTypeProxy(typeof(NewExpressionProxy))]
public class NewExpression : Expression, IArgumentProvider
{
private readonly ConstructorInfo _constructor;
private IList _arguments;
private readonly ReadOnlyCollection _members;
internal NewExpression(ConstructorInfo constructor, IList arguments, ReadOnlyCollection members)
{
_constructor = constructor;
_arguments = arguments;
_members = members;
}
///
/// Gets the static type of the expression that this represents. (Inherited from .)
///
/// The that represents the static type of the expression.
public override Type Type
{
get { return _constructor.DeclaringType; }
}
///
/// Returns the node type of this . (Inherited from .)
///
/// The that represents this expression.
public sealed override ExpressionType NodeType
{
get { return ExpressionType.New; }
}
///
/// Gets the called constructor.
///
public ConstructorInfo Constructor
{
get { return _constructor; }
}
///
/// Gets the arguments to the constructor.
///
public ReadOnlyCollection Arguments
{
get { return ReturnReadOnly(ref _arguments); }
}
public Expression GetArgument(int index)
{
return _arguments[index];
}
public int ArgumentCount
{
get { return _arguments.Count; }
}
///
/// Gets the members that can retrieve the values of the fields that were initialized with constructor arguments.
///
public ReadOnlyCollection Members
{
get { return _members; }
}
protected internal override Expression Accept(ExpressionVisitor visitor)
{
return visitor.VisitNew(this);
}
///
/// 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.
/// This expression if no children changed, or an expression with the updated children.
public NewExpression Update(IEnumerable arguments)
{
if (arguments == Arguments)
{
return this;
}
if (Members != null)
{
return New(Constructor, arguments, Members);
}
return New(Constructor, arguments);
}
}
internal class NewValueTypeExpression : NewExpression
{
private readonly Type _valueType;
internal NewValueTypeExpression(Type type, ReadOnlyCollection arguments, ReadOnlyCollection members)
: base(null, arguments, members)
{
_valueType = type;
}
public sealed override Type Type
{
get { return _valueType; }
}
}
public partial class Expression
{
///
/// Creates a new that represents calling the specified constructor that takes no arguments.
///
/// The to set the property equal to.
/// A that has the property equal to and the property set to the specified value.
public static NewExpression New(ConstructorInfo constructor)
{
return New(constructor, (IEnumerable)null);
}
///
/// Creates a new that represents calling the specified constructor that takes no arguments.
///
/// The to set the property equal to.
/// An array of objects to use to populate the Arguments collection.
/// A that has the property equal to and the and properties set to the specified value.
public static NewExpression New(ConstructorInfo constructor, params Expression[] arguments)
{
return New(constructor, (IEnumerable)arguments);
}
///
/// Creates a new that represents calling the specified constructor that takes no arguments.
///
/// The to set the property equal to.
/// An of objects to use to populate the Arguments collection.
/// A that has the property equal to and the and properties set to the specified value.
public static NewExpression New(ConstructorInfo constructor, IEnumerable arguments)
{
ContractUtils.RequiresNotNull(constructor, "constructor");
ContractUtils.RequiresNotNull(constructor.DeclaringType, "constructor.DeclaringType");
TypeHelper.ValidateType(constructor.DeclaringType);
var argList = arguments.ToReadOnly();
ValidateArgumentTypes(constructor, ExpressionType.New, ref argList);
return new NewExpression(constructor, argList, null);
}
///
/// Creates a new that represents calling the specified constructor with the specified arguments. The members that access the constructor initialized fields are specified.
///
/// The to set the property equal to.
/// An of objects to use to populate the Arguments collection.
/// An of objects to use to populate the Members collection.
/// A that has the property equal to and the , and properties set to the specified value.
public static NewExpression New(ConstructorInfo constructor, IEnumerable arguments, IEnumerable members)
{
ContractUtils.RequiresNotNull(constructor, "constructor");
var memberList = members.ToReadOnly();
var argList = arguments.ToReadOnly();
ValidateNewArgs(constructor, ref argList, ref memberList);
return new NewExpression(constructor, argList, memberList);
}
///
/// Creates a new that represents calling the specified constructor with the specified arguments. The members that access the constructor initialized fields are specified.
///
/// The to set the property equal to.
/// An of objects to use to populate the Arguments collection.
/// An Array of objects to use to populate the Members collection.
/// A that has the property equal to and the , and properties set to the specified value.
public static NewExpression New(ConstructorInfo constructor, IEnumerable arguments, params MemberInfo[] members)
{
return New(constructor, arguments, (IEnumerable)members);
}
///
/// Creates a that represents calling the parameterless constructor of the specified type.
///
/// A that has a constructor that takes no arguments.
/// A that has the property equal to New and the Constructor property set to the ConstructorInfo that represents the parameterless constructor of the specified type.
public static NewExpression New(Type type)
{
ContractUtils.RequiresNotNull(type, "type");
if (type == typeof(void))
{
throw Error.ArgumentCannotBeOfTypeVoid();
}
if (!type.IsValueType)
{
var constructorInfo = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SingleOrDefault(c => c.GetParameters().Length == 0);
if (constructorInfo == null)
{
throw Error.TypeMissingDefaultConstructor(type);
}
return New(constructorInfo);
}
return new NewValueTypeExpression(type, EmptyCollection.Instance, null);
}
private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection arguments, ref ReadOnlyCollection members)
{
ParameterInfo[] pis;
if ((pis = constructor.GetParameters()).Length > 0)
{
if (arguments.Count != pis.Length)
{
throw Error.IncorrectNumberOfConstructorArguments();
}
if (arguments.Count != members.Count)
{
throw Error.IncorrectNumberOfArgumentsForMembers();
}
Expression[] newArguments = null;
MemberInfo[] newMembers = null;
var n = arguments.Count;
for (var i = 0; i < n; i++)
{
var arg = arguments[i];
RequiresCanRead(arg, "argument");
var member = members[i];
ContractUtils.RequiresNotNull(member, "member");
if (member.DeclaringType != constructor.DeclaringType)
{
throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType.Name);
}
Type memberType;
ValidateAnonymousTypeMember(ref member, out memberType);
if (!TypeHelper.AreReferenceAssignable(memberType, arg.Type))
{
if (!TryQuote(memberType, ref arg))
{
throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType);
}
}
var pi = pis[i];
var pType = pi.ParameterType;
if (pType.IsByRef)
{
pType = pType.GetElementType();
}
if (!TypeHelper.AreReferenceAssignable(pType, arg.Type))
{
if (!TryQuote(pType, ref arg))
{
throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType);
}
}
if (newArguments == null && arg != arguments[i])
{
newArguments = new Expression[n];
for (var j = 0; j < i; j++)
{
newArguments[j] = arguments[j];
}
}
if (newArguments != null)
{
newArguments[i] = arg;
}
if (newMembers == null && member != members[i])
{
newMembers = new MemberInfo[members.Count];
for (var j = 0; j < i; j++)
{
newMembers[j] = members[j];
}
}
if (newMembers != null)
{
newMembers[i] = member;
}
}
if (newArguments != null)
{
arguments = new TrueReadOnlyCollection(newArguments);
}
if (newMembers != null)
{
members = new TrueReadOnlyCollection(newMembers);
}
}
else if (arguments != null && arguments.Count > 0)
{
throw Error.IncorrectNumberOfConstructorArguments();
}
else if (members != null && members.Count > 0)
{
throw Error.IncorrectNumberOfMembersForGivenConstructor();
}
}
private static void ValidateAnonymousTypeMember(ref MemberInfo member, out Type memberType)
{
var field = member as FieldInfo;
if (field != null)
{
if (field.IsStatic)
{
throw Error.ArgumentMustBeInstanceMember();
}
memberType = field.FieldType;
return;
}
var pi = member as PropertyInfo;
if (pi != null)
{
if (!pi.CanRead)
{
throw Error.PropertyDoesNotHaveGetter(pi);
}
if (pi.GetGetMethod().IsStatic)
{
throw Error.ArgumentMustBeInstanceMember();
}
memberType = pi.PropertyType;
return;
}
var method = member as MethodInfo;
if (method != null)
{
if (method.IsStatic)
{
throw Error.ArgumentMustBeInstanceMember();
}
var prop = GetProperty(method);
member = prop;
memberType = prop.PropertyType;
return;
}
throw Error.ArgumentMustBeFieldInfoOrPropertInfoOrMethod();
}
}
}
#endif