/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ using System; using System.Collections.Generic; using System.Reflection; #if FEATURE_REFEMIT using System.Reflection.Emit; #endif using System.Security; using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; namespace Microsoft.Scripting.Interpreter { public abstract partial class CallInstruction : Instruction { public abstract MethodInfo Info { get; } /// /// The number of arguments including "this" for instance methods. /// public abstract int ArgumentCount { get; } #region Construction internal CallInstruction() { } private static readonly Dictionary _cache = new Dictionary(); /// Instruction can't be created due to insufficient privileges. public static CallInstruction Create(MethodInfo info) { return Create(info, info.GetParameters()); } /// Instruction can't be created due to insufficient privileges. public static CallInstruction Create(MethodInfo info, ParameterInfo[] parameters) { int argumentCount = parameters.Length; if (!info.IsStatic) { argumentCount++; } // A workaround for CLR bug #796414 (Unable to create delegates for Array.Get/Set): // T[]::Address - not supported by ETs due to T& return value if (info.DeclaringType != null && info.DeclaringType.IsArray && (info.Name == "Get" || info.Name == "Set")) { return GetArrayAccessor(info, argumentCount); } #if FULL_AOT_RUNTIME return new MethodInfoCallInstruction(info, argumentCount); #else if (ReflectionUtils.IsDynamicMethod(info) || !info.IsStatic && info.DeclaringType.IsValueType()) { return new MethodInfoCallInstruction(info, argumentCount); } if (argumentCount >= MaxHelpers) { // no delegate for this size, fallback to reflection invoke return new MethodInfoCallInstruction(info, argumentCount); } foreach (ParameterInfo pi in parameters) { if (pi.ParameterType.IsByRef) { // we don't support ref args via generics. return new MethodInfoCallInstruction(info, argumentCount); } } // see if we've created one w/ a delegate CallInstruction res; if (ShouldCache(info)) { lock (_cache) { if (_cache.TryGetValue(info, out res)) { return res; } } } // create it try { if (argumentCount < MaxArgs) { res = FastCreate(info, parameters); } else { res = SlowCreate(info, parameters); } } catch (TargetInvocationException tie) { if (!(tie.InnerException is NotSupportedException)) { throw tie.InnerException; } res = new MethodInfoCallInstruction(info, argumentCount); } catch (NotSupportedException) { // if Delegate.CreateDelegate can't handle the method fallback to // the slow reflection version. For example this can happen w/ // a generic method defined on an interface and implemented on a class or // a virtual generic method. res = new MethodInfoCallInstruction(info, argumentCount); } // cache it for future users if it's a reasonable method to cache if (ShouldCache(info)) { lock (_cache) { _cache[info] = res; } } return res; #endif } private static CallInstruction GetArrayAccessor(MethodInfo info, int argumentCount) { Type arrayType = info.DeclaringType; bool isGetter = info.Name == "Get"; switch (arrayType.GetArrayRank()) { case 1: return Create(isGetter ? arrayType.GetMethod("GetValue", new[] { typeof(int)}) : new Action(ArrayItemSetter1).GetMethodInfo() ); case 2: return Create(isGetter ? arrayType.GetMethod("GetValue", new[] { typeof(int), typeof(int) }) : new Action(ArrayItemSetter2).GetMethodInfo() ); case 3: return Create(isGetter ? arrayType.GetMethod("GetValue", new[] { typeof(int), typeof(int), typeof(int) }) : new Action(ArrayItemSetter3).GetMethodInfo() ); default: return new MethodInfoCallInstruction(info, argumentCount); } } public static void ArrayItemSetter1(Array array, int index0, object value) { array.SetValue(value, index0); } public static void ArrayItemSetter2(Array array, int index0, int index1, object value) { array.SetValue(value, index0, index1); } public static void ArrayItemSetter3(Array array, int index0, int index1, int index2, object value) { array.SetValue(value, index0, index1, index2); } private static bool ShouldCache(MethodInfo info) { return !ReflectionUtils.IsDynamicMethod(info); } /// /// Gets the next type or null if no more types are available. /// private static Type TryGetParameterOrReturnType(MethodInfo target, ParameterInfo[] pi, int index) { if (!target.IsStatic) { index--; if (index < 0) { return target.DeclaringType; } } if (index < pi.Length) { // next in signature return pi[index].ParameterType; } if (target.ReturnType == typeof(void) || index > pi.Length) { // no more parameters return null; } // last parameter on Invoke is return type return target.ReturnType; } private static bool IndexIsNotReturnType(int index, MethodInfo target, ParameterInfo[] pi) { return pi.Length != index || (pi.Length == index && !target.IsStatic); } /// /// Uses reflection to create new instance of the appropriate ReflectedCaller /// private static CallInstruction SlowCreate(MethodInfo info, ParameterInfo[] pis) { List types = new List(); if (!info.IsStatic) types.Add(info.DeclaringType); foreach (ParameterInfo pi in pis) { types.Add(pi.ParameterType); } if (info.ReturnType != typeof(void)) { types.Add(info.ReturnType); } Type[] arrTypes = types.ToArray(); return (CallInstruction)Activator.CreateInstance(GetHelperType(info, arrTypes), info); } #endregion #region Instruction public sealed override int ProducedStack { get { return Info.ReturnType == typeof(void) ? 0 : 1; } } public sealed override int ConsumedStack { get { return ArgumentCount; } } public sealed override string InstructionName { get { return "Call"; } } public override string ToString() { return "Call(" + Info + ")"; } #endregion } internal sealed partial class MethodInfoCallInstruction : CallInstruction { private readonly MethodInfo _target; private readonly int _argumentCount; public override MethodInfo Info { get { return _target; } } public override int ArgumentCount { get { return _argumentCount; } } internal MethodInfoCallInstruction(MethodInfo target, int argumentCount) { _target = target; _argumentCount = argumentCount; } public override object Invoke(params object[] args) { return InvokeWorker(args); } public override object InvokeInstance(object instance, params object[] args) { if (_target.IsStatic) { try { return _target.Invoke(null, args); } catch (TargetInvocationException e) { throw ExceptionHelpers.UpdateForRethrow(e.InnerException); } } try { return _target.Invoke(instance, args); } catch (TargetInvocationException e) { throw ExceptionHelpers.UpdateForRethrow(e.InnerException); } } private object InvokeWorker(params object[] args) { if (_target.IsStatic) { try { return _target.Invoke(null, args); } catch (TargetInvocationException e) { throw ExceptionHelpers.UpdateForRethrow(e.InnerException); } } try { return _target.Invoke(args[0], GetNonStaticArgs(args)); } catch (TargetInvocationException e) { throw ExceptionHelpers.UpdateForRethrow(e.InnerException); } } private static object[] GetNonStaticArgs(object[] args) { object[] newArgs = new object[args.Length - 1]; for (int i = 0; i < newArgs.Length; i++) { newArgs[i] = args[i + 1]; } return newArgs; } public sealed override int Run(InterpretedFrame frame) { int first = frame.StackIndex - _argumentCount; object[] args = new object[_argumentCount]; for (int i = 0; i < args.Length; i++) { args[i] = frame.Data[first + i]; } object ret = Invoke(args); if (_target.ReturnType != typeof(void)) { frame.Data[first] = ret; frame.StackIndex = first + 1; } else { frame.StackIndex = first; } return 1; } } }