/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Microsoft Public License. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Microsoft Public License, please send an email to * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Microsoft Public License. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ using System; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Scripting.Generation; using Microsoft.Scripting.Utils; namespace Microsoft.Scripting.Interpretation { /// /// Interpreter partial class. This part contains interpretation code for lambdas. /// public static partial class Interpreter { private static int _DelegateCounter; private static WeakDictionary _Delegates; private static object DoExecute(InterpreterState state, LambdaExpression lambda) { object ret = Interpreter.Interpret(state, lambda.Body); ControlFlow cf = ret as ControlFlow; if (cf != null) { return cf.Value; } else { return ret; } } /// /// Called by the code:LambdaInvoker.Invoke from the delegate generated below by /// code:GetDelegateForInterpreter. /// /// This method must repackage arguments to match the lambdas signature, which /// may mean repackaging the parameter arrays. /// /// Input are two arrays - regular arguments passed into the generated delegate, /// and (if the delegate had params array), the parameter array, separately. /// internal static object InterpretLambda(InterpreterState lexicalParentState, LambdaExpression lambda, object[] args) { Assert.NotNull(lexicalParentState, lambda, args); var state = InterpreterState.Current.Update( (caller) => lexicalParentState.CreateForLambda(lambda, caller, args) ); try { object result = Interpret(state, lambda.Body); var cf = result as ControlFlow; if (cf != null) { return (cf.Kind == ControlFlowKind.Yield) ? cf.Value : null; } return result; } finally { InterpreterState.Current.Value = state.Caller; } } /// /// Gets the delegate associated with the LambdaExpression. /// Either it uses cached MethodInfo and creates delegate from it, or it will generate /// completely new dynamic method, store it in a cache and use it to create the delegate. /// private static Delegate GetDelegateForInterpreter(InterpreterState state, LambdaExpression lambda) { MethodInfo method; if (!LambdaInvoker.TryGetGenericInvokeMethod(lambda.Parameters.Count, out method) || HasByRefParameter(lambda)) { return GenerateDelegateForInterpreter(state, lambda); } Type[] signature = GetSignature(lambda); method = method.MakeGenericMethod(signature); return ReflectionUtils.CreateDelegate(method, lambda.Type, new LambdaInvoker(lambda, state)); } private static bool HasByRefParameter(LambdaExpression lambda) { for (int i = 0; i < lambda.Parameters.Count; i++) { if (lambda.Parameters[i].IsByRef) { return true; } } return false; } private static Type[] GetSignature(LambdaExpression lambda) { Type[] result = new Type[1 + lambda.Parameters.Count]; for (int i = 0; i < lambda.Parameters.Count; i++) { result[i] = lambda.Parameters[i].Type; } result[lambda.Parameters.Count] = lambda.ReturnType; return result; } private static Delegate GenerateDelegateForInterpreter(InterpreterState state, LambdaExpression lambda) { if (_Delegates == null) { Interlocked.CompareExchange>( ref _Delegates, new WeakDictionary(), null ); } bool found; MethodInfo method; // // LOCK to find the MethodInfo // lock (_Delegates) { found = _Delegates.TryGetValue(lambda, out method); } if (!found) { method = CreateDelegateForInterpreter(lambda.Type); // // LOCK to store the MethodInfo // (and maybe find one added while we were creating new delegate, in which case // throw away the new one and use the one from the cache. // lock (_Delegates) { MethodInfo conflict; if (!_Delegates.TryGetValue(lambda, out conflict)) { _Delegates.Add(lambda, method); } else { method = conflict; } } } return ReflectionUtils.CreateDelegate(method, lambda.Type, new LambdaInvoker(lambda, state)); } /// /// The core of the interpreter, calling back onto itself via delegates. /// private static MethodInfo CreateDelegateForInterpreter(Type type) { Debug.Assert(type != typeof(Delegate) && typeof(Delegate).IsAssignableFrom(type)); // // Get the desired signature // MethodInfo invoke = type.GetMethod("Invoke"); ParameterInfo[] parameters = invoke.GetParameters(); string name = "Interpreted_" + Interlocked.Increment(ref _DelegateCounter); Type[] signature = CreateInterpreterSignature(parameters); DynamicILGen il = Snippets.Shared.CreateDynamicMethod(name, invoke.ReturnType, signature, false); // Collect all arguments received by the delegate into an array // and pass them to the Interpreter along with the LambdaInvoker // LambdaInvoker il.EmitLoadArg(0); int count = parameters.Length; // Create the array il.EmitInt(count); il.Emit(OpCodes.Newarr, typeof(object)); for (int i = 0; i < count; i++) { il.Emit(OpCodes.Dup); il.EmitInt(i); il.EmitLoadArg(i + 1); EmitExplicitCast(il, parameters[i].ParameterType, typeof(object)); il.EmitStoreElement(typeof(object)); } // Call back to interpreter il.EmitCall(LambdaInvoker.GetInvokeMethod()); // Cast back to the delegate return type EmitExplicitCast(il, typeof(object), invoke.ReturnType); // And return whatever the result was. il.Emit(OpCodes.Ret); // // We are done (for now), finish the MethodInfo // return il.Finish(); } private static void EmitExplicitCast(ILGen il, Type from, Type to) { if (!il.TryEmitExplicitCast(from, to)) { throw new ArgumentException(String.Format("Cannot cast from '{0}' to '{1}'", from, to)); } } private static Type[] CreateInterpreterSignature(ParameterInfo[] parameters) { Type[] signature = new Type[parameters.Length + 1]; // First one is always LambdaInvoker. signature[0] = typeof(LambdaInvoker); // The rest is copied from the parameter infos. for (int i = 0; i < parameters.Length; i++) { signature[i + 1] = parameters[i].ParameterType; } return signature; } } }