2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 #if CODEPLEX_40
19 using ILGenerator = System.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
20 #else
21 using ILGenerator = Microsoft.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
22 #endif
23
24 using System.Collections.Generic;
25 using System.Collections.ObjectModel;
26 using System.Diagnostics;
27 #if CODEPLEX_40
28 using System.Dynamic.Utils;
29 #else
30 using Microsoft.Scripting.Utils;
31 #endif
32 using System.Reflection;
33 using System.Reflection.Emit;
34 using System.Runtime.CompilerServices;
35 #if !CODEPLEX_40
36 using Microsoft.Runtime.CompilerServices;
37 #endif
38
39 using System.Threading;
40
41 #if CODEPLEX_40
42 namespace System.Linq.Expressions.Compiler {
43 #else
44 namespace Microsoft.Linq.Expressions.Compiler {
45 #endif
46
47     /// <summary>
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.
51     /// </summary>
52     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
53     internal sealed partial class LambdaCompiler {
54
55         private delegate void WriteBack();
56
57         // Information on the entire lambda tree currently being compiled
58         private readonly AnalyzedTree _tree;
59
60         private readonly ILGenerator _ilg;
61
62         // The TypeBuilder backing this method, if any
63         private readonly TypeBuilder _typeBuilder;
64
65         private readonly MethodInfo _method;
66
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>();
71
72         // The currently active variable scope
73         private CompilerScope _scope;
74
75         // The lambda we are compiling
76         private readonly LambdaExpression _lambda;
77
78         // True if the method's first argument is of type Closure
79         private readonly bool _hasClosureArgument;
80
81         // True if we want to emitting debug symbols
82         private bool EmitDebugSymbols { get { return _tree.DebugInfoGenerator != null; } }
83
84         // Runtime constants bound to the delegate
85         private readonly BoundConstants _boundConstants;
86
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>();
89
90         /// <summary>
91         /// The value is true if a clearance was emitted and no new sequence point
92         /// has been emitted since that.
93         /// </summary>
94         bool _sequencePointCleared;
95
96         /// <summary>
97         /// Creates a lambda compiler that will compile to a dynamic method
98         /// </summary>
99         private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda) {
100             Type[] parameterTypes = GetParameterTypes(lambda).AddFirst(typeof(Closure));
101
102 #if SILVERLIGHT
103             var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes);
104 #else
105             var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true);
106 #endif
107
108             _tree = tree;
109             _lambda = lambda;
110             _method = method;
111
112             _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
113
114             _hasClosureArgument = true;
115
116             // These are populated by AnalyzeTree/VariableBinder
117             _scope = tree.Scopes[lambda];
118             _boundConstants = tree.Constants[lambda];
119
120             InitializeMethod();
121         }
122
123         /// <summary>
124         /// Creates a lambda compiler that will compile into the provided Methodbuilder
125         /// </summary>
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));
131             }
132
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]);
140             }
141
142             _tree = tree;
143             _lambda = lambda;
144             _typeBuilder = (TypeBuilder)method.DeclaringType;
145             _method = method;
146
147             _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
148
149             // These are populated by AnalyzeTree/VariableBinder
150             _scope = tree.Scopes[lambda];
151             _boundConstants = tree.Constants[lambda];
152
153             InitializeMethod();
154         }
155
156         /// <summary>
157         /// Creates a lambda compiler for an inlined lambda
158         /// </summary>
159         private LambdaCompiler(LambdaCompiler parent, LambdaExpression lambda) {
160             _tree = parent._tree;
161             _lambda = lambda;
162             _method = parent._method;
163             _ilg = parent._ilg;
164             _hasClosureArgument = parent._hasClosureArgument;
165             _typeBuilder = parent._typeBuilder;
166             _scope = _tree.Scopes[lambda];
167             _boundConstants = parent._boundConstants;
168         }
169
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);
174         }
175
176         public override string ToString() {
177             return _method.ToString();
178         }
179
180         internal ILGenerator IL {
181             get { return _ilg; }
182         }
183
184         internal ReadOnlyCollection<ParameterExpression> Parameters {
185             get { return _lambda.Parameters; }
186         }
187
188         internal bool CanEmitBoundConstants {
189             get { return _method is DynamicMethod; }
190         }
191
192         #region Compiler entry points
193         
194         /// <summary>
195         /// Compiler entry point
196         /// </summary>
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) {
201             // 1. Bind lambda
202             AnalyzedTree tree = AnalyzeLambda(ref lambda);
203
204             tree.DebugInfoGenerator = debugInfoGenerator;
205             
206             // 2. Create lambda compiler
207             LambdaCompiler c = new LambdaCompiler(tree, lambda);
208
209             // 3. Emit
210             c.EmitLambdaBody();
211
212             // 4. Return the delegate.
213             return c.CreateDelegate();
214         }
215
216         /// <summary>
217         /// Mutates the MethodBuilder parameter, filling in IL, parameters,
218         /// and return type.
219         /// 
220         /// (probably shouldn't be modifying parameters/return type...)
221         /// </summary>
222         internal static void Compile(LambdaExpression lambda, MethodBuilder method, DebugInfoGenerator debugInfoGenerator) {
223             // 1. Bind lambda
224             AnalyzedTree tree = AnalyzeLambda(ref lambda);
225
226             tree.DebugInfoGenerator = debugInfoGenerator;
227             
228             // 2. Create lambda compiler
229             LambdaCompiler c = new LambdaCompiler(tree, lambda, method);
230
231             // 3. Emit
232             c.EmitLambdaBody();
233         }
234
235         #endregion
236
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);
241
242             // Bind any variable references in this lambda
243             return VariableBinder.Bind(lambda);
244         }
245
246         internal LocalBuilder GetLocal(Type type) {
247             Debug.Assert(type != null);
248
249             LocalBuilder local;
250             if (_freeLocals.TryDequeue(type, out local)) {
251                 Debug.Assert(type == local.LocalType);
252                 return local;
253             }
254
255             return _ilg.DeclareLocal(type);
256         }
257
258         internal void FreeLocal(LocalBuilder local) {
259             if (local != null) {
260                 _freeLocals.Enqueue(local.LocalType, local);
261             }
262         }
263
264         internal LocalBuilder GetNamedLocal(Type type, ParameterExpression variable) {
265             Debug.Assert(type != null && variable != null);
266
267             LocalBuilder lb = _ilg.DeclareLocal(type);
268             if (EmitDebugSymbols && variable.Name != null) {
269                 _tree.DebugInfoGenerator.SetLocalName(lb, variable.Name);
270             }
271             return lb;
272         }
273
274         /// <summary>
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
278         /// </summary>
279         internal int GetLambdaArgument(int index) {
280             return index + (_hasClosureArgument ? 1 : 0) + (_method.IsStatic ? 0 : 1);
281         }
282
283         /// <summary>
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.
286         /// </summary>
287         internal void EmitLambdaArgument(int index) {
288             _ilg.EmitLoadArg(GetLambdaArgument(index));
289         }
290
291         internal void EmitClosureArgument() {
292             Debug.Assert(_hasClosureArgument, "must have a Closure argument");
293             Debug.Assert(_method.IsStatic, "must be a static method");
294             _ilg.EmitLoadArg(0);
295         }
296
297         private Delegate CreateDelegate() {
298             Debug.Assert(_method is DynamicMethod);
299
300             return _method.CreateDelegate(_lambda.Type, new Closure(_boundConstants.ToArray(), null));
301         }
302
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
307             // uses.
308             return _typeBuilder.DefineField("<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}" + name, type, FieldAttributes.Static | FieldAttributes.Private);
309         }
310
311         /// <summary>
312         /// Creates an unitialized field suitible for private implementation details
313         /// Works with DynamicMethods or TypeBuilders.
314         /// </summary>
315         private MemberExpression CreateLazyInitializedField<T>(string name) {
316             if (_method is DynamicMethod) {
317                 return Expression.Field(Expression.Constant(new StrongBox<T>()), "Value");
318             } else {
319                 return Expression.Field(null, CreateStaticField(name, typeof(T)));
320             }
321         }
322     }
323 }