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.
359 lines
16 KiB
359 lines
16 KiB
5 years ago
|
#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
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Represents a constructor call.
|
||
|
/// </summary>
|
||
|
[DebuggerTypeProxy(typeof(NewExpressionProxy))]
|
||
|
public class NewExpression : Expression, IArgumentProvider
|
||
|
{
|
||
|
private readonly ConstructorInfo _constructor;
|
||
|
private IList<Expression> _arguments;
|
||
|
private readonly ReadOnlyCollection<MemberInfo> _members;
|
||
|
|
||
|
internal NewExpression(ConstructorInfo constructor, IList<Expression> arguments, ReadOnlyCollection<MemberInfo> members)
|
||
|
{
|
||
|
_constructor = constructor;
|
||
|
_arguments = arguments;
|
||
|
_members = members;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
|
||
|
/// </summary>
|
||
|
/// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
|
||
|
public override Type Type
|
||
|
{
|
||
|
get { return _constructor.DeclaringType; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
|
||
|
/// </summary>
|
||
|
/// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
|
||
|
public sealed override ExpressionType NodeType
|
||
|
{
|
||
|
get { return ExpressionType.New; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the called constructor.
|
||
|
/// </summary>
|
||
|
public ConstructorInfo Constructor
|
||
|
{
|
||
|
get { return _constructor; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the arguments to the constructor.
|
||
|
/// </summary>
|
||
|
public ReadOnlyCollection<Expression> Arguments
|
||
|
{
|
||
|
get { return ReturnReadOnly(ref _arguments); }
|
||
|
}
|
||
|
|
||
|
public Expression GetArgument(int index)
|
||
|
{
|
||
|
return _arguments[index];
|
||
|
}
|
||
|
|
||
|
public int ArgumentCount
|
||
|
{
|
||
|
get { return _arguments.Count; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the members that can retrieve the values of the fields that were initialized with constructor arguments.
|
||
|
/// </summary>
|
||
|
public ReadOnlyCollection<MemberInfo> Members
|
||
|
{
|
||
|
get { return _members; }
|
||
|
}
|
||
|
|
||
|
protected internal override Expression Accept(ExpressionVisitor visitor)
|
||
|
{
|
||
|
return visitor.VisitNew(this);
|
||
|
}
|
||
|
|
||
|
/// <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="arguments">The <see cref="Arguments" /> property of the result.</param>
|
||
|
/// <returns>This expression if no children changed, or an expression with the updated children.</returns>
|
||
|
public NewExpression Update(IEnumerable<Expression> 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<Expression> arguments, ReadOnlyCollection<MemberInfo> members)
|
||
|
: base(null, arguments, members)
|
||
|
{
|
||
|
_valueType = type;
|
||
|
}
|
||
|
|
||
|
public sealed override Type Type
|
||
|
{
|
||
|
get { return _valueType; }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public partial class Expression
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor that takes no arguments.
|
||
|
/// </summary>
|
||
|
/// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
|
||
|
/// <returns>A <see cref="NewExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/> property set to the specified value.</returns>
|
||
|
public static NewExpression New(ConstructorInfo constructor)
|
||
|
{
|
||
|
return New(constructor, (IEnumerable<Expression>)null);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor that takes no arguments.
|
||
|
/// </summary>
|
||
|
/// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
|
||
|
/// <param name="arguments">An array of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
|
||
|
/// <returns>A <see cref="NewExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/> and <see cref="P:Arguments"/> properties set to the specified value.</returns>
|
||
|
public static NewExpression New(ConstructorInfo constructor, params Expression[] arguments)
|
||
|
{
|
||
|
return New(constructor, (IEnumerable<Expression>)arguments);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor that takes no arguments.
|
||
|
/// </summary>
|
||
|
/// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
|
||
|
/// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
|
||
|
/// <returns>A <see cref="NewExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/> and <see cref="P:Arguments"/> properties set to the specified value.</returns>
|
||
|
public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> 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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor with the specified arguments. The members that access the constructor initialized fields are specified.
|
||
|
/// </summary>
|
||
|
/// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
|
||
|
/// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
|
||
|
/// <param name="members">An <see cref="IEnumerable{T}"/> of <see cref="MemberInfo"/> objects to use to populate the Members collection.</param>
|
||
|
/// <returns>A <see cref="NewExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/>, <see cref="P:Arguments"/> and <see cref="P:Members"/> properties set to the specified value.</returns>
|
||
|
public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> arguments, IEnumerable<MemberInfo> 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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor with the specified arguments. The members that access the constructor initialized fields are specified.
|
||
|
/// </summary>
|
||
|
/// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
|
||
|
/// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
|
||
|
/// <param name="members">An Array of <see cref="MemberInfo"/> objects to use to populate the Members collection.</param>
|
||
|
/// <returns>A <see cref="NewExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/>, <see cref="P:Arguments"/> and <see cref="P:Members"/> properties set to the specified value.</returns>
|
||
|
public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> arguments, params MemberInfo[] members)
|
||
|
{
|
||
|
return New(constructor, arguments, (IEnumerable<MemberInfo>)members);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a <see cref="NewExpression"/> that represents calling the parameterless constructor of the specified type.
|
||
|
/// </summary>
|
||
|
/// <param name="type">A <see cref="Type"/> that has a constructor that takes no arguments. </param>
|
||
|
/// <returns>A <see cref="NewExpression"/> that has the <see cref="NodeType"/> property equal to New and the Constructor property set to the ConstructorInfo that represents the parameterless constructor of the specified type.</returns>
|
||
|
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<Expression>.Instance, null);
|
||
|
}
|
||
|
|
||
|
private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection<Expression> arguments, ref ReadOnlyCollection<MemberInfo> 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<Expression>(newArguments);
|
||
|
}
|
||
|
if (newMembers != null)
|
||
|
{
|
||
|
members = new TrueReadOnlyCollection<MemberInfo>(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
|