2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.Lambda.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. \r
4  *\r
5  * This source code is subject to terms and conditions of the Microsoft Public License. A \r
6  * copy of the license can be found in the License.html file at the root of this distribution. If \r
7  * you cannot locate the  Microsoft Public License, please send an email to \r
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound \r
9  * by the terms of the Microsoft Public License.\r
10  *\r
11  * You must not remove this notice, or any other, from this software.\r
12  *\r
13  *\r
14  * ***************************************************************************/\r
15 using System; using Microsoft;\r
16 \r
17 \r
18 using System.Diagnostics;\r
19 #if CODEPLEX_40\r
20 using System.Dynamic.Utils;\r
21 #else\r
22 using Microsoft.Scripting.Utils;\r
23 #endif\r
24 using System.Reflection;\r
25 using System.Reflection.Emit;\r
26 using System.Runtime.CompilerServices;\r
27 #if !CODEPLEX_40
28 using Microsoft.Runtime.CompilerServices;
29 #endif
30 \r
31 using System.Threading;\r
32 \r
33 #if CODEPLEX_40\r
34 namespace System.Linq.Expressions.Compiler {\r
35 #else\r
36 namespace Microsoft.Linq.Expressions.Compiler {\r
37 #endif\r
38 \r
39     /// <summary>\r
40     /// Dynamic Language Runtime Compiler.\r
41     /// This part compiles lambdas.\r
42     /// </summary>\r
43     partial class LambdaCompiler {\r
44         private static int _Counter;\r
45 \r
46         internal void EmitConstantArray<T>(T[] array) {\r
47             // Emit as runtime constant if possible\r
48             // if not, emit into IL\r
49             if (_method is DynamicMethod) {\r
50                 EmitConstant(array, typeof(T[]));\r
51             } else if(_typeBuilder != null) {\r
52                 // store into field in our type builder, we will initialize\r
53                 // the value only once.\r
54                 FieldBuilder fb = CreateStaticField("ConstantArray", typeof(T[]));\r
55                 Label l = _ilg.DefineLabel();\r
56                 _ilg.Emit(OpCodes.Ldsfld, fb);\r
57                 _ilg.Emit(OpCodes.Ldnull);\r
58                 _ilg.Emit(OpCodes.Bne_Un, l);\r
59                 _ilg.EmitArray(array);\r
60                 _ilg.Emit(OpCodes.Stsfld, fb);\r
61                 _ilg.MarkLabel(l);\r
62                 _ilg.Emit(OpCodes.Ldsfld, fb);\r
63             } else { \r
64                 _ilg.EmitArray(array);\r
65             }\r
66         }\r
67 \r
68         private void EmitClosureCreation(LambdaCompiler inner) {\r
69             bool closure = inner._scope.NeedsClosure;\r
70             bool boundConstants = inner._boundConstants.Count > 0;\r
71 \r
72             if (!closure && !boundConstants) {\r
73                 _ilg.EmitNull();\r
74                 return;\r
75             }\r
76 \r
77             // new Closure(constantPool, currentHoistedLocals)\r
78             if (boundConstants) {\r
79                 _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[]));\r
80             } else {\r
81                 _ilg.EmitNull();\r
82             }\r
83             if (closure) {\r
84                 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);\r
85             } else {\r
86                 _ilg.EmitNull();\r
87             }\r
88             _ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) }));\r
89         }\r
90 \r
91         /// <summary>\r
92         /// Emits code which creates new instance of the delegateType delegate.\r
93         /// \r
94         /// Since the delegate is getting closed over the "Closure" argument, this\r
95         /// cannot be used with virtual/instance methods (inner must be static method)\r
96         /// </summary>\r
97         private void EmitDelegateConstruction(LambdaCompiler inner) {\r
98             Type delegateType = inner._lambda.Type;\r
99             DynamicMethod dynamicMethod = inner._method as DynamicMethod;\r
100             if (dynamicMethod != null) {\r
101                 // dynamicMethod.CreateDelegate(delegateType, closure)\r
102                 _boundConstants.EmitConstant(this, dynamicMethod, typeof(DynamicMethod));\r
103                 _ilg.EmitType(delegateType);\r
104                 EmitClosureCreation(inner);\r
105                 _ilg.Emit(OpCodes.Callvirt, typeof(DynamicMethod).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object) }));\r
106                 _ilg.Emit(OpCodes.Castclass, delegateType);\r
107             } else {\r
108                 // new DelegateType(closure)\r
109                 EmitClosureCreation(inner);\r
110                 _ilg.Emit(OpCodes.Ldftn, (MethodInfo)inner._method);\r
111                 _ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0]));\r
112             }\r
113         }\r
114 \r
115         /// <summary>\r
116         /// Emits a delegate to the method generated for the LambdaExpression.\r
117         /// May end up creating a wrapper to match the requested delegate type.\r
118         /// </summary>\r
119         /// <param name="lambda">Lambda for which to generate a delegate</param>\r
120         /// \r
121         private void EmitDelegateConstruction(LambdaExpression lambda) {\r
122             // 1. Create the new compiler\r
123             LambdaCompiler impl;\r
124             if (_method is DynamicMethod) {\r
125                 impl = new LambdaCompiler(_tree, lambda);\r
126             } else {\r
127                 // When the lambda does not have a name or the name is empty, generate a unique name for it.\r
128                 string name = String.IsNullOrEmpty(lambda.Name) ? GetUniqueMethodName() : lambda.Name;\r
129                 MethodBuilder mb = _typeBuilder.DefineMethod(name, MethodAttributes.Private | MethodAttributes.Static);\r
130                 impl = new LambdaCompiler(_tree, lambda, mb);\r
131             }\r
132 \r
133             // 2. emit the lambda\r
134             // Since additional ILs are always emitted after the lambda's body, should not emit with tail call optimization.\r
135             impl.EmitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail);\r
136 \r
137             // 3. emit the delegate creation in the outer lambda\r
138             EmitDelegateConstruction(impl);\r
139         }\r
140 \r
141         private static Type[] GetParameterTypes(LambdaExpression lambda) {\r
142             return lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);\r
143         }\r
144 \r
145         private static string GetUniqueMethodName() {\r
146             return "<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}lambda_method";\r
147         }\r
148 \r
149         private void EmitLambdaBody() {\r
150             // The lambda body is the "last" expression of the lambda\r
151             CompilationFlags tailCallFlag = _lambda.TailCall ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;\r
152             EmitLambdaBody(null, false, tailCallFlag);\r
153         }\r
154 \r
155         /// <summary>\r
156         /// Emits the lambda body. If inlined, the parameters should already be\r
157         /// pushed onto the IL stack.\r
158         /// </summary>\r
159         /// <param name="parent">The parent scope.</param>\r
160         /// <param name="inlined">true if the lambda is inlined; false otherwise.</param>\r
161         /// <param name="flags">\r
162         /// The emum to specify if the lambda is compiled with the tail call optimization. \r
163         /// </param>\r
164         private void EmitLambdaBody(CompilerScope parent, bool inlined, CompilationFlags flags) {\r
165             _scope.Enter(this, parent);\r
166 \r
167             if (inlined) {\r
168                 // The arguments were already pushed onto the IL stack.\r
169                 // Store them into locals, popping in reverse order.\r
170                 //\r
171                 // If any arguments were ByRef, the address is on the stack and\r
172                 // we'll be storing it into the variable, which has a ref type.\r
173                 for (int i = _lambda.Parameters.Count - 1; i >= 0; i--) {\r
174                     _scope.EmitSet(_lambda.Parameters[i]);\r
175                 }\r
176             }\r
177 \r
178             // Need to emit the expression start for the lambda body\r
179             flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);\r
180             if (_lambda.ReturnType == typeof(void)) {\r
181                 EmitExpressionAsVoid(_lambda.Body, flags);\r
182             } else {\r
183                 EmitExpression(_lambda.Body, flags);\r
184             }\r
185 \r
186             // Return must be the last instruction in a CLI method.\r
187             // But if we're inlining the lambda, we want to leave the return\r
188             // value on the IL stack.\r
189             if (!inlined) {\r
190                 _ilg.Emit(OpCodes.Ret);\r
191             }\r
192 \r
193             _scope.Exit();\r
194 \r
195             // Validate labels\r
196             Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda);\r
197             foreach (LabelInfo label in _labelInfo.Values) {\r
198                 label.ValidateFinish();\r
199             }\r
200         }\r
201     }\r
202 }\r