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 * dlr@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 * ***************************************************************************/
17 using System.Threading.Tasks;
21 using System.Linq.Expressions;
23 using Microsoft.Scripting.Ast;
27 using System.Reflection;
29 using System.Reflection.Emit;
31 using System.Runtime.CompilerServices;
32 using System.Security;
33 using System.Threading;
35 using Microsoft.Scripting.Generation;
36 using Microsoft.Scripting.Runtime;
37 using Microsoft.Scripting.Utils;
39 using AstUtils = Microsoft.Scripting.Ast.Utils;
41 namespace Microsoft.Scripting.Interpreter {
43 public sealed class LightLambdaCompileEventArgs : EventArgs {
44 public Delegate Compiled { get; private set; }
46 internal LightLambdaCompileEventArgs(Delegate compiled) {
51 public partial class LightLambda {
52 private readonly StrongBox<object>[] _closure;
53 private readonly Interpreter _interpreter;
54 private static readonly CacheDict<Type, Func<LightLambda, Delegate>> _runCache = new CacheDict<Type, Func<LightLambda, Delegate>>(100);
56 // Adaptive compilation support
57 private readonly LightDelegateCreator _delegateCreator;
59 const Delegate _compiled = null;
61 private Delegate _compiled;
63 private int _compilationThreshold;
67 /// Provides notification that the LightLambda has been compiled.
69 public event EventHandler<LightLambdaCompileEventArgs> Compile;
72 internal LightLambda(LightDelegateCreator delegateCreator, StrongBox<object>[] closure, int compilationThreshold) {
73 _delegateCreator = delegateCreator;
75 _interpreter = delegateCreator.Interpreter;
76 _compilationThreshold = compilationThreshold;
79 private static Func<LightLambda, Delegate> GetRunDelegateCtor(Type delegateType) {
81 Func<LightLambda, Delegate> fastCtor;
82 if (_runCache.TryGetValue(delegateType, out fastCtor)) {
85 return MakeRunDelegateCtor(delegateType);
89 private static Func<LightLambda, Delegate> MakeRunDelegateCtor(Type delegateType) {
90 var method = delegateType.GetMethod("Invoke");
91 var paramInfos = method.GetParameters();
95 if (paramInfos.Length >= MaxParameters) {
99 if (method.ReturnType == typeof(void)) {
101 paramTypes = new Type[paramInfos.Length];
103 paramTypes = new Type[paramInfos.Length + 1];
104 paramTypes[paramTypes.Length - 1] = method.ReturnType;
107 MethodInfo runMethod;
109 if (method.ReturnType == typeof(void) && paramTypes.Length == 2 &&
110 paramInfos[0].ParameterType.IsByRef && paramInfos[1].ParameterType.IsByRef) {
111 runMethod = typeof(LightLambda).GetMethod("RunVoidRef2", BindingFlags.NonPublic | BindingFlags.Instance);
112 paramTypes[0] = paramInfos[0].ParameterType.GetElementType();
113 paramTypes[1] = paramInfos[1].ParameterType.GetElementType();
114 } else if (method.ReturnType == typeof(void) && paramTypes.Length == 0) {
115 runMethod = typeof(LightLambda).GetMethod("RunVoid0", BindingFlags.NonPublic | BindingFlags.Instance);
117 for (int i = 0; i < paramInfos.Length; i++) {
118 paramTypes[i] = paramInfos[i].ParameterType;
119 if (paramTypes[i].IsByRef) {
124 if (DelegateHelpers.MakeDelegate(paramTypes) == delegateType) {
125 name = "Make" + name + paramInfos.Length;
127 MethodInfo ctorMethod = typeof(LightLambda).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(paramTypes);
128 return _runCache[delegateType] = (Func<LightLambda, Delegate>)ctorMethod.CreateDelegate(typeof(Func<LightLambda, Delegate>));
131 runMethod = typeof(LightLambda).GetMethod(name + paramInfos.Length, BindingFlags.NonPublic | BindingFlags.Instance);
134 #if FEATURE_LCG && !SILVERLIGHT && !WP75
136 DynamicMethod dm = new DynamicMethod("FastCtor", typeof(Delegate), new[] { typeof(LightLambda) }, typeof(LightLambda), true);
137 var ilgen = dm.GetILGenerator();
138 ilgen.Emit(OpCodes.Ldarg_0);
139 ilgen.Emit(OpCodes.Ldftn, runMethod.IsGenericMethodDefinition ? runMethod.MakeGenericMethod(paramTypes) : runMethod);
140 ilgen.Emit(OpCodes.Newobj, delegateType.GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
141 ilgen.Emit(OpCodes.Ret);
142 return _runCache[delegateType] = (Func<LightLambda, Delegate>)dm.CreateDelegate(typeof(Func<LightLambda, Delegate>));
143 } catch (SecurityException) {
147 // we don't have permission for restricted skip visibility dynamic methods, use the slower Delegate.CreateDelegate.
148 var targetMethod = runMethod.IsGenericMethodDefinition ? runMethod.MakeGenericMethod(paramTypes) : runMethod;
149 return _runCache[delegateType] = lambda => targetMethod.CreateDelegate(delegateType, lambda);
152 //TODO enable sharing of these custom delegates
153 private Delegate CreateCustomDelegate(Type delegateType) {
154 PerfTrack.NoteEvent(PerfTrack.Categories.Compiler, "Synchronously compiling a custom delegate");
156 var method = delegateType.GetMethod("Invoke");
157 var paramInfos = method.GetParameters();
158 var parameters = new ParameterExpression[paramInfos.Length];
159 var parametersAsObject = new Expression[paramInfos.Length];
160 for (int i = 0; i < paramInfos.Length; i++) {
161 ParameterExpression parameter = Expression.Parameter(paramInfos[i].ParameterType, paramInfos[i].Name);
162 parameters[i] = parameter;
163 parametersAsObject[i] = Expression.Convert(parameter, typeof(object));
166 var data = Expression.NewArrayInit(typeof(object), parametersAsObject);
167 var self = AstUtils.Constant(this);
168 var runMethod = typeof(LightLambda).GetMethod("Run");
169 var body = Expression.Convert(Expression.Call(self, runMethod, data), method.ReturnType);
170 var lambda = Expression.Lambda(delegateType, body, parameters);
171 return lambda.Compile();
174 internal Delegate MakeDelegate(Type delegateType) {
175 Func<LightLambda, Delegate> fastCtor = GetRunDelegateCtor(delegateType);
176 if (fastCtor != null) {
177 return fastCtor(this);
179 return CreateCustomDelegate(delegateType);
183 private bool TryGetCompiled() {
184 #if !MONO_INTERPRETER
185 // Use the compiled delegate if available.
186 if (_delegateCreator.HasCompiled) {
187 _compiled = _delegateCreator.CreateCompiledDelegate(_closure);
189 // Send it to anyone who's interested.
190 var compileEvent = Compile;
191 if (compileEvent != null && _delegateCreator.SameDelegateType) {
192 compileEvent(this, new LightLambdaCompileEventArgs(_compiled));
198 // Don't lock here, it's a frequently hit path.
200 // There could be multiple threads racing, but that is okay.
201 // Two bad things can happen:
202 // * We miss decrements (some thread sets the counter forward)
203 // * We might enter the "if" branch more than once.
205 // The first is okay, it just means we take longer to compile.
206 // The second we explicitly guard against inside of Compile().
208 // We can't miss 0. The first thread that writes -1 must have read 0 and hence start compilation.
209 if (unchecked(_compilationThreshold--) == 0) {
211 if (PlatformAdaptationLayer.IsCompactFramework) {
212 _compilationThreshold = Int32.MaxValue;
216 if (_interpreter.CompileSynchronously) {
217 _delegateCreator.Compile(null);
218 return TryGetCompiled();
220 // Kick off the compile on another thread so this one can keep going
222 new Task(_delegateCreator.Compile, null).Start();
224 ThreadPool.QueueUserWorkItem(_delegateCreator.Compile, null);
232 private InterpretedFrame MakeFrame() {
233 return new InterpretedFrame(_interpreter, _closure);
236 internal void RunVoidRef2<T0, T1>(ref T0 arg0, ref T1 arg1) {
237 if (_compiled != null || TryGetCompiled()) {
238 ((ActionRef<T0, T1>)_compiled)(ref arg0, ref arg1);
242 // copy in and copy out for today...
243 var frame = MakeFrame();
244 frame.Data[0] = arg0;
245 frame.Data[1] = arg1;
246 var currentFrame = frame.Enter();
248 _interpreter.Run(frame);
250 frame.Leave(currentFrame);
251 arg0 = (T0)frame.Data[0];
252 arg1 = (T1)frame.Data[1];
256 #if !MONO_INTERPRETER
257 public object Run(params object[] arguments) {
258 if (_compiled != null || TryGetCompiled()) {
260 return _compiled.DynamicInvoke(arguments);
261 } catch (TargetInvocationException e) {
262 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
266 var frame = MakeFrame();
267 for (int i = 0; i < arguments.Length; i++) {
268 frame.Data[i] = arguments[i];
270 var currentFrame = frame.Enter();
272 _interpreter.Run(frame);
274 frame.Leave(currentFrame);