1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
19 using ILGenerator = System.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
21 using ILGenerator = Microsoft.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
24 using System.Collections.Generic;
25 using System.Collections.ObjectModel;
26 using System.Diagnostics;
28 using System.Dynamic.Utils;
30 using Microsoft.Scripting.Utils;
32 using System.Reflection;
33 using System.Reflection.Emit;
34 using System.Runtime.CompilerServices;
36 using Microsoft.Runtime.CompilerServices;
39 using System.Threading;
42 namespace System.Linq.Expressions.Compiler {
44 namespace Microsoft.Linq.Expressions.Compiler {
48 /// LambdaCompiler is responsible for compiling individual lambda (LambdaExpression). The complete tree may
49 /// contain multiple lambdas, the Compiler class is reponsible for compiling the whole tree, individual
50 /// lambdas are then compiled by the LambdaCompiler.
52 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
53 internal sealed partial class LambdaCompiler {
55 private delegate void WriteBack();
57 // Information on the entire lambda tree currently being compiled
58 private readonly AnalyzedTree _tree;
60 private readonly ILGenerator _ilg;
62 // The TypeBuilder backing this method, if any
63 private readonly TypeBuilder _typeBuilder;
65 private readonly MethodInfo _method;
67 // Currently active LabelTargets and their mapping to IL labels
68 private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda);
69 // Mapping of labels used for "long" jumps (jumping out and into blocks)
70 private readonly Dictionary<LabelTarget, LabelInfo> _labelInfo = new Dictionary<LabelTarget, LabelInfo>();
72 // The currently active variable scope
73 private CompilerScope _scope;
75 // The lambda we are compiling
76 private readonly LambdaExpression _lambda;
78 // True if the method's first argument is of type Closure
79 private readonly bool _hasClosureArgument;
81 // True if we want to emitting debug symbols
82 private bool EmitDebugSymbols { get { return _tree.DebugInfoGenerator != null; } }
84 // Runtime constants bound to the delegate
85 private readonly BoundConstants _boundConstants;
87 // Free list of locals, so we reuse them rather than creating new ones
88 private readonly KeyedQueue<Type, LocalBuilder> _freeLocals = new KeyedQueue<Type, LocalBuilder>();
91 /// The value is true if a clearance was emitted and no new sequence point
92 /// has been emitted since that.
94 bool _sequencePointCleared;
97 /// Creates a lambda compiler that will compile to a dynamic method
99 private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda) {
100 Type[] parameterTypes = GetParameterTypes(lambda).AddFirst(typeof(Closure));
103 var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes);
105 var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true);
112 _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
114 _hasClosureArgument = true;
116 // These are populated by AnalyzeTree/VariableBinder
117 _scope = tree.Scopes[lambda];
118 _boundConstants = tree.Constants[lambda];
124 /// Creates a lambda compiler that will compile into the provided Methodbuilder
126 private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method) {
127 _hasClosureArgument = tree.Scopes[lambda].NeedsClosure;
128 Type[] paramTypes = GetParameterTypes(lambda);
129 if (_hasClosureArgument) {
130 paramTypes = paramTypes.AddFirst(typeof(Closure));
133 method.SetReturnType(lambda.ReturnType);
134 method.SetParameters(paramTypes);
135 var paramNames = lambda.Parameters.Map(p => p.Name);
136 // parameters are index from 1, with closure argument we need to skip the first arg
137 int startIndex = _hasClosureArgument ? 2 : 1;
138 for (int i = 0; i < paramNames.Length; i++) {
139 method.DefineParameter(i + startIndex, ParameterAttributes.None, paramNames[i]);
144 _typeBuilder = (TypeBuilder)method.DeclaringType;
147 _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
149 // These are populated by AnalyzeTree/VariableBinder
150 _scope = tree.Scopes[lambda];
151 _boundConstants = tree.Constants[lambda];
157 /// Creates a lambda compiler for an inlined lambda
159 private LambdaCompiler(LambdaCompiler parent, LambdaExpression lambda) {
160 _tree = parent._tree;
162 _method = parent._method;
164 _hasClosureArgument = parent._hasClosureArgument;
165 _typeBuilder = parent._typeBuilder;
166 _scope = _tree.Scopes[lambda];
167 _boundConstants = parent._boundConstants;
170 private void InitializeMethod() {
171 // See if we can find a return label, so we can emit better IL
172 AddReturnLabel(_lambda);
173 _boundConstants.EmitCacheConstants(this);
176 public override string ToString() {
177 return _method.ToString();
180 internal ILGenerator IL {
184 internal ReadOnlyCollection<ParameterExpression> Parameters {
185 get { return _lambda.Parameters; }
188 internal bool CanEmitBoundConstants {
189 get { return _method is DynamicMethod; }
192 #region Compiler entry points
195 /// Compiler entry point
197 /// <param name="lambda">LambdaExpression to compile.</param>
198 /// <param name="debugInfoGenerator">Debug info generator.</param>
199 /// <returns>The compiled delegate.</returns>
200 internal static Delegate Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator) {
202 AnalyzedTree tree = AnalyzeLambda(ref lambda);
204 tree.DebugInfoGenerator = debugInfoGenerator;
206 // 2. Create lambda compiler
207 LambdaCompiler c = new LambdaCompiler(tree, lambda);
212 // 4. Return the delegate.
213 return c.CreateDelegate();
217 /// Mutates the MethodBuilder parameter, filling in IL, parameters,
220 /// (probably shouldn't be modifying parameters/return type...)
222 internal static void Compile(LambdaExpression lambda, MethodBuilder method, DebugInfoGenerator debugInfoGenerator) {
224 AnalyzedTree tree = AnalyzeLambda(ref lambda);
226 tree.DebugInfoGenerator = debugInfoGenerator;
228 // 2. Create lambda compiler
229 LambdaCompiler c = new LambdaCompiler(tree, lambda, method);
237 private static AnalyzedTree AnalyzeLambda(ref LambdaExpression lambda) {
238 // Spill the stack for any exception handling blocks or other
239 // constructs which require entering with an empty stack
240 lambda = StackSpiller.AnalyzeLambda(lambda);
242 // Bind any variable references in this lambda
243 return VariableBinder.Bind(lambda);
246 internal LocalBuilder GetLocal(Type type) {
247 Debug.Assert(type != null);
250 if (_freeLocals.TryDequeue(type, out local)) {
251 Debug.Assert(type == local.LocalType);
255 return _ilg.DeclareLocal(type);
258 internal void FreeLocal(LocalBuilder local) {
260 _freeLocals.Enqueue(local.LocalType, local);
264 internal LocalBuilder GetNamedLocal(Type type, ParameterExpression variable) {
265 Debug.Assert(type != null && variable != null);
267 LocalBuilder lb = _ilg.DeclareLocal(type);
268 if (EmitDebugSymbols && variable.Name != null) {
269 _tree.DebugInfoGenerator.SetLocalName(lb, variable.Name);
275 /// Gets the argument slot corresponding to the parameter at the given
276 /// index. Assumes that the method takes a certain number of prefix
277 /// arguments, followed by the real parameters stored in Parameters
279 internal int GetLambdaArgument(int index) {
280 return index + (_hasClosureArgument ? 1 : 0) + (_method.IsStatic ? 0 : 1);
284 /// Returns the index-th argument. This method provides access to the actual arguments
285 /// defined on the lambda itself, and excludes the possible 0-th closure argument.
287 internal void EmitLambdaArgument(int index) {
288 _ilg.EmitLoadArg(GetLambdaArgument(index));
291 internal void EmitClosureArgument() {
292 Debug.Assert(_hasClosureArgument, "must have a Closure argument");
293 Debug.Assert(_method.IsStatic, "must be a static method");
297 private Delegate CreateDelegate() {
298 Debug.Assert(_method is DynamicMethod);
300 return _method.CreateDelegate(_lambda.Type, new Closure(_boundConstants.ToArray(), null));
303 private FieldBuilder CreateStaticField(string name, Type type) {
304 // We are emitting into someone else's type. We don't want name
305 // conflicts, so choose a long name that is unlikely to confict.
306 // Naming scheme chosen here is similar to what the C# compiler
308 return _typeBuilder.DefineField("<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}" + name, type, FieldAttributes.Static | FieldAttributes.Private);
312 /// Creates an unitialized field suitible for private implementation details
313 /// Works with DynamicMethods or TypeBuilders.
315 private MemberExpression CreateLazyInitializedField<T>(string name) {
316 if (_method is DynamicMethod) {
317 return Expression.Field(Expression.Constant(new StrongBox<T>()), "Value");
319 return Expression.Field(null, CreateStaticField(name, typeof(T)));