Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / Instructions / CallInstruction.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  * ironpy@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 using System;
16 using System.Collections.Generic;
17 using System.Reflection;
18 #if FEATURE_REFEMIT
19 using System.Reflection.Emit;
20 #endif
21 using System.Security;
22 using Microsoft.Scripting.Runtime;
23 using Microsoft.Scripting.Utils;
24
25 namespace Microsoft.Scripting.Interpreter {
26     public abstract partial class CallInstruction : Instruction {
27         public abstract MethodInfo Info { get; }
28
29         /// <summary>
30         /// The number of arguments including "this" for instance methods.
31         /// </summary>
32         public abstract int ArgumentCount { get; }
33
34         #region Construction
35
36         internal CallInstruction() { }
37         
38         private static readonly Dictionary<MethodInfo, CallInstruction> _cache = new Dictionary<MethodInfo, CallInstruction>();
39
40         /// <exception cref="SecurityException">Instruction can't be created due to insufficient privileges.</exception>
41         public static CallInstruction Create(MethodInfo info) {
42             return Create(info, info.GetParameters());
43         }
44
45         /// <exception cref="SecurityException">Instruction can't be created due to insufficient privileges.</exception>
46         public static CallInstruction Create(MethodInfo info, ParameterInfo[] parameters) {
47             int argumentCount = parameters.Length;
48             if (!info.IsStatic) {
49                 argumentCount++;
50             }
51
52             // A workaround for CLR bug #796414 (Unable to create delegates for Array.Get/Set):
53             // T[]::Address - not supported by ETs due to T& return value
54             if (info.DeclaringType != null && info.DeclaringType.IsArray && (info.Name == "Get" || info.Name == "Set")) {
55                 return GetArrayAccessor(info, argumentCount);
56             }
57
58             if (ReflectionUtils.IsDynamicMethod(info) || !info.IsStatic && info.DeclaringType.IsValueType()) {
59                 return new MethodInfoCallInstruction(info, argumentCount);
60             }
61
62             if (argumentCount >= MaxHelpers) {
63                 // no delegate for this size, fallback to reflection invoke
64                 return new MethodInfoCallInstruction(info, argumentCount);
65             }
66
67             foreach (ParameterInfo pi in parameters) {
68                 if (pi.ParameterType.IsByRef) {
69                     // we don't support ref args via generics.
70                     return new MethodInfoCallInstruction(info, argumentCount);
71                 }
72             }
73
74             // see if we've created one w/ a delegate
75             CallInstruction res;
76             if (ShouldCache(info)) {
77                 lock (_cache) {
78                     if (_cache.TryGetValue(info, out res)) {
79                         return res;
80                     }
81                 }
82             }
83
84             // create it 
85             try {
86                 if (argumentCount < MaxArgs) {
87                     res = FastCreate(info, parameters);
88                 } else {
89                     res = SlowCreate(info, parameters);
90                 }
91             } catch (TargetInvocationException tie) {
92                 if (!(tie.InnerException is NotSupportedException)) {
93                     throw tie.InnerException;
94                 }
95
96                 res = new MethodInfoCallInstruction(info, argumentCount);
97             } catch (NotSupportedException) {
98                 // if Delegate.CreateDelegate can't handle the method fallback to 
99                 // the slow reflection version.  For example this can happen w/ 
100                 // a generic method defined on an interface and implemented on a class or 
101                 // a virtual generic method.
102                 res = new MethodInfoCallInstruction(info, argumentCount);
103             }
104
105             // cache it for future users if it's a reasonable method to cache
106             if (ShouldCache(info)) {
107                 lock (_cache) {
108                     _cache[info] = res;
109                 }
110             }
111
112             return res;            
113         }
114
115         private static CallInstruction GetArrayAccessor(MethodInfo info, int argumentCount) {
116             Type arrayType = info.DeclaringType;
117             bool isGetter = info.Name == "Get";
118             switch (arrayType.GetArrayRank()) {
119                 case 1:
120                     return Create(isGetter ?
121                         arrayType.GetMethod("GetValue", new[] { typeof(int)}) :
122                         new Action<Array, int, object>(ArrayItemSetter1).GetMethodInfo()
123                     );
124                
125                 case 2: 
126                     return Create(isGetter ? 
127                         arrayType.GetMethod("GetValue", new[] { typeof(int), typeof(int) }) :
128                         new Action<Array, int, int, object>(ArrayItemSetter2).GetMethodInfo()
129                     );
130
131                 case 3: 
132                     return Create(isGetter ?
133                         arrayType.GetMethod("GetValue", new[] { typeof(int), typeof(int), typeof(int) }) :
134                         new Action<Array, int, int, int, object>(ArrayItemSetter3).GetMethodInfo()
135                     );
136
137                 default: 
138                     return new MethodInfoCallInstruction(info, argumentCount);
139             }
140         }
141
142         public static void ArrayItemSetter1(Array array, int index0, object value) {
143             array.SetValue(value, index0);
144         }
145
146         public static void ArrayItemSetter2(Array array, int index0, int index1, object value) {
147             array.SetValue(value, index0, index1);
148         }
149
150         public static void ArrayItemSetter3(Array array, int index0, int index1, int index2, object value) {
151             array.SetValue(value, index0, index1, index2);
152         }
153
154         private static bool ShouldCache(MethodInfo info) {            
155             return !ReflectionUtils.IsDynamicMethod(info);
156         }
157                
158         /// <summary>
159         /// Gets the next type or null if no more types are available.
160         /// </summary>
161         private static Type TryGetParameterOrReturnType(MethodInfo target, ParameterInfo[] pi, int index) {
162             if (!target.IsStatic) {
163                 index--;
164                 if (index < 0) {
165                     return target.DeclaringType;
166                 }
167             }
168
169             if (index < pi.Length) {
170                 // next in signature
171                 return pi[index].ParameterType;
172             }
173
174             if (target.ReturnType == typeof(void) || index > pi.Length) {
175                 // no more parameters
176                 return null;
177             }
178
179             // last parameter on Invoke is return type
180             return target.ReturnType;
181         }
182
183         private static bool IndexIsNotReturnType(int index, MethodInfo target, ParameterInfo[] pi) {
184             return pi.Length != index || (pi.Length == index && !target.IsStatic);
185         }
186
187         /// <summary>
188         /// Uses reflection to create new instance of the appropriate ReflectedCaller
189         /// </summary>
190         private static CallInstruction SlowCreate(MethodInfo info, ParameterInfo[] pis) {
191             List<Type> types = new List<Type>();
192             if (!info.IsStatic) types.Add(info.DeclaringType);
193             foreach (ParameterInfo pi in pis) {
194                 types.Add(pi.ParameterType);
195             }
196             if (info.ReturnType != typeof(void)) {
197                 types.Add(info.ReturnType);
198             }
199             Type[] arrTypes = types.ToArray();
200
201             return (CallInstruction)Activator.CreateInstance(GetHelperType(info, arrTypes), info);
202         }
203
204         #endregion
205
206         #region Instruction
207
208         public sealed override int ProducedStack { get { return Info.ReturnType == typeof(void) ? 0 : 1; } }
209         public sealed override int ConsumedStack { get { return ArgumentCount; } }
210
211         public sealed override string InstructionName {
212             get { return "Call"; }
213         }
214
215         public override string ToString() {
216             return "Call(" + Info + ")";
217         }
218
219         #endregion
220     }
221
222     internal sealed partial class MethodInfoCallInstruction : CallInstruction {
223         private readonly MethodInfo _target;
224         private readonly int _argumentCount;
225
226         public override MethodInfo Info { get { return _target; } }
227         public override int ArgumentCount { get { return _argumentCount; } }
228
229         internal MethodInfoCallInstruction(MethodInfo target, int argumentCount) {
230             _target = target;
231             _argumentCount = argumentCount;
232         }
233         
234         public override object Invoke(params object[] args) {
235             return InvokeWorker(args);
236         }
237        
238         public override object InvokeInstance(object instance, params object[] args) {
239             if (_target.IsStatic) {
240                 try {
241                     return _target.Invoke(null, args);
242                 } catch (TargetInvocationException e) {
243                     throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
244                 }
245             }
246
247             try {
248                 return _target.Invoke(instance, args);
249             } catch (TargetInvocationException e) {
250                 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
251             }
252         }
253
254         private object InvokeWorker(params object[] args) {
255             if (_target.IsStatic) {
256                 try {
257                     return _target.Invoke(null, args);
258                 } catch (TargetInvocationException e) {
259                     throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
260                 }
261             }
262
263             try {
264                 return _target.Invoke(args[0], GetNonStaticArgs(args));
265             } catch (TargetInvocationException e) {
266                 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
267             }
268         }
269
270         private static object[] GetNonStaticArgs(object[] args) {
271             object[] newArgs = new object[args.Length - 1];
272             for (int i = 0; i < newArgs.Length; i++) {
273                 newArgs[i] = args[i + 1];
274             }
275             return newArgs;
276         }
277
278         public sealed override int Run(InterpretedFrame frame) {
279             int first = frame.StackIndex - _argumentCount;
280             object[] args = new object[_argumentCount];
281             for (int i = 0; i < args.Length; i++) {
282                 args[i] = frame.Data[first + i];
283             }
284
285             object ret = Invoke(args);
286             if (_target.ReturnType != typeof(void)) {
287                 frame.Data[first] = ret;
288                 frame.StackIndex = first + 1;
289             } else {
290                 frame.StackIndex = first;
291             }
292             return 1;
293         }
294     }
295     
296 }