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