Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / NewExpression.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Reflection;
22 using System.Runtime.CompilerServices;
23 //using Microsoft.Scripting.Utils;
24
25 #if !FEATURE_CORE_DLR
26 namespace Microsoft.Scripting.Ast {
27 #else
28 namespace System.Linq.Expressions {
29 #endif
30     /// <summary>
31     /// Represents a constructor call.
32     /// </summary>
33     [DebuggerTypeProxy(typeof(Expression.NewExpressionProxy))]
34     public class NewExpression : Expression, IArgumentProvider {
35         private readonly ConstructorInfo _constructor;
36         private IList<Expression> _arguments;
37         private readonly ReadOnlyCollection<MemberInfo> _members;
38
39         internal NewExpression(ConstructorInfo constructor, IList<Expression> arguments, ReadOnlyCollection<MemberInfo> members) {
40             _constructor = constructor;
41             _arguments = arguments;
42             _members = members;
43         }
44
45         /// <summary>
46         /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
47         /// </summary>
48         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
49         public override Type Type {
50             get { return _constructor.DeclaringType; }
51         }
52
53         /// <summary>
54         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
55         /// </summary>
56         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
57         public sealed override ExpressionType NodeType {
58             get { return ExpressionType.New; }
59         }
60
61         /// <summary>
62         /// Gets the called constructor.
63         /// </summary>
64         public ConstructorInfo Constructor {
65             get { return _constructor; }
66         }
67
68         /// <summary>
69         /// Gets the arguments to the constructor.
70         /// </summary>
71         public ReadOnlyCollection<Expression> Arguments {
72             get { return ReturnReadOnly(ref _arguments); }
73         }
74
75         Expression IArgumentProvider.GetArgument(int index) {
76             return _arguments[index];
77         }
78
79         int IArgumentProvider.ArgumentCount {
80             get {
81                 return _arguments.Count;
82             }
83         }
84
85         /// <summary>
86         /// Gets the members that can retrieve the values of the fields that were initialized with constructor arguments.
87         /// </summary>
88         public ReadOnlyCollection<MemberInfo> Members {
89             get { return _members; }
90         }
91
92         /// <summary>
93         /// Dispatches to the specific visit method for this node type.
94         /// </summary>
95         protected internal override Expression Accept(ExpressionVisitor visitor) {
96             return visitor.VisitNew(this);
97         }
98
99         /// <summary>
100         /// Creates a new expression that is like this one, but using the
101         /// supplied children. If all of the children are the same, it will
102         /// return this expression.
103         /// </summary>
104         /// <param name="arguments">The <see cref="Arguments" /> property of the result.</param>
105         /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
106         public NewExpression Update(IEnumerable<Expression> arguments) {
107             if (arguments == Arguments) {
108                 return this;
109             }
110             if (Members != null) {
111                 return Expression.New(Constructor, arguments, Members);
112             }
113             return Expression.New(Constructor, arguments);
114         }
115     }
116
117     internal class NewValueTypeExpression : NewExpression {
118         private readonly Type _valueType;
119
120         internal NewValueTypeExpression(Type type, ReadOnlyCollection<Expression> arguments, ReadOnlyCollection<MemberInfo> members)
121             : base(null, arguments, members) {
122             _valueType = type;
123         }
124
125         public sealed override Type Type {
126             get { return _valueType; }
127         }
128     }
129
130     public partial class Expression {
131
132         /// <summary>
133         /// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor that takes no arguments. 
134         /// </summary>
135         /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
136         /// <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>
137         public static NewExpression New(ConstructorInfo constructor) {
138             return New(constructor, (IEnumerable<Expression>)null);
139         }
140
141
142         /// <summary>
143         /// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor that takes no arguments. 
144         /// </summary>
145         /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
146         /// <param name="arguments">An array of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
147         /// <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>
148         public static NewExpression New(ConstructorInfo constructor, params Expression[] arguments) {
149             return New(constructor, (IEnumerable<Expression>)arguments);
150         }
151
152
153         /// <summary>
154         /// Creates a new <see cref="NewExpression"/> that represents calling the specified constructor that takes no arguments. 
155         /// </summary>
156         /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
157         /// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
158         /// <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>
159         public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> arguments) {
160             ContractUtils.RequiresNotNull(constructor, "constructor");
161             ContractUtils.RequiresNotNull(constructor.DeclaringType, "constructor.DeclaringType");
162             TypeUtils.ValidateType(constructor.DeclaringType);
163             var argList = arguments.ToReadOnly();
164             ValidateArgumentTypes(constructor, ExpressionType.New, ref argList);
165
166             return new NewExpression(constructor, argList, null);
167         }
168
169
170         /// <summary>
171         /// 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. 
172         /// </summary>
173         /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
174         /// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
175         /// <param name="members">An <see cref="IEnumerable{T}"/> of <see cref="MemberInfo"/> objects to use to populate the Members collection.</param>
176         /// <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>
177         public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> arguments, IEnumerable<MemberInfo> members) {
178             ContractUtils.RequiresNotNull(constructor, "constructor");
179             var memberList = members.ToReadOnly();
180             var argList = arguments.ToReadOnly();
181             ValidateNewArgs(constructor, ref argList, ref memberList);
182             return new NewExpression(constructor, argList, memberList);
183         }
184
185
186         /// <summary>
187         /// 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. 
188         /// </summary>
189         /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
190         /// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
191         /// <param name="members">An Array of <see cref="MemberInfo"/> objects to use to populate the Members collection.</param>
192         /// <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>
193         public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> arguments, params MemberInfo[] members) {
194             return New(constructor, arguments, (IEnumerable<MemberInfo>)members);
195         }
196
197
198         /// <summary>
199         /// Creates a <see cref="NewExpression"/> that represents calling the parameterless constructor of the specified type. 
200         /// </summary>
201         /// <param name="type">A <see cref="Type"/> that has a constructor that takes no arguments. </param>
202         /// <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>
203         public static NewExpression New(Type type) {
204             ContractUtils.RequiresNotNull(type, "type");
205             if (type == typeof(void)) {
206                 throw Error.ArgumentCannotBeOfTypeVoid();
207             }
208             ConstructorInfo ci = null;
209             if (!type.IsValueType) {
210                 ci = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Microsoft.Scripting.Utils.ReflectionUtils.EmptyTypes, null);
211                 if (ci == null) {
212                     throw Error.TypeMissingDefaultConstructor(type);
213                 }
214                 return New(ci);
215             }
216             return new NewValueTypeExpression(type, EmptyReadOnlyCollection<Expression>.Instance, null);
217         }
218
219
220
221         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
222         private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection<Expression> arguments, ref ReadOnlyCollection<MemberInfo> members) {
223             ParameterInfo[] pis;
224             if ((pis = constructor.GetParametersCached()).Length > 0) {
225                 if (arguments.Count != pis.Length) {
226                     throw Error.IncorrectNumberOfConstructorArguments();
227                 }
228                 if (arguments.Count != members.Count) {
229                     throw Error.IncorrectNumberOfArgumentsForMembers();
230                 }
231                 Expression[] newArguments = null;
232                 MemberInfo[] newMembers = null;
233                 for (int i = 0, n = arguments.Count; i < n; i++) {
234                     Expression arg = arguments[i];
235                     RequiresCanRead(arg, "argument");
236                     MemberInfo member = members[i];
237                     ContractUtils.RequiresNotNull(member, "member");
238                     if (!TypeUtils.AreEquivalent(member.DeclaringType, constructor.DeclaringType)) {
239                         throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType.Name);
240                     }
241                     Type memberType;
242                     ValidateAnonymousTypeMember(ref member, out memberType);
243                     if (!TypeUtils.AreReferenceAssignable(memberType, arg.Type)) {
244                         if (!TryQuote(memberType, ref arg)) {
245                             throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType);
246                         }
247                     }
248                     ParameterInfo pi = pis[i];
249                     Type pType = pi.ParameterType;
250                     if (pType.IsByRef) {
251                         pType = pType.GetElementType();
252                     }
253                     if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) {
254                         if (!TryQuote(pType, ref arg)) {
255                             throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType);
256                         }
257                     }
258                     if (newArguments == null && arg != arguments[i]) {
259                         newArguments = new Expression[arguments.Count];
260                         for (int j = 0; j < i; j++) {
261                             newArguments[j] = arguments[j];
262                         }
263                     }
264                     if (newArguments != null) {
265                         newArguments[i] = arg;
266                     }
267
268                     if (newMembers == null && member != members[i]) {
269                         newMembers = new MemberInfo[members.Count];
270                         for (int j = 0; j < i; j++) {
271                             newMembers[j] = members[j];
272                         }
273                     }
274                     if (newMembers != null) {
275                         newMembers[i] = member;
276                     }
277                 }
278                 if (newArguments != null) {
279                     arguments = new TrueReadOnlyCollection<Expression>(newArguments);
280                 }
281                 if (newMembers != null) {
282                     members = new TrueReadOnlyCollection<MemberInfo>(newMembers);
283                 }
284             } else if (arguments != null && arguments.Count > 0) {
285                 throw Error.IncorrectNumberOfConstructorArguments();
286             } else if (members != null && members.Count > 0) {
287                 throw Error.IncorrectNumberOfMembersForGivenConstructor();
288             }
289         }
290
291
292         private static void ValidateAnonymousTypeMember(ref MemberInfo member, out Type memberType) {
293             switch (member.MemberType) {
294                 case MemberTypes.Field:
295                     FieldInfo field = member as FieldInfo;
296                     if (field.IsStatic) {
297                         throw Error.ArgumentMustBeInstanceMember();
298                     }
299                     memberType = field.FieldType;
300                     return;
301                 case MemberTypes.Property:
302                     PropertyInfo pi = member as PropertyInfo;
303                     if (!pi.CanRead) {
304                         throw Error.PropertyDoesNotHaveGetter(pi);
305                     }
306                     if (pi.GetGetMethod().IsStatic) {
307                         throw Error.ArgumentMustBeInstanceMember();
308                     }
309                     memberType = pi.PropertyType;
310                     return;
311                 case MemberTypes.Method:
312                     MethodInfo method = member as MethodInfo;
313                     if (method.IsStatic) {
314                         throw Error.ArgumentMustBeInstanceMember();
315                     }
316                     PropertyInfo prop = GetProperty(method);
317                     member = prop;
318                     memberType = prop.PropertyType;
319                     return;
320                 default:
321                     throw Error.ArgumentMustBeFieldInfoOrPropertInfoOrMethod();
322             }
323             // don't add code here, we've already returned
324         }
325     }
326 }