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.Linq.Expressions;
18 using Microsoft.Scripting.Ast;
20 using Microsoft.Scripting.Ast;
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;
31 namespace Microsoft.Scripting.Interpreter {
34 /// Manages creation of interpreted delegates. These delegates will get
35 /// compiled if they are executed often enough.
37 internal sealed class LightDelegateCreator {
38 // null if we are forced to compile
39 private readonly Interpreter _interpreter;
40 private readonly Expression _lambda;
43 // Adaptive compilation support:
44 private Type _compiledDelegateType;
45 private Delegate _compiled;
46 private readonly object _compileLock = new object();
48 internal LightDelegateCreator(Interpreter interpreter, LambdaExpression lambda) {
49 Assert.NotNull(lambda);
50 _interpreter = interpreter;
54 internal LightDelegateCreator(Interpreter interpreter, LightLambdaExpression lambda) {
55 Assert.NotNull(lambda);
56 _interpreter = interpreter;
60 internal Interpreter Interpreter {
61 get { return _interpreter; }
64 private bool HasClosure {
65 get { return _interpreter != null && _interpreter.ClosureSize > 0; }
68 internal bool HasCompiled {
69 get { return _compiled != null; }
73 /// true if the compiled delegate has the same type as the lambda;
74 /// false if the type was changed for interpretation.
76 internal bool SameDelegateType {
77 get { return _compiledDelegateType == DelegateType; }
80 internal Delegate CreateDelegate() {
81 return CreateDelegate(null);
84 internal Delegate CreateDelegate(StrongBox<object>[] closure) {
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.
92 // Ideally, we would just rebind the compiled delegate using
93 // Delegate.CreateDelegate. Unfortunately, it doesn't work on
95 if (SameDelegateType) {
96 return CreateCompiledDelegate(closure);
100 if (_interpreter == null) {
101 // We can't interpret, so force a compile
103 Delegate compiled = CreateCompiledDelegate(closure);
104 Debug.Assert(compiled.GetType() == DelegateType);
108 // Otherwise, we'll create an interpreted LightLambda
109 return new LightLambda(this, closure, _interpreter._compilationThreshold).MakeDelegate(DelegateType);
112 private Type DelegateType {
114 LambdaExpression le = _lambda as LambdaExpression;
119 return ((LightLambdaExpression)_lambda).Type;
123 #if !MONO_INTERPRETER
125 /// Used by LightLambda to get the compiled delegate.
127 internal Delegate CreateCompiledDelegate(StrongBox<object>[] closure) {
128 Debug.Assert(HasClosure == (closure != null));
131 // We need to apply the closure to get the actual delegate.
132 var applyClosure = (Func<StrongBox<object>[], Delegate>)_compiled;
133 return applyClosure(closure);
139 /// Create a compiled delegate for the LightLambda, and saves it so
140 /// future calls to Run will execute the compiled code instead of
143 internal void Compile(object state) {
144 if (_compiled != null) {
148 // Compilation is expensive, we only want to do it once.
149 lock (_compileLock) {
150 if (_compiled != null) {
154 PerfTrack.NoteEvent(PerfTrack.Categories.Compiler, "Interpreted lambda compiled");
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
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);
167 _compiled = LightLambdaClosureVisitor.BindLambda(lambda, _interpreter.ClosureVariables);
169 _compiled = lambda.Compile();
174 private static Type GetFuncOrAction(LambdaExpression lambda) {
176 bool isVoid = lambda.ReturnType == typeof(void);
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));
182 Type[] types = lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
184 if (Expression.TryGetActionType(types, out delegateType)) {
188 types = types.AddLast(lambda.ReturnType);
189 if (Expression.TryGetFuncType(types, out delegateType)) {