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