[arm64] Fix finally abort
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / LightDelegateCreator.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_CORE_DLR
17 using System.Linq.Expressions;
18 using Microsoft.Scripting.Ast;
19 #else
20 using Microsoft.Scripting.Ast;
21 #endif
22
23 using System;
24 using System.Collections.Generic;
25 using System.Diagnostics;
26 using System.Runtime.CompilerServices;
27 using System.Threading;
28 using Microsoft.Scripting.Generation;
29 using Microsoft.Scripting.Utils;
30
31 namespace Microsoft.Scripting.Interpreter {
32     
33     /// <summary>
34     /// Manages creation of interpreted delegates. These delegates will get
35     /// compiled if they are executed often enough.
36     /// </summary>
37     internal sealed class LightDelegateCreator {
38         // null if we are forced to compile
39         private readonly Interpreter _interpreter;
40         private readonly Expression _lambda;
41
42 #if !MONO_INTERPRETER
43         // Adaptive compilation support:
44         private Type _compiledDelegateType;
45         private Delegate _compiled;
46         private readonly object _compileLock = new object();
47 #endif
48         internal LightDelegateCreator(Interpreter interpreter, LambdaExpression lambda) {
49             Assert.NotNull(lambda);
50             _interpreter = interpreter;
51             _lambda = lambda;
52         }
53
54         internal LightDelegateCreator(Interpreter interpreter, LightLambdaExpression lambda) {
55             Assert.NotNull(lambda);
56             _interpreter = interpreter;
57             _lambda = lambda;
58         }
59
60         internal Interpreter Interpreter {
61             get { return _interpreter; }
62         }
63
64         private bool HasClosure {
65             get { return _interpreter != null && _interpreter.ClosureSize > 0; }
66         }
67 #if !MONO_INTERPRETER
68         internal bool HasCompiled {
69             get { return _compiled != null; }
70         }
71
72         /// <summary>
73         /// true if the compiled delegate has the same type as the lambda;
74         /// false if the type was changed for interpretation.
75         /// </summary>
76         internal bool SameDelegateType {
77             get { return _compiledDelegateType == DelegateType; }
78         }
79 #endif
80         internal Delegate CreateDelegate() {
81             return CreateDelegate(null);
82         }
83
84         internal Delegate CreateDelegate(StrongBox<object>[] closure) {
85 #if !MONO_INTERPRETER
86             if (_compiled != null) {
87                 // If the delegate type we want is not a Func/Action, we can't
88                 // use the compiled code directly. So instead just fall through
89                 // and create an interpreted LightLambda, which will pick up
90                 // the compiled delegate on its first run.
91                 //
92                 // Ideally, we would just rebind the compiled delegate using
93                 // Delegate.CreateDelegate. Unfortunately, it doesn't work on
94                 // dynamic methods.
95                 if (SameDelegateType) {
96                     return CreateCompiledDelegate(closure);
97                 }
98             }
99
100             if (_interpreter == null) {
101                 // We can't interpret, so force a compile
102                 Compile(null);
103                 Delegate compiled = CreateCompiledDelegate(closure);
104                 Debug.Assert(compiled.GetType() == DelegateType);
105                 return compiled;
106             }
107 #endif
108             // Otherwise, we'll create an interpreted LightLambda
109             return new LightLambda(this, closure, _interpreter._compilationThreshold).MakeDelegate(DelegateType);
110         }
111
112         private Type DelegateType {
113             get {
114                 LambdaExpression le = _lambda as LambdaExpression;
115                 if (le != null) {
116                     return le.Type;
117                 }
118
119                 return ((LightLambdaExpression)_lambda).Type;
120             }
121         }
122
123 #if !MONO_INTERPRETER
124         /// <summary>
125         /// Used by LightLambda to get the compiled delegate.
126         /// </summary>
127         internal Delegate CreateCompiledDelegate(StrongBox<object>[] closure) {
128             Debug.Assert(HasClosure == (closure != null));
129
130             if (HasClosure) {
131                 // We need to apply the closure to get the actual delegate.
132                 var applyClosure = (Func<StrongBox<object>[], Delegate>)_compiled;
133                 return applyClosure(closure);
134             }
135             return _compiled;
136         }
137
138         /// <summary>
139         /// Create a compiled delegate for the LightLambda, and saves it so
140         /// future calls to Run will execute the compiled code instead of
141         /// interpreting.
142         /// </summary>
143         internal void Compile(object state) {
144             if (_compiled != null) {
145                 return;
146             }
147
148             // Compilation is expensive, we only want to do it once.
149             lock (_compileLock) {
150                 if (_compiled != null) {
151                     return;
152                 }
153
154                 PerfTrack.NoteEvent(PerfTrack.Categories.Compiler, "Interpreted lambda compiled");
155                 
156                 // Interpreter needs a standard delegate type.
157                 // So change the lambda's delegate type to Func<...> or
158                 // Action<...> so it can be called from the LightLambda.Run
159                 // methods.
160                 LambdaExpression lambda = (_lambda as LambdaExpression) ?? (LambdaExpression)((LightLambdaExpression)_lambda).Reduce();
161                 if (_interpreter != null) {
162                     _compiledDelegateType = GetFuncOrAction(lambda);
163                     lambda = Expression.Lambda(_compiledDelegateType, lambda.Body, lambda.Name, lambda.Parameters);
164                 }
165
166                 if (HasClosure) {
167                     _compiled = LightLambdaClosureVisitor.BindLambda(lambda, _interpreter.ClosureVariables);
168                 } else {
169                     _compiled = lambda.Compile();
170                 }
171             }
172         }
173
174         private static Type GetFuncOrAction(LambdaExpression lambda) {
175             Type delegateType;
176             bool isVoid = lambda.ReturnType == typeof(void);
177
178             if (isVoid && lambda.Parameters.Count == 2 &&
179                 lambda.Parameters[0].IsByRef && lambda.Parameters[1].IsByRef) {
180                 return typeof(ActionRef<,>).MakeGenericType(lambda.Parameters.Map(p => p.Type));
181             } else {
182                 Type[] types = lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
183                 if (isVoid) {
184                     if (Expression.TryGetActionType(types, out delegateType)) {
185                         return delegateType;
186                     }
187                 } else {
188                     types = types.AddLast(lambda.ReturnType);
189                     if (Expression.TryGetFuncType(types, out delegateType)) {
190                         return delegateType;
191                     }
192                 }
193                 return lambda.Type;
194             }
195         }
196 #endif
197     }
198 }