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