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
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
25 namespace Microsoft.Scripting.Ast.Compiler {
\r
27 namespace System.Linq.Expressions.Compiler {
\r
31 /// Dynamic Language Runtime Compiler.
\r
32 /// This part compiles lambdas.
\r
34 partial class LambdaCompiler {
\r
35 private static int _Counter;
\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
53 _ilg.Emit(OpCodes.Ldsfld, fb);
\r
55 _ilg.EmitArray(array);
\r
59 private void EmitClosureCreation(LambdaCompiler inner) {
\r
60 bool closure = inner._scope.NeedsClosure;
\r
61 bool boundConstants = inner._boundConstants.Count > 0;
\r
63 if (!closure && !boundConstants) {
\r
68 // new Closure(constantPool, currentHoistedLocals)
\r
69 if (boundConstants) {
\r
70 _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[]));
\r
75 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
\r
79 _ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) }));
\r
83 /// Emits code which creates new instance of the delegateType delegate.
\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
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
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
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
110 /// <param name="lambda">Lambda for which to generate a delegate</param>
\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
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
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
128 // 3. emit the delegate creation in the outer lambda
\r
129 EmitDelegateConstruction(impl);
\r
132 private static Type[] GetParameterTypes(LambdaExpression lambda) {
\r
133 return lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
\r
136 private static string GetUniqueMethodName() {
\r
137 return "<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}lambda_method";
\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
147 /// Emits the lambda body. If inlined, the parameters should already be
\r
148 /// pushed onto the IL stack.
\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
155 private void EmitLambdaBody(CompilerScope parent, bool inlined, CompilationFlags flags) {
\r
156 _scope.Enter(this, parent);
\r
159 // The arguments were already pushed onto the IL stack.
\r
160 // Store them into locals, popping in reverse order.
\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
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
174 EmitExpression(_lambda.Body, flags);
\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
181 _ilg.Emit(OpCodes.Ret);
\r
187 Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda);
\r
188 foreach (LabelInfo label in _labelInfo.Values) {
\r
189 label.ValidateFinish();
\r