1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation.
\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
11 * You must not remove this notice, or any other, from this software.
\r
14 * ***************************************************************************/
\r
15 using System; using Microsoft;
\r
18 using System.Diagnostics;
\r
20 using System.Dynamic.Utils;
\r
22 using Microsoft.Scripting.Utils;
\r
24 using System.Reflection;
\r
25 using System.Reflection.Emit;
\r
26 using System.Runtime.CompilerServices;
\r
28 using Microsoft.Runtime.CompilerServices;
31 using System.Threading;
\r
34 namespace System.Linq.Expressions.Compiler {
\r
36 namespace Microsoft.Linq.Expressions.Compiler {
\r
40 /// Dynamic Language Runtime Compiler.
\r
41 /// This part compiles lambdas.
\r
43 partial class LambdaCompiler {
\r
44 private static int _Counter;
\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
62 _ilg.Emit(OpCodes.Ldsfld, fb);
\r
64 _ilg.EmitArray(array);
\r
68 private void EmitClosureCreation(LambdaCompiler inner) {
\r
69 bool closure = inner._scope.NeedsClosure;
\r
70 bool boundConstants = inner._boundConstants.Count > 0;
\r
72 if (!closure && !boundConstants) {
\r
77 // new Closure(constantPool, currentHoistedLocals)
\r
78 if (boundConstants) {
\r
79 _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[]));
\r
84 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
\r
88 _ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) }));
\r
92 /// Emits code which creates new instance of the delegateType delegate.
\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
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
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
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
119 /// <param name="lambda">Lambda for which to generate a delegate</param>
\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
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
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
137 // 3. emit the delegate creation in the outer lambda
\r
138 EmitDelegateConstruction(impl);
\r
141 private static Type[] GetParameterTypes(LambdaExpression lambda) {
\r
142 return lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
\r
145 private static string GetUniqueMethodName() {
\r
146 return "<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}lambda_method";
\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
156 /// Emits the lambda body. If inlined, the parameters should already be
\r
157 /// pushed onto the IL stack.
\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
164 private void EmitLambdaBody(CompilerScope parent, bool inlined, CompilationFlags flags) {
\r
165 _scope.Enter(this, parent);
\r
168 // The arguments were already pushed onto the IL stack.
\r
169 // Store them into locals, popping in reverse order.
\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
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
183 EmitExpression(_lambda.Body, flags);
\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
190 _ilg.Emit(OpCodes.Ret);
\r
196 Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda);
\r
197 foreach (LabelInfo label in _labelInfo.Values) {
\r
198 label.ValidateFinish();
\r