[arm64] Fix finally abort
[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 #if MONO_INTERPRETER
59         const Delegate _compiled = null;
60 #else
61         private Delegate _compiled;
62 #endif
63         private int _compilationThreshold;
64
65 #if !MONO_INTERPRETER
66         /// <summary>
67         /// Provides notification that the LightLambda has been compiled.
68         /// </summary>
69         public event EventHandler<LightLambdaCompileEventArgs> Compile;
70 #endif
71
72         internal LightLambda(LightDelegateCreator delegateCreator, StrongBox<object>[] closure, int compilationThreshold) {
73             _delegateCreator = delegateCreator;
74             _closure = closure;
75             _interpreter = delegateCreator.Interpreter;
76             _compilationThreshold = compilationThreshold;
77         }
78
79         private static Func<LightLambda, Delegate> GetRunDelegateCtor(Type delegateType) {
80             lock (_runCache) {
81                 Func<LightLambda, Delegate> fastCtor;
82                 if (_runCache.TryGetValue(delegateType, out fastCtor)) {
83                     return fastCtor;
84                 }
85                 return MakeRunDelegateCtor(delegateType);
86             }
87         }
88
89         private static Func<LightLambda, Delegate> MakeRunDelegateCtor(Type delegateType) {
90             var method = delegateType.GetMethod("Invoke");
91             var paramInfos = method.GetParameters();
92             Type[] paramTypes;
93             string name = "Run";
94
95             if (paramInfos.Length >= MaxParameters) {
96                 return null;
97             }
98
99             if (method.ReturnType == typeof(void)) {
100                 name += "Void";
101                 paramTypes = new Type[paramInfos.Length];
102             } else {
103                 paramTypes = new Type[paramInfos.Length + 1];
104                 paramTypes[paramTypes.Length - 1] = method.ReturnType;
105             }
106
107             MethodInfo runMethod;
108
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);
116             } else {
117                 for (int i = 0; i < paramInfos.Length; i++) {
118                     paramTypes[i] = paramInfos[i].ParameterType;
119                     if (paramTypes[i].IsByRef) {
120                         return null;
121                     }
122                 }
123
124                 if (DelegateHelpers.MakeDelegate(paramTypes) == delegateType) {
125                     name = "Make" + name + paramInfos.Length;
126                     
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>));
129                 }
130
131                 runMethod = typeof(LightLambda).GetMethod(name + paramInfos.Length, BindingFlags.NonPublic | BindingFlags.Instance);
132             }
133
134 #if FEATURE_LCG && !SILVERLIGHT && !WP75
135             try {
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) {
144             }
145 #endif
146
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);
150         }
151     
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");
155
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));
164             }
165
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();
172         }
173
174         internal Delegate MakeDelegate(Type delegateType) {            
175             Func<LightLambda, Delegate> fastCtor = GetRunDelegateCtor(delegateType);
176             if (fastCtor != null) {
177                 return fastCtor(this);
178             } else {
179                 return CreateCustomDelegate(delegateType);
180             }
181         }
182
183         private bool TryGetCompiled() {
184 #if !MONO_INTERPRETER
185             // Use the compiled delegate if available.
186             if (_delegateCreator.HasCompiled) {
187                 _compiled = _delegateCreator.CreateCompiledDelegate(_closure);
188
189                 // Send it to anyone who's interested.
190                 var compileEvent = Compile;
191                 if (compileEvent != null && _delegateCreator.SameDelegateType) {
192                     compileEvent(this, new LightLambdaCompileEventArgs(_compiled));
193                 }
194
195                 return true;
196             }
197
198             // Don't lock here, it's a frequently hit path.
199             //
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.
204             //
205             // The first is okay, it just means we take longer to compile.
206             // The second we explicitly guard against inside of Compile().
207             //
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) {
210 #if SILVERLIGHT
211                 if (PlatformAdaptationLayer.IsCompactFramework) {
212                     _compilationThreshold = Int32.MaxValue;
213                     return false;
214                 }
215 #endif
216                 if (_interpreter.CompileSynchronously) {
217                     _delegateCreator.Compile(null);
218                     return TryGetCompiled();
219                 } else {
220                     // Kick off the compile on another thread so this one can keep going
221 #if FEATURE_TASKS
222                     new Task(_delegateCreator.Compile, null).Start();
223 #else
224                     ThreadPool.QueueUserWorkItem(_delegateCreator.Compile, null);
225 #endif
226                 }
227             }
228 #endif
229             return false;
230         }
231
232         private InterpretedFrame MakeFrame() {
233             return new InterpretedFrame(_interpreter, _closure);
234         }
235
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);
239                 return;
240             }
241
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();
247             try {
248                 _interpreter.Run(frame);
249             } finally {
250                 frame.Leave(currentFrame);
251                 arg0 = (T0)frame.Data[0];
252                 arg1 = (T1)frame.Data[1];
253             }
254         }
255
256 #if !MONO_INTERPRETER
257         public object Run(params object[] arguments) {
258             if (_compiled != null || TryGetCompiled()) {
259                 try {
260                     return _compiled.DynamicInvoke(arguments);
261                 } catch (TargetInvocationException e) {
262                     throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
263                 }
264             }
265
266             var frame = MakeFrame();
267             for (int i = 0; i < arguments.Length; i++) {
268                 frame.Data[i] = arguments[i];
269             }
270             var currentFrame = frame.Enter();
271             try {
272                 _interpreter.Run(frame);
273             } finally {
274                 frame.Leave(currentFrame);
275             }
276             return frame.Pop();
277         }
278 #endif
279     }
280 }