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
#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 |