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