[sgen] Reenable gc-altstack test
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / IndexExpression.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 System.Text;
24
25 #if !FEATURE_CORE_DLR
26 namespace Microsoft.Scripting.Ast {
27 #else
28 namespace System.Linq.Expressions {
29 #endif
30     /// <summary>
31     /// Represents indexing a property or array.
32     /// </summary>
33     [DebuggerTypeProxy(typeof(Expression.IndexExpressionProxy))]
34     public sealed class IndexExpression : Expression, IArgumentProvider {
35         private readonly Expression _instance;
36         private readonly PropertyInfo _indexer;
37         private IList<Expression> _arguments;
38
39         internal IndexExpression(
40             Expression instance,
41             PropertyInfo indexer,
42             IList<Expression> arguments) {
43
44             if (indexer == null) {
45                 Debug.Assert(instance != null && instance.Type.IsArray);
46                 Debug.Assert(instance.Type.GetArrayRank() == arguments.Count);
47             }
48
49             _instance = instance;
50             _indexer = indexer;
51             _arguments = arguments;
52         }
53
54         /// <summary>
55         /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
56         /// </summary>
57         /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
58         public sealed override ExpressionType NodeType {
59             get { return ExpressionType.Index; }
60         }
61
62         /// <summary>
63         /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
64         /// </summary>
65         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
66         public sealed override Type Type {
67             get {
68                 if (_indexer != null) {
69                     return _indexer.PropertyType;
70                 }
71                 return _instance.Type.GetElementType();
72             }
73         }
74
75         /// <summary>
76         /// An object to index.
77         /// </summary>
78         public Expression Object {
79             get { return _instance; }
80         }
81
82         /// <summary>
83         /// Gets the <see cref="PropertyInfo"/> for the property if the expression represents an indexed property, returns null otherwise.
84         /// </summary>
85         public PropertyInfo Indexer {
86             get { return _indexer; }
87         }
88
89         /// <summary>
90         /// Gets the arguments to be used to index the property or array.
91         /// </summary>
92         public ReadOnlyCollection<Expression> Arguments {
93             get { return ReturnReadOnly(ref _arguments); }
94         }
95
96         /// <summary>
97         /// Creates a new expression that is like this one, but using the
98         /// supplied children. If all of the children are the same, it will
99         /// return this expression.
100         /// </summary>
101         /// <param name="object">The <see cref="Object" /> property of the result.</param>
102         /// <param name="arguments">The <see cref="Arguments" /> property of the result.</param>
103         /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
104         public IndexExpression Update(Expression @object, IEnumerable<Expression> arguments) {
105             if (@object == Object && arguments == Arguments) {
106                 return this;
107             }
108             return Expression.MakeIndex(@object, Indexer, arguments);
109         }
110
111         Expression IArgumentProvider.GetArgument(int index) {
112             return _arguments[index];
113         }
114
115         int IArgumentProvider.ArgumentCount {
116             get {
117                 return _arguments.Count;
118             }
119         }
120
121         /// <summary>
122         /// Dispatches to the specific visit method for this node type.
123         /// </summary>
124         protected internal override Expression Accept(ExpressionVisitor visitor) {
125             return visitor.VisitIndex(this);
126         }
127
128         internal Expression Rewrite(Expression instance, Expression[] arguments) {
129             Debug.Assert(instance != null);
130             Debug.Assert(arguments == null || arguments.Length == _arguments.Count);
131
132             return Expression.MakeIndex(instance, _indexer, arguments ?? _arguments);
133         }
134     }
135
136     public partial class Expression {
137
138         /// <summary>
139         /// Creates an <see cref="IndexExpression"/> that represents accessing an indexed property in an object.
140         /// </summary>
141         /// <param name="instance">The object to which the property belongs. Should be null if the property is static(shared).</param>
142         /// <param name="indexer">An <see cref="Expression"/> representing the property to index.</param>
143         /// <param name="arguments">An IEnumerable{Expression} contaning the arguments to be used to index the property.</param>
144         /// <returns>The created <see cref="IndexExpression"/>.</returns>
145         public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, IEnumerable<Expression> arguments) {
146             if (indexer != null) {
147                 return Property(instance, indexer, arguments);
148             } else {
149                 return ArrayAccess(instance, arguments);
150             }
151         }
152
153         #region ArrayAccess
154
155         /// <summary>
156         /// Creates an <see cref="IndexExpression"></see> to access an array.
157         /// </summary>
158         /// <param name="array">An expression representing the array to index.</param>
159         /// <param name="indexes">An array containing expressions used to index the array.</param>
160         /// <remarks>The expression representing the array can be obtained by using the MakeMemberAccess method, 
161         /// or through NewArrayBounds or NewArrayInit.</remarks>
162         /// <returns>The created <see cref="IndexExpression"/>.</returns>
163         public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) {
164             return ArrayAccess(array, (IEnumerable<Expression>)indexes);
165         }
166
167         /// <summary>
168         /// Creates an <see cref="IndexExpression"></see> to access an array.
169         /// </summary>
170         /// <param name="array">An expression representing the array to index.</param>
171         /// <param name="indexes">An <see cref="IEnumerable{Expression}"/> containing expressions used to index the array.</param>
172         /// <remarks>The expression representing the array can be obtained by using the MakeMemberAccess method, 
173         /// or through NewArrayBounds or NewArrayInit.</remarks>
174         /// <returns>The created <see cref="IndexExpression"/>.</returns>
175         public static IndexExpression ArrayAccess(Expression array, IEnumerable<Expression> indexes) {
176             RequiresCanRead(array, "array");
177
178             Type arrayType = array.Type;
179             if (!arrayType.IsArray) {
180                 throw Error.ArgumentMustBeArray();
181             }
182
183             var indexList = indexes.ToReadOnly();
184             if (arrayType.GetArrayRank() != indexList.Count) {
185                 throw Error.IncorrectNumberOfIndexes();
186             }
187
188             foreach (Expression e in indexList) {
189                 RequiresCanRead(e, "indexes");
190                 if (e.Type != typeof(int)) {
191                     throw Error.ArgumentMustBeArrayIndexType();
192                 }
193             }
194
195             return new IndexExpression(array, null, indexList);
196         }
197
198         #endregion
199
200         #region Property
201         /// <summary>
202         /// Creates an <see cref="IndexExpression"/> representing the access to an indexed property.
203         /// </summary>
204         /// <param name="instance">The object to which the property belongs. If the property is static/shared, it must be null.</param>
205         /// <param name="propertyName">The name of the indexer.</param>
206         /// <param name="arguments">An array of <see cref="Expression"/> objects that are used to index the property.</param>
207         /// <returns>The created <see cref="IndexExpression"/>.</returns>
208         public static IndexExpression Property(Expression instance, string propertyName, params Expression[] arguments) {
209             RequiresCanRead(instance, "instance");
210             ContractUtils.RequiresNotNull(propertyName, "indexerName");
211             PropertyInfo pi = FindInstanceProperty(instance.Type, propertyName, arguments);
212             return Property(instance, pi, arguments);
213         }
214
215         #region methods for finding a PropertyInfo by its name
216         /// <summary>
217         /// The method finds the instance property with the specified name in a type. The property's type signature needs to be compatible with
218         /// the arguments if it is a indexer. If the arguments is null or empty, we get a normal property.
219         /// </summary>
220         private static PropertyInfo FindInstanceProperty(Type type, string propertyName, Expression[] arguments) {
221             // bind to public names first
222             BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy;
223             PropertyInfo pi = FindProperty(type, propertyName, arguments, flags);
224             if (pi == null) {
225                 flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy;
226                 pi = FindProperty(type, propertyName, arguments, flags);
227             }
228             if (pi == null) {
229                 if (arguments == null || arguments.Length == 0) {
230                     throw Error.InstancePropertyWithoutParameterNotDefinedForType(propertyName, type);
231                 } else {
232                     throw Error.InstancePropertyWithSpecifiedParametersNotDefinedForType(propertyName, GetArgTypesString(arguments), type);
233                 }
234             }
235             return pi;
236         }
237
238         private static string GetArgTypesString(Expression[] arguments) {
239             StringBuilder argTypesStr = new StringBuilder();
240             var isFirst = true;
241             argTypesStr.Append("(");
242             foreach (var t in arguments.Select(arg => arg.Type)) {
243                 if (!isFirst) {
244                     argTypesStr.Append(", ");
245                 }
246                 argTypesStr.Append(t.Name);
247                 isFirst = false;
248             }
249             argTypesStr.Append(")");
250             return argTypesStr.ToString();
251         }
252
253         private static PropertyInfo FindProperty(Type type, string propertyName, Expression[] arguments, BindingFlags flags) {
254             MemberInfo[] members = type.FindMembers(MemberTypes.Property, flags, Type.FilterNameIgnoreCase, propertyName);
255             if (members == null || members.Length == 0)
256                 return null;
257
258             PropertyInfo pi;
259             var propertyInfos = members.Map(t => (PropertyInfo)t);
260             int count = FindBestProperty(propertyInfos, arguments, out pi);
261
262             if (count == 0)
263                 return null;
264             if (count > 1)
265                 throw Error.PropertyWithMoreThanOneMatch(propertyName, type);
266             return pi;
267         }
268
269         private static int FindBestProperty(IEnumerable<PropertyInfo> properties, Expression[] args, out PropertyInfo property) {
270             int count = 0;
271             property = null;
272             foreach (PropertyInfo pi in properties) {
273                 if (pi != null && IsCompatible(pi, args)) {
274                     if (property == null) {
275                         property = pi;
276                         count = 1;
277                     }
278                     else {
279                         count++;
280                     }
281                 }
282             }
283             return count;
284         }
285
286         private static bool IsCompatible(PropertyInfo pi, Expression[] args) {
287             MethodInfo mi;
288
289             mi = pi.GetGetMethod(true);
290             ParameterInfo[] parms;
291             if (mi != null) {
292                 parms = mi.GetParametersCached();
293             } else {
294                 mi = pi.GetSetMethod(true);
295                 //The setter has an additional parameter for the value to set,
296                 //need to remove the last type to match the arguments.
297                 parms = mi.GetParametersCached().RemoveLast();
298             }
299             
300             if (mi == null) {
301                 return false;
302             }
303             if (args == null) {
304                 return parms.Length == 0;
305             }
306             
307             if (parms.Length != args.Length)
308                 return false;
309             for (int i = 0; i < args.Length; i++) {
310                 if (args[i] == null) return false;
311                 if (!TypeUtils.AreReferenceAssignable(parms[i].ParameterType, args[i].Type)) {
312                     return false;
313                 }
314             }
315             return true;
316         }
317         #endregion
318
319         /// <summary>
320         /// Creates an <see cref="IndexExpression"/> representing the access to an indexed property.
321         /// </summary>
322         /// <param name="instance">The object to which the property belongs. If the property is static/shared, it must be null.</param>
323         /// <param name="indexer">The <see cref="PropertyInfo"/> that represents the property to index.</param>
324         /// <param name="arguments">An array of <see cref="Expression"/> objects that are used to index the property.</param>
325         /// <returns>The created <see cref="IndexExpression"/>.</returns>
326         public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) {
327             return Property(instance, indexer, (IEnumerable<Expression>)arguments);
328         }
329
330         /// <summary>
331         /// Creates an <see cref="IndexExpression"/> representing the access to an indexed property.
332         /// </summary>
333         /// <param name="instance">The object to which the property belongs. If the property is static/shared, it must be null.</param>
334         /// <param name="indexer">The <see cref="PropertyInfo"/> that represents the property to index.</param>
335         /// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects that are used to index the property.</param>
336         /// <returns>The created <see cref="IndexExpression"/>.</returns>
337         public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable<Expression> arguments) {
338             var argList = arguments.ToReadOnly();
339             ValidateIndexedProperty(instance, indexer, ref argList);
340             return new IndexExpression(instance, indexer, argList);
341         }
342
343         // CTS places no restrictions on properties (see ECMA-335 8.11.3),
344         // so we validate that the property conforms to CLS rules here.
345         //
346         // Does reflection help us out at all? Expression.Property skips all of
347         // these checks, so either it needs more checks or we need less here.
348         private static void ValidateIndexedProperty(Expression instance, PropertyInfo property, ref ReadOnlyCollection<Expression> argList) {
349
350             // If both getter and setter specified, all their parameter types
351             // should match, with exception of the last setter parameter which
352             // should match the type returned by the get method.
353             // Accessor parameters cannot be ByRef.
354
355             ContractUtils.RequiresNotNull(property, "property");
356             if (property.PropertyType.IsByRef) throw Error.PropertyCannotHaveRefType();
357             if (property.PropertyType == typeof(void)) throw Error.PropertyTypeCannotBeVoid();
358
359             ParameterInfo[] getParameters = null;
360             MethodInfo getter = property.GetGetMethod(true);
361             if (getter != null) {
362                 getParameters = getter.GetParametersCached();
363                 ValidateAccessor(instance, getter, getParameters, ref argList);
364             }
365
366             MethodInfo setter = property.GetSetMethod(true);
367             if (setter != null) {
368                 ParameterInfo[] setParameters = setter.GetParametersCached();
369                 if (setParameters.Length == 0) throw Error.SetterHasNoParams();
370
371                 // valueType is the type of the value passed to the setter (last parameter)
372                 Type valueType = setParameters[setParameters.Length - 1].ParameterType;
373                 if (valueType.IsByRef) throw Error.PropertyCannotHaveRefType();
374                 if (setter.ReturnType != typeof(void)) throw Error.SetterMustBeVoid();
375                 if (property.PropertyType != valueType) throw Error.PropertyTyepMustMatchSetter();
376
377                 if (getter != null) {
378                     if (getter.IsStatic ^ setter.IsStatic) throw Error.BothAccessorsMustBeStatic();
379                     if (getParameters.Length != setParameters.Length - 1) throw Error.IndexesOfSetGetMustMatch();
380
381                     for (int i = 0; i < getParameters.Length; i++) {
382                         if (getParameters[i].ParameterType != setParameters[i].ParameterType) throw Error.IndexesOfSetGetMustMatch();
383                     }
384                 } else {
385                     ValidateAccessor(instance, setter, setParameters.RemoveLast(), ref argList);
386                 }
387             }
388
389             if (getter == null && setter == null) {
390                 throw Error.PropertyDoesNotHaveAccessor(property);
391             }
392         }
393
394         private static void ValidateAccessor(Expression instance, MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection<Expression> arguments) {
395             ContractUtils.RequiresNotNull(arguments, "arguments");
396
397             ValidateMethodInfo(method);
398             if ((method.CallingConvention & CallingConventions.VarArgs) != 0) throw Error.AccessorsCannotHaveVarArgs();
399             if (method.IsStatic) {
400                 if (instance != null) throw Error.OnlyStaticMethodsHaveNullInstance();
401             } else {
402                 if (instance == null) throw Error.OnlyStaticMethodsHaveNullInstance();
403                 RequiresCanRead(instance, "instance");
404                 ValidateCallInstanceType(instance.Type, method);
405             }
406
407             ValidateAccessorArgumentTypes(method, indexes, ref arguments);
408         }
409
410         private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection<Expression> arguments) {
411             if (indexes.Length > 0) {
412                 if (indexes.Length != arguments.Count) {
413                     throw Error.IncorrectNumberOfMethodCallArguments(method);
414                 }
415                 Expression[] newArgs = null;
416                 for (int i = 0, n = indexes.Length; i < n; i++) {
417                     Expression arg = arguments[i];
418                     ParameterInfo pi = indexes[i];
419                     RequiresCanRead(arg, "arguments");
420
421                     Type pType = pi.ParameterType;
422                     if (pType.IsByRef) throw Error.AccessorsCannotHaveByRefArgs();
423                     TypeUtils.ValidateType(pType);
424
425                     if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) {
426                         if (!TryQuote(pType, ref arg)) {
427                             throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method);
428                         }
429                     }
430                     if (newArgs == null && arg != arguments[i]) {
431                         newArgs = new Expression[arguments.Count];
432                         for (int j = 0; j < i; j++) {
433                             newArgs[j] = arguments[j];
434                         }
435                     }
436                     if (newArgs != null) {
437                         newArgs[i] = arg;
438                     }
439                 }
440                 if (newArgs != null) {
441                     arguments = new TrueReadOnlyCollection<Expression>(newArgs);
442                 }
443
444             } else if (arguments.Count > 0) {
445                 throw Error.IncorrectNumberOfMethodCallArguments(method);
446             }
447         }
448
449         #endregion
450     }
451 }