#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