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