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.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Reflection;
22 using System.Reflection.Emit;
23 using System.Runtime.CompilerServices;
24 using System.Threading;
27 namespace Microsoft.Scripting.Ast.Compiler {
29 namespace System.Linq.Expressions.Compiler {
31 #if !FEATURE_CORE_DLR || SILVERLIGHT
32 using ILGenerator = OffsetTrackingILGenerator;
36 /// LambdaCompiler is responsible for compiling individual lambda (LambdaExpression). The complete tree may
37 /// contain multiple lambdas, the Compiler class is reponsible for compiling the whole tree, individual
38 /// lambdas are then compiled by the LambdaCompiler.
40 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
41 internal sealed partial class LambdaCompiler {
43 private delegate void WriteBack();
45 // Information on the entire lambda tree currently being compiled
46 private readonly AnalyzedTree _tree;
48 private readonly ILGenerator _ilg;
51 // The TypeBuilder backing this method, if any
52 private readonly TypeBuilder _typeBuilder;
54 private readonly MethodInfo _method;
56 // Currently active LabelTargets and their mapping to IL labels
57 private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda);
58 // Mapping of labels used for "long" jumps (jumping out and into blocks)
59 private readonly Dictionary<LabelTarget, LabelInfo> _labelInfo = new Dictionary<LabelTarget, LabelInfo>();
61 // The currently active variable scope
62 private CompilerScope _scope;
64 // The lambda we are compiling
65 private readonly LambdaExpression _lambda;
67 // True if the method's first argument is of type Closure
68 private readonly bool _hasClosureArgument;
70 // True if we want to emitting debug symbols
71 private bool EmitDebugSymbols { get { return _tree.DebugInfoGenerator != null; } }
73 // Runtime constants bound to the delegate
74 private readonly BoundConstants _boundConstants;
76 // Free list of locals, so we reuse them rather than creating new ones
77 private readonly KeyedQueue<Type, LocalBuilder> _freeLocals = new KeyedQueue<Type, LocalBuilder>();
80 /// The value is true if a clearance was emitted and no new sequence point
81 /// has been emitted since that.
83 bool _sequencePointCleared;
86 /// Creates a lambda compiler that will compile to a dynamic method
88 private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda) {
89 Type[] parameterTypes = GetParameterTypes(lambda).AddFirst(typeof(Closure));
91 #if (SILVERLIGHT && CLR2) || WP75
92 var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes);
94 var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true);
101 #if !FEATURE_CORE_DLR || SILVERLIGHT
102 _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
104 _ilg = method.GetILGenerator();
107 _hasClosureArgument = true;
109 // These are populated by AnalyzeTree/VariableBinder
110 _scope = tree.Scopes[lambda];
111 _boundConstants = tree.Constants[lambda];
118 /// Creates a lambda compiler that will compile into the provided Methodbuilder
120 private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method) {
121 _hasClosureArgument = tree.Scopes[lambda].NeedsClosure;
122 Type[] paramTypes = GetParameterTypes(lambda);
123 if (_hasClosureArgument) {
124 paramTypes = paramTypes.AddFirst(typeof(Closure));
127 method.SetReturnType(lambda.ReturnType);
128 method.SetParameters(paramTypes);
129 var paramNames = lambda.Parameters.Map(p => p.Name);
130 // parameters are index from 1, with closure argument we need to skip the first arg
131 int startIndex = _hasClosureArgument ? 2 : 1;
132 for (int i = 0; i < paramNames.Length; i++) {
133 method.DefineParameter(i + startIndex, ParameterAttributes.None, paramNames[i]);
138 _typeBuilder = (TypeBuilder)method.DeclaringType;
141 #if !FEATURE_CORE_DLR || SILVERLIGHT
142 _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
144 _ilg = method.GetILGenerator();
147 // These are populated by AnalyzeTree/VariableBinder
148 _scope = tree.Scopes[lambda];
149 _boundConstants = tree.Constants[lambda];
156 /// Creates a lambda compiler for an inlined lambda
158 private LambdaCompiler(LambdaCompiler parent, LambdaExpression lambda) {
159 _tree = parent._tree;
161 _method = parent._method;
163 _hasClosureArgument = parent._hasClosureArgument;
164 _scope = _tree.Scopes[lambda];
165 _boundConstants = parent._boundConstants;
167 _typeBuilder = parent._typeBuilder;
171 private void InitializeMethod() {
172 // See if we can find a return label, so we can emit better IL
173 AddReturnLabel(_lambda);
174 _boundConstants.EmitCacheConstants(this);
177 public override string ToString() {
178 return _method.ToString();
181 internal ILGenerator IL {
185 internal ReadOnlyCollection<ParameterExpression> Parameters {
186 get { return _lambda.Parameters; }
189 internal bool CanEmitBoundConstants {
190 get { return _method is DynamicMethod; }
193 #region Compiler entry points
196 /// Compiler entry point
198 /// <param name="lambda">LambdaExpression to compile.</param>
199 /// <param name="debugInfoGenerator">Debug info generator.</param>
200 /// <returns>The compiled delegate.</returns>
201 internal static Delegate Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator) {
203 AnalyzedTree tree = AnalyzeLambda(ref lambda);
205 tree.DebugInfoGenerator = debugInfoGenerator;
207 // 2. Create lambda compiler
208 LambdaCompiler c = new LambdaCompiler(tree, lambda);
213 // 4. Return the delegate.
214 return c.CreateDelegate();
219 /// Mutates the MethodBuilder parameter, filling in IL, parameters,
222 /// (probably shouldn't be modifying parameters/return type...)
224 internal static void Compile(LambdaExpression lambda, MethodBuilder method, DebugInfoGenerator debugInfoGenerator) {
226 AnalyzedTree tree = AnalyzeLambda(ref lambda);
228 tree.DebugInfoGenerator = debugInfoGenerator;
230 // 2. Create lambda compiler
231 LambdaCompiler c = new LambdaCompiler(tree, lambda, method);
239 private static AnalyzedTree AnalyzeLambda(ref LambdaExpression lambda) {
240 // Spill the stack for any exception handling blocks or other
241 // constructs which require entering with an empty stack
242 lambda = StackSpiller.AnalyzeLambda(lambda);
244 // Bind any variable references in this lambda
245 return VariableBinder.Bind(lambda);
248 internal LocalBuilder GetLocal(Type type) {
249 Debug.Assert(type != null);
252 if (_freeLocals.TryDequeue(type, out local)) {
253 Debug.Assert(type == local.LocalType);
257 return _ilg.DeclareLocal(type);
260 internal void FreeLocal(LocalBuilder local) {
262 _freeLocals.Enqueue(local.LocalType, local);
266 internal LocalBuilder GetNamedLocal(Type type, ParameterExpression variable) {
267 Debug.Assert(type != null && variable != null);
269 LocalBuilder lb = _ilg.DeclareLocal(type);
270 if (EmitDebugSymbols && variable.Name != null) {
271 _tree.DebugInfoGenerator.SetLocalName(lb, variable.Name);
277 /// Gets the argument slot corresponding to the parameter at the given
278 /// index. Assumes that the method takes a certain number of prefix
279 /// arguments, followed by the real parameters stored in Parameters
281 internal int GetLambdaArgument(int index) {
282 return index + (_hasClosureArgument ? 1 : 0) + (_method.IsStatic ? 0 : 1);
286 /// Returns the index-th argument. This method provides access to the actual arguments
287 /// defined on the lambda itself, and excludes the possible 0-th closure argument.
289 internal void EmitLambdaArgument(int index) {
290 _ilg.EmitLoadArg(GetLambdaArgument(index));
293 internal void EmitClosureArgument() {
294 Debug.Assert(_hasClosureArgument, "must have a Closure argument");
295 Debug.Assert(_method.IsStatic, "must be a static method");
299 private Delegate CreateDelegate() {
300 Debug.Assert(_method is DynamicMethod);
302 return _method.CreateDelegate(_lambda.Type, new Closure(_boundConstants.ToArray(), null));
306 private FieldBuilder CreateStaticField(string name, Type type) {
307 // We are emitting into someone else's type. We don't want name
308 // conflicts, so choose a long name that is unlikely to confict.
309 // Naming scheme chosen here is similar to what the C# compiler
311 return _typeBuilder.DefineField("<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}" + name, type, FieldAttributes.Static | FieldAttributes.Private);
315 /// Creates an unitialized field suitible for private implementation details
316 /// Works with DynamicMethods or TypeBuilders.
318 private MemberExpression CreateLazyInitializedField<T>(string name) {
320 if (!(_method is DynamicMethod)) {
321 return Expression.Field(null, CreateStaticField(name, typeof(T)));
324 return Expression.Field(Expression.Constant(new StrongBox<T>(default(T))), "Value");