Merge pull request #819 from brendanzagaeski/patch-1
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / LightLambda.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if FEATURE_TASKS
17 using System.Threading.Tasks;
18 #endif
19
20 #if FEATURE_CORE_DLR
21 using System.Linq.Expressions;
22 #else
23 using Microsoft.Scripting.Ast;
24 #endif
25
26 using System;
27 using System.Reflection;
28 #if FEATURE_REFEMIT
29 using System.Reflection.Emit;
30 #endif
31 using System.Runtime.CompilerServices;
32 using System.Security;
33 using System.Threading;
34
35 using Microsoft.Scripting.Generation;
36 using Microsoft.Scripting.Runtime;
37 using Microsoft.Scripting.Utils;
38
39 using AstUtils = Microsoft.Scripting.Ast.Utils;
40
41 namespace Microsoft.Scripting.Interpreter {
42
43     public sealed class LightLambdaCompileEventArgs : EventArgs {
44         public Delegate Compiled { get; private set; }
45
46         internal LightLambdaCompileEventArgs(Delegate compiled) {
47             Compiled = compiled;
48         }
49     }
50
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);
55
56         // Adaptive compilation support
57         private readonly LightDelegateCreator _delegateCreator;
58         private Delegate _compiled;
59         private int _compilationThreshold;
60
61         /// <summary>
62         /// Provides notification that the LightLambda has been compiled.
63         /// </summary>
64         public event EventHandler<LightLambdaCompileEventArgs> Compile;
65
66         internal LightLambda(LightDelegateCreator delegateCreator, StrongBox<object>[] closure, int compilationThreshold) {
67             _delegateCreator = delegateCreator;
68             _closure = closure;
69             _interpreter = delegateCreator.Interpreter;
70             _compilationThreshold = compilationThreshold;
71         }
72
73         private static Func<LightLambda, Delegate> GetRunDelegateCtor(Type delegateType) {
74             lock (_runCache) {
75                 Func<LightLambda, Delegate> fastCtor;
76                 if (_runCache.TryGetValue(delegateType, out fastCtor)) {
77                     return fastCtor;
78                 }
79                 return MakeRunDelegateCtor(delegateType);
80             }
81         }
82
83         private static Func<LightLambda, Delegate> MakeRunDelegateCtor(Type delegateType) {
84             var method = delegateType.GetMethod("Invoke");
85             var paramInfos = method.GetParameters();
86             Type[] paramTypes;
87             string name = "Run";
88
89             if (paramInfos.Length >= MaxParameters) {
90                 return null;
91             }
92
93             if (method.ReturnType == typeof(void)) {
94                 name += "Void";
95                 paramTypes = new Type[paramInfos.Length];
96             } else {
97                 paramTypes = new Type[paramInfos.Length + 1];
98                 paramTypes[paramTypes.Length - 1] = method.ReturnType;
99             }
100
101             MethodInfo runMethod;
102
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);
110             } else {
111                 for (int i = 0; i < paramInfos.Length; i++) {
112                     paramTypes[i] = paramInfos[i].ParameterType;
113                     if (paramTypes[i].IsByRef) {
114                         return null;
115                     }
116                 }
117
118                 if (DelegateHelpers.MakeDelegate(paramTypes) == delegateType) {
119                     name = "Make" + name + paramInfos.Length;
120                     
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>));
123                 }
124
125                 runMethod = typeof(LightLambda).GetMethod(name + paramInfos.Length, BindingFlags.NonPublic | BindingFlags.Instance);
126             }
127
128 #if FEATURE_LCG && !SILVERLIGHT && !WP75
129             try {
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) {
138             }
139 #endif
140
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);
144         }
145     
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");
149
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));
158             }
159
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();
166         }
167
168         internal Delegate MakeDelegate(Type delegateType) {            
169             Func<LightLambda, Delegate> fastCtor = GetRunDelegateCtor(delegateType);
170             if (fastCtor != null) {
171                 return fastCtor(this);
172             } else {
173                 return CreateCustomDelegate(delegateType);
174             }
175         }
176
177         private bool TryGetCompiled() {
178             // Use the compiled delegate if available.
179             if (_delegateCreator.HasCompiled) {
180                 _compiled = _delegateCreator.CreateCompiledDelegate(_closure);
181
182                 // Send it to anyone who's interested.
183                 var compileEvent = Compile;
184                 if (compileEvent != null && _delegateCreator.SameDelegateType) {
185                     compileEvent(this, new LightLambdaCompileEventArgs(_compiled));
186                 }
187
188                 return true;
189             }
190
191             // Don't lock here, it's a frequently hit path.
192             //
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.
197             //
198             // The first is okay, it just means we take longer to compile.
199             // The second we explicitly guard against inside of Compile().
200             //
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) {
203 #if SILVERLIGHT
204                 if (PlatformAdaptationLayer.IsCompactFramework) {
205                     _compilationThreshold = Int32.MaxValue;
206                     return false;
207                 }
208 #endif
209                 if (_interpreter.CompileSynchronously) {
210                     _delegateCreator.Compile(null);
211                     return TryGetCompiled();
212                 } else {
213                     // Kick off the compile on another thread so this one can keep going
214 #if FEATURE_TASKS
215                     new Task(_delegateCreator.Compile, null).Start();
216 #else
217                     ThreadPool.QueueUserWorkItem(_delegateCreator.Compile, null);
218 #endif
219                 }
220             }
221
222             return false;
223         }
224
225         private InterpretedFrame MakeFrame() {
226             return new InterpretedFrame(_interpreter, _closure);
227         }
228
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);
232                 return;
233             }
234
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();
240             try {
241                 _interpreter.Run(frame);
242             } finally {
243                 frame.Leave(currentFrame);
244                 arg0 = (T0)frame.Data[0];
245                 arg1 = (T1)frame.Data[1];
246             }
247         }
248
249         
250         public object Run(params object[] arguments) {
251             if (_compiled != null || TryGetCompiled()) {
252                 try {
253                     return _compiled.DynamicInvoke(arguments);
254                 } catch (TargetInvocationException e) {
255                     throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
256                 }
257             }
258
259             var frame = MakeFrame();
260             for (int i = 0; i < arguments.Length; i++) {
261                 frame.Data[i] = arguments[i];
262             }
263             var currentFrame = frame.Enter();
264             try {
265                 _interpreter.Run(frame);
266             } finally {
267                 frame.Leave(currentFrame);
268             }
269             return frame.Pop();
270         }
271     }
272 }