| File: System\Linq\Expressions\Compiler\LambdaCompiler.Lambda.cs | Web Access |
| Project: src\runtime\src\libraries\System.Linq.Expressions\src\System.Linq.Expressions.csproj (System.Linq.Expressions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using static System.Linq.Expressions.CachedReflectionInfo; namespace System.Linq.Expressions.Compiler { /// <summary> /// Dynamic Language Runtime Compiler. /// This part compiles lambdas. /// </summary> internal sealed partial class LambdaCompiler { #if FEATURE_COMPILE_TO_METHODBUILDER private static int s_counter; #endif internal void EmitConstantArray<T>(T[] array) { #if FEATURE_COMPILE_TO_METHODBUILDER // Emit as runtime constant if possible // if not, emit into IL if (_method is DynamicMethod) #else Debug.Assert(_method is DynamicMethod); #endif { EmitConstant(array, typeof(T[])); } #if FEATURE_COMPILE_TO_METHODBUILDER else if (_typeBuilder != null) { // store into field in our type builder, we will initialize // the value only once. FieldBuilder fb = CreateStaticField("ConstantArray", typeof(T[])); Label l = _ilg.DefineLabel(); _ilg.Emit(OpCodes.Ldsfld, fb); _ilg.Emit(OpCodes.Ldnull); _ilg.Emit(OpCodes.Bne_Un, l); _ilg.EmitArray(array, this); _ilg.Emit(OpCodes.Stsfld, fb); _ilg.MarkLabel(l); _ilg.Emit(OpCodes.Ldsfld, fb); } else { _ilg.EmitArray(array, this); } #endif } private void EmitClosureCreation(LambdaCompiler inner) { bool closure = inner._scope.NeedsClosure; bool boundConstants = inner._boundConstants.Count > 0; if (!closure && !boundConstants) { _ilg.EmitNull(); return; } // new Closure(constantPool, currentHoistedLocals) if (boundConstants) { _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[])); } else { _ilg.EmitNull(); } if (closure) { _scope.EmitGet(_scope.NearestHoistedLocals!.SelfVariable); } else { _ilg.EmitNull(); } _ilg.EmitNew(Closure_ObjectArray_ObjectArray); } /// <summary> /// Emits code which creates new instance of the delegateType delegate. /// /// Since the delegate is getting closed over the "Closure" argument, this /// cannot be used with virtual/instance methods (inner must be static method) /// </summary> private void EmitDelegateConstruction(LambdaCompiler inner) { Type delegateType = inner._lambda.Type; DynamicMethod? dynamicMethod = inner._method as DynamicMethod; #if FEATURE_COMPILE_TO_METHODBUILDER if (dynamicMethod != null) #else Debug.Assert(dynamicMethod != null); #endif { // Emit MethodInfo.CreateDelegate instead because DynamicMethod is not in Windows 8 Profile _boundConstants.EmitConstant(this, dynamicMethod, typeof(MethodInfo)); _ilg.EmitType(delegateType); EmitClosureCreation(inner); _ilg.Emit(OpCodes.Callvirt, MethodInfo_CreateDelegate_Type_Object); _ilg.Emit(OpCodes.Castclass, delegateType); } #if FEATURE_COMPILE_TO_METHODBUILDER else { // new DelegateType(closure) EmitClosureCreation(inner); _ilg.Emit(OpCodes.Ldftn, inner._method); _ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0])); } #endif } /// <summary> /// Emits a delegate to the method generated for the LambdaExpression. /// May end up creating a wrapper to match the requested delegate type. /// </summary> /// <param name="lambda">Lambda for which to generate a delegate</param> /// private void EmitDelegateConstruction(LambdaExpression lambda) { // 1. Create the new compiler LambdaCompiler impl; #if FEATURE_COMPILE_TO_METHODBUILDER if (_method is DynamicMethod) #else Debug.Assert(_method is DynamicMethod); #endif { impl = new LambdaCompiler(_tree, lambda); } #if FEATURE_COMPILE_TO_METHODBUILDER else { // When the lambda does not have a name or the name is empty, generate a unique name for it. string name = String.IsNullOrEmpty(lambda.Name) ? GetUniqueMethodName() : lambda.Name; MethodBuilder mb = _typeBuilder.DefineMethod(name, MethodAttributes.Private | MethodAttributes.Static); impl = new LambdaCompiler(_tree, lambda, mb); } #endif // 2. emit the lambda // Since additional ILs are always emitted after the lambda's body, should not emit with tail call optimization. impl.EmitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail); // 3. emit the delegate creation in the outer lambda EmitDelegateConstruction(impl); } private static Type[] GetParameterTypes(LambdaExpression lambda, Type firstType) { int count = lambda.ParameterCount; Type[] result; int i; if (firstType != null) { result = new Type[count + 1]; result[0] = firstType; i = 1; } else { result = new Type[count]; i = 0; } for (var j = 0; j < count; j++, i++) { ParameterExpression p = lambda.GetParameter(j); result[i] = p.IsByRef ? p.Type.MakeByRefType() : p.Type; } return result; } #if FEATURE_COMPILE_TO_METHODBUILDER private static string GetUniqueMethodName() { return "<ExpressionCompilerImplementationDetails>{" + System.Threading.Interlocked.Increment(ref s_counter) + "}lambda_method"; } #endif private void EmitLambdaBody() { // The lambda body is the "last" expression of the lambda CompilationFlags tailCallFlag = _lambda.TailCall ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail; EmitLambdaBody(null, false, tailCallFlag); } /// <summary> /// Emits the lambda body. If inlined, the parameters should already be /// pushed onto the IL stack. /// </summary> /// <param name="parent">The parent scope.</param> /// <param name="inlined">true if the lambda is inlined; false otherwise.</param> /// <param name="flags"> /// The enum to specify if the lambda is compiled with the tail call optimization. /// </param> private void EmitLambdaBody(CompilerScope? parent, bool inlined, CompilationFlags flags) { _scope.Enter(this, parent); if (inlined) { // The arguments were already pushed onto the IL stack. // Store them into locals, popping in reverse order. // // If any arguments were ByRef, the address is on the stack and // we'll be storing it into the variable, which has a ref type. for (int i = _lambda.ParameterCount - 1; i >= 0; i--) { _scope.EmitSet(_lambda.GetParameter(i)); } } // Need to emit the expression start for the lambda body flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart); if (_lambda.ReturnType == typeof(void)) { EmitExpressionAsVoid(_lambda.Body, flags); } else { EmitExpression(_lambda.Body, flags); } // Return must be the last instruction in a CLI method. // But if we're inlining the lambda, we want to leave the return // value on the IL stack. if (!inlined) { _ilg.Emit(OpCodes.Ret); } _scope.Exit(); // Validate labels Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda); foreach (LabelInfo label in _labelInfo.Values) { label.ValidateFinish(); } } } }