1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
16 using System.Collections.Generic;
17 using System.Reflection;
19 using System.Reflection.Emit;
21 using System.Security;
22 using Microsoft.Scripting.Runtime;
23 using Microsoft.Scripting.Utils;
25 namespace Microsoft.Scripting.Interpreter {
26 public abstract partial class CallInstruction : Instruction {
27 public abstract MethodInfo Info { get; }
30 /// The number of arguments including "this" for instance methods.
32 public abstract int ArgumentCount { get; }
36 internal CallInstruction() { }
38 private static readonly Dictionary<MethodInfo, CallInstruction> _cache = new Dictionary<MethodInfo, CallInstruction>();
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());
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;
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);
59 return new MethodInfoCallInstruction(info, argumentCount);
61 if (ReflectionUtils.IsDynamicMethod(info) || !info.IsStatic && info.DeclaringType.IsValueType()) {
62 return new MethodInfoCallInstruction(info, argumentCount);
65 if (argumentCount >= MaxHelpers) {
66 // no delegate for this size, fallback to reflection invoke
67 return new MethodInfoCallInstruction(info, argumentCount);
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);
77 // see if we've created one w/ a delegate
79 if (ShouldCache(info)) {
81 if (_cache.TryGetValue(info, out res)) {
89 if (argumentCount < MaxArgs) {
90 res = FastCreate(info, parameters);
92 res = SlowCreate(info, parameters);
94 } catch (TargetInvocationException tie) {
95 if (!(tie.InnerException is NotSupportedException)) {
96 throw tie.InnerException;
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);
108 // cache it for future users if it's a reasonable method to cache
109 if (ShouldCache(info)) {
119 private static CallInstruction GetArrayAccessor(MethodInfo info, int argumentCount) {
120 Type arrayType = info.DeclaringType;
121 bool isGetter = info.Name == "Get";
122 switch (arrayType.GetArrayRank()) {
124 return Create(isGetter ?
125 arrayType.GetMethod("GetValue", new[] { typeof(int)}) :
126 new Action<Array, int, object>(ArrayItemSetter1).GetMethodInfo()
130 return Create(isGetter ?
131 arrayType.GetMethod("GetValue", new[] { typeof(int), typeof(int) }) :
132 new Action<Array, int, int, object>(ArrayItemSetter2).GetMethodInfo()
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()
142 return new MethodInfoCallInstruction(info, argumentCount);
146 public static void ArrayItemSetter1(Array array, int index0, object value) {
147 array.SetValue(value, index0);
150 public static void ArrayItemSetter2(Array array, int index0, int index1, object value) {
151 array.SetValue(value, index0, index1);
154 public static void ArrayItemSetter3(Array array, int index0, int index1, int index2, object value) {
155 array.SetValue(value, index0, index1, index2);
158 private static bool ShouldCache(MethodInfo info) {
159 return !ReflectionUtils.IsDynamicMethod(info);
163 /// Gets the next type or null if no more types are available.
165 private static Type TryGetParameterOrReturnType(MethodInfo target, ParameterInfo[] pi, int index) {
166 if (!target.IsStatic) {
169 return target.DeclaringType;
173 if (index < pi.Length) {
175 return pi[index].ParameterType;
178 if (target.ReturnType == typeof(void) || index > pi.Length) {
179 // no more parameters
183 // last parameter on Invoke is return type
184 return target.ReturnType;
187 private static bool IndexIsNotReturnType(int index, MethodInfo target, ParameterInfo[] pi) {
188 return pi.Length != index || (pi.Length == index && !target.IsStatic);
192 /// Uses reflection to create new instance of the appropriate ReflectedCaller
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);
200 if (info.ReturnType != typeof(void)) {
201 types.Add(info.ReturnType);
203 Type[] arrTypes = types.ToArray();
205 return (CallInstruction)Activator.CreateInstance(GetHelperType(info, arrTypes), info);
212 public sealed override int ProducedStack { get { return Info.ReturnType == typeof(void) ? 0 : 1; } }
213 public sealed override int ConsumedStack { get { return ArgumentCount; } }
215 public sealed override string InstructionName {
216 get { return "Call"; }
219 public override string ToString() {
220 return "Call(" + Info + ")";
226 internal sealed partial class MethodInfoCallInstruction : CallInstruction {
227 private readonly MethodInfo _target;
228 private readonly int _argumentCount;
230 public override MethodInfo Info { get { return _target; } }
231 public override int ArgumentCount { get { return _argumentCount; } }
233 internal MethodInfoCallInstruction(MethodInfo target, int argumentCount) {
235 _argumentCount = argumentCount;
238 public override object Invoke(params object[] args) {
239 return InvokeWorker(args);
242 public override object InvokeInstance(object instance, params object[] args) {
243 if (_target.IsStatic) {
245 return _target.Invoke(null, args);
246 } catch (TargetInvocationException e) {
247 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
252 return _target.Invoke(instance, args);
253 } catch (TargetInvocationException e) {
254 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
258 private object InvokeWorker(params object[] args) {
259 if (_target.IsStatic) {
261 return _target.Invoke(null, args);
262 } catch (TargetInvocationException e) {
263 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
268 return _target.Invoke(args[0], GetNonStaticArgs(args));
269 } catch (TargetInvocationException e) {
270 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
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];
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];
289 object ret = Invoke(args);
290 if (_target.ReturnType != typeof(void)) {
291 frame.Data[first] = ret;
292 frame.StackIndex = first + 1;
294 frame.StackIndex = first;