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;
58 private Delegate _compiled;
59 private int _compilationThreshold;
62 /// Provides notification that the LightLambda has been compiled.
64 public event EventHandler<LightLambdaCompileEventArgs> Compile;
66 internal LightLambda(LightDelegateCreator delegateCreator, StrongBox<object>[] closure, int compilationThreshold) {
67 _delegateCreator = delegateCreator;
69 _interpreter = delegateCreator.Interpreter;
70 _compilationThreshold = compilationThreshold;
73 private static Func<LightLambda, Delegate> GetRunDelegateCtor(Type delegateType) {
75 Func<LightLambda, Delegate> fastCtor;
76 if (_runCache.TryGetValue(delegateType, out fastCtor)) {
79 return MakeRunDelegateCtor(delegateType);
83 private static Func<LightLambda, Delegate> MakeRunDelegateCtor(Type delegateType) {
84 var method = delegateType.GetMethod("Invoke");
85 var paramInfos = method.GetParameters();
89 if (paramInfos.Length >= MaxParameters) {
93 if (method.ReturnType == typeof(void)) {
95 paramTypes = new Type[paramInfos.Length];
97 paramTypes = new Type[paramInfos.Length + 1];
98 paramTypes[paramTypes.Length - 1] = method.ReturnType;
101 MethodInfo runMethod;
103 if (method.ReturnType == typeof(void) && paramTypes.Length == 2 &&
104 paramInfos[0].ParameterType.IsByRef && paramInfos[1].ParameterType.IsByRef) {
105 runMethod = typeof(LightLambda).GetMethod("RunVoidRef2", BindingFlags.NonPublic | BindingFlags.Instance);
106 paramTypes[0] = paramInfos[0].ParameterType.GetElementType();
107 paramTypes[1] = paramInfos[1].ParameterType.GetElementType();
108 } else if (method.ReturnType == typeof(void) && paramTypes.Length == 0) {
109 runMethod = typeof(LightLambda).GetMethod("RunVoid0", BindingFlags.NonPublic | BindingFlags.Instance);
111 for (int i = 0; i < paramInfos.Length; i++) {
112 paramTypes[i] = paramInfos[i].ParameterType;
113 if (paramTypes[i].IsByRef) {
118 if (DelegateHelpers.MakeDelegate(paramTypes) == delegateType) {
119 name = "Make" + name + paramInfos.Length;
121 MethodInfo ctorMethod = typeof(LightLambda).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(paramTypes);
122 return _runCache[delegateType] = (Func<LightLambda, Delegate>)ctorMethod.CreateDelegate(typeof(Func<LightLambda, Delegate>));
125 runMethod = typeof(LightLambda).GetMethod(name + paramInfos.Length, BindingFlags.NonPublic | BindingFlags.Instance);
128 #if FEATURE_LCG && !SILVERLIGHT && !WP75
130 DynamicMethod dm = new DynamicMethod("FastCtor", typeof(Delegate), new[] { typeof(LightLambda) }, typeof(LightLambda), true);
131 var ilgen = dm.GetILGenerator();
132 ilgen.Emit(OpCodes.Ldarg_0);
133 ilgen.Emit(OpCodes.Ldftn, runMethod.IsGenericMethodDefinition ? runMethod.MakeGenericMethod(paramTypes) : runMethod);
134 ilgen.Emit(OpCodes.Newobj, delegateType.GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
135 ilgen.Emit(OpCodes.Ret);
136 return _runCache[delegateType] = (Func<LightLambda, Delegate>)dm.CreateDelegate(typeof(Func<LightLambda, Delegate>));
137 } catch (SecurityException) {
141 // we don't have permission for restricted skip visibility dynamic methods, use the slower Delegate.CreateDelegate.
142 var targetMethod = runMethod.IsGenericMethodDefinition ? runMethod.MakeGenericMethod(paramTypes) : runMethod;
143 return _runCache[delegateType] = lambda => targetMethod.CreateDelegate(delegateType, lambda);
146 //TODO enable sharing of these custom delegates
147 private Delegate CreateCustomDelegate(Type delegateType) {
148 PerfTrack.NoteEvent(PerfTrack.Categories.Compiler, "Synchronously compiling a custom delegate");
150 var method = delegateType.GetMethod("Invoke");
151 var paramInfos = method.GetParameters();
152 var parameters = new ParameterExpression[paramInfos.Length];
153 var parametersAsObject = new Expression[paramInfos.Length];
154 for (int i = 0; i < paramInfos.Length; i++) {
155 ParameterExpression parameter = Expression.Parameter(paramInfos[i].ParameterType, paramInfos[i].Name);
156 parameters[i] = parameter;
157 parametersAsObject[i] = Expression.Convert(parameter, typeof(object));
160 var data = Expression.NewArrayInit(typeof(object), parametersAsObject);
161 var self = AstUtils.Constant(this);
162 var runMethod = typeof(LightLambda).GetMethod("Run");
163 var body = Expression.Convert(Expression.Call(self, runMethod, data), method.ReturnType);
164 var lambda = Expression.Lambda(delegateType, body, parameters);
165 return lambda.Compile();
168 internal Delegate MakeDelegate(Type delegateType) {
169 Func<LightLambda, Delegate> fastCtor = GetRunDelegateCtor(delegateType);
170 if (fastCtor != null) {
171 return fastCtor(this);
173 return CreateCustomDelegate(delegateType);
177 private bool TryGetCompiled() {
178 // Use the compiled delegate if available.
179 if (_delegateCreator.HasCompiled) {
180 _compiled = _delegateCreator.CreateCompiledDelegate(_closure);
182 // Send it to anyone who's interested.
183 var compileEvent = Compile;
184 if (compileEvent != null && _delegateCreator.SameDelegateType) {
185 compileEvent(this, new LightLambdaCompileEventArgs(_compiled));
191 // Don't lock here, it's a frequently hit path.
193 // There could be multiple threads racing, but that is okay.
194 // Two bad things can happen:
195 // * We miss decrements (some thread sets the counter forward)
196 // * We might enter the "if" branch more than once.
198 // The first is okay, it just means we take longer to compile.
199 // The second we explicitly guard against inside of Compile().
201 // We can't miss 0. The first thread that writes -1 must have read 0 and hence start compilation.
202 if (unchecked(_compilationThreshold--) == 0) {
204 if (PlatformAdaptationLayer.IsCompactFramework) {
205 _compilationThreshold = Int32.MaxValue;
209 if (_interpreter.CompileSynchronously) {
210 _delegateCreator.Compile(null);
211 return TryGetCompiled();
213 // Kick off the compile on another thread so this one can keep going
215 new Task(_delegateCreator.Compile, null).Start();
217 ThreadPool.QueueUserWorkItem(_delegateCreator.Compile, null);
225 private InterpretedFrame MakeFrame() {
226 return new InterpretedFrame(_interpreter, _closure);
229 internal void RunVoidRef2<T0, T1>(ref T0 arg0, ref T1 arg1) {
230 if (_compiled != null || TryGetCompiled()) {
231 ((ActionRef<T0, T1>)_compiled)(ref arg0, ref arg1);
235 // copy in and copy out for today...
236 var frame = MakeFrame();
237 frame.Data[0] = arg0;
238 frame.Data[1] = arg1;
239 var currentFrame = frame.Enter();
241 _interpreter.Run(frame);
243 frame.Leave(currentFrame);
244 arg0 = (T0)frame.Data[0];
245 arg1 = (T1)frame.Data[1];
250 public object Run(params object[] arguments) {
251 if (_compiled != null || TryGetCompiled()) {
253 return _compiled.DynamicInvoke(arguments);
254 } catch (TargetInvocationException e) {
255 throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
259 var frame = MakeFrame();
260 for (int i = 0; i < arguments.Length; i++) {
261 frame.Data[i] = arguments[i];
263 var currentFrame = frame.Enter();
265 _interpreter.Run(frame);
267 frame.Leave(currentFrame);