File: System\Linq\Expressions\Compiler\LambdaCompiler.cs
Web Access
Project: src\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.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic.Utils;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Threading;
 
namespace System.Linq.Expressions.Compiler
{
    internal interface ILocalCache
    {
        LocalBuilder GetLocal(Type type);
 
        void FreeLocal(LocalBuilder local);
    }
 
    /// <summary>
    /// LambdaCompiler is responsible for compiling individual lambda (LambdaExpression). The complete tree may
    /// contain multiple lambdas, the Compiler class is responsible for compiling the whole tree, individual
    /// lambdas are then compiled by the LambdaCompiler.
    /// </summary>
    [RequiresDynamicCode(Expression.LambdaCompilerRequiresDynamicCode)]
    internal sealed partial class LambdaCompiler : ILocalCache
    {
        private static int s_lambdaMethodIndex;
 
        private delegate void WriteBack(LambdaCompiler compiler);
 
        // Information on the entire lambda tree currently being compiled
        private readonly AnalyzedTree _tree;
 
        private readonly ILGenerator _ilg;
 
#if FEATURE_COMPILE_TO_METHODBUILDER
        // The TypeBuilder backing this method, if any
        private readonly TypeBuilder _typeBuilder;
#endif
 
        private readonly MethodInfo _method;
 
        // Currently active LabelTargets and their mapping to IL labels
        private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda);
        // Mapping of labels used for "long" jumps (jumping out and into blocks)
        private readonly Dictionary<LabelTarget, LabelInfo> _labelInfo = new Dictionary<LabelTarget, LabelInfo>();
 
        // The currently active variable scope
        private CompilerScope _scope;
 
        // The lambda we are compiling
        private readonly LambdaExpression _lambda;
 
        // True if the method's first argument is of type Closure
        private readonly bool _hasClosureArgument;
 
        // Runtime constants bound to the delegate
        private readonly BoundConstants _boundConstants;
 
        // Free list of locals, so we reuse them rather than creating new ones
        private readonly KeyedStack<Type, LocalBuilder> _freeLocals = new KeyedStack<Type, LocalBuilder>();
 
        /// <summary>
        /// Creates a lambda compiler that will compile to a dynamic method
        /// </summary>
        private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda)
        {
            Type[] parameterTypes = GetParameterTypes(lambda, typeof(Closure));
 
            int lambdaMethodIndex = Interlocked.Increment(ref s_lambdaMethodIndex);
            var method = new DynamicMethod(lambda.Name ?? ("lambda_method" + lambdaMethodIndex.ToString()), lambda.ReturnType, parameterTypes, true);
 
            _tree = tree;
            _lambda = lambda;
            _method = method;
 
            // In a Win8 immersive process user code is not allowed to access non-W8P framework APIs through
            // reflection or RefEmit. Framework code, however, is given an exemption.
            // This is to make sure that user code cannot access non-W8P framework APIs via ExpressionTree.
 
            // TODO: This API is not available, is there an alternative way to achieve the same.
            // method.ProfileAPICheck = true;
 
            _ilg = method.GetILGenerator();
 
            _hasClosureArgument = true;
 
            // These are populated by AnalyzeTree/VariableBinder
            _scope = tree.Scopes[lambda];
            _boundConstants = tree.Constants[lambda];
 
            InitializeMethod();
        }
 
#if FEATURE_COMPILE_TO_METHODBUILDER
        /// <summary>
        /// Creates a lambda compiler that will compile into the provided MethodBuilder
        /// </summary>
        private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method)
        {
            var scope = tree.Scopes[lambda];
            var hasClosureArgument = scope.NeedsClosure;
 
            Type[] paramTypes = GetParameterTypes(lambda, hasClosureArgument ? typeof(Closure) : null);
 
            method.SetReturnType(lambda.ReturnType);
            method.SetParameters(paramTypes);
            var parameters = lambda.Parameters;
            // parameters are index from 1, with closure argument we need to skip the first arg
            int startIndex = hasClosureArgument ? 2 : 1;
            for (int i = 0, n = parameters.Count; i < n; i++)
            {
                method.DefineParameter(i + startIndex, ParameterAttributes.None, parameters[i].Name);
            }
 
            _tree = tree;
            _lambda = lambda;
            _typeBuilder = (TypeBuilder)method.DeclaringType;
            _method = method;
            _hasClosureArgument = hasClosureArgument;
 
            _ilg = method.GetILGenerator();
 
            // These are populated by AnalyzeTree/VariableBinder
            _scope = scope;
            _boundConstants = tree.Constants[lambda];
 
            InitializeMethod();
        }
#endif
 
        /// <summary>
        /// Creates a lambda compiler for an inlined lambda
        /// </summary>
        private LambdaCompiler(
            LambdaCompiler parent,
            LambdaExpression lambda,
            InvocationExpression invocation)
        {
            _tree = parent._tree;
            _lambda = lambda;
            _method = parent._method;
            _ilg = parent._ilg;
            _hasClosureArgument = parent._hasClosureArgument;
#if FEATURE_COMPILE_TO_METHODBUILDER
            _typeBuilder = parent._typeBuilder;
#endif
            // inlined scopes are associated with invocation, not with the lambda
            _scope = _tree.Scopes[invocation];
            _boundConstants = parent._boundConstants;
        }
 
        private void InitializeMethod()
        {
            // See if we can find a return label, so we can emit better IL
            AddReturnLabel(_lambda);
            _boundConstants.EmitCacheConstants(this);
        }
 
        internal ILGenerator IL => _ilg;
 
        internal IParameterProvider Parameters => _lambda;
 
#if FEATURE_COMPILE_TO_METHODBUILDER
        internal bool CanEmitBoundConstants => _method is DynamicMethod;
#endif
 
        #region Compiler entry points
 
        /// <summary>
        /// Compiler entry point
        /// </summary>
        /// <param name="lambda">LambdaExpression to compile.</param>
        /// <returns>The compiled delegate.</returns>
        internal static Delegate Compile(LambdaExpression lambda)
        {
            lambda.ValidateArgumentCount();
 
            // 1. Bind lambda
            AnalyzedTree tree = AnalyzeLambda(ref lambda);
 
            // 2. Create lambda compiler
            LambdaCompiler c = new LambdaCompiler(tree, lambda);
 
            // 3. Emit
            c.EmitLambdaBody();
 
            // 4. Return the delegate.
            return c.CreateDelegate();
        }
 
#if FEATURE_COMPILE_TO_METHODBUILDER
        /// <summary>
        /// Mutates the MethodBuilder parameter, filling in IL, parameters,
        /// and return type.
        ///
        /// (probably shouldn't be modifying parameters/return type...)
        /// </summary>
        internal static void Compile(LambdaExpression lambda, MethodBuilder method)
        {
            // 1. Bind lambda
            AnalyzedTree tree = AnalyzeLambda(ref lambda);
 
            // 2. Create lambda compiler
            LambdaCompiler c = new LambdaCompiler(tree, lambda, method);
 
            // 3. Emit
            c.EmitLambdaBody();
        }
#endif
 
        #endregion
 
        private static AnalyzedTree AnalyzeLambda(ref LambdaExpression lambda)
        {
            // Spill the stack for any exception handling blocks or other
            // constructs which require entering with an empty stack
            lambda = StackSpiller.AnalyzeLambda(lambda);
 
            // Bind any variable references in this lambda
            return VariableBinder.Bind(lambda);
        }
 
        public LocalBuilder GetLocal(Type type) => _freeLocals.TryPop(type) ?? _ilg.DeclareLocal(type);
 
        public void FreeLocal(LocalBuilder local)
        {
            Debug.Assert(local != null);
            _freeLocals.Push(local.LocalType, local);
        }
 
        /// <summary>
        /// Gets the argument slot corresponding to the parameter at the given
        /// index. Assumes that the method takes a certain number of prefix
        /// arguments, followed by the real parameters stored in Parameters
        /// </summary>
        internal int GetLambdaArgument(int index)
        {
            return index + (_hasClosureArgument ? 1 : 0) + (_method.IsStatic ? 0 : 1);
        }
 
        /// <summary>
        /// Returns the index-th argument. This method provides access to the actual arguments
        /// defined on the lambda itself, and excludes the possible 0-th closure argument.
        /// </summary>
        internal void EmitLambdaArgument(int index)
        {
            _ilg.EmitLoadArg(GetLambdaArgument(index));
        }
 
        internal void EmitClosureArgument()
        {
            Debug.Assert(_hasClosureArgument, "must have a Closure argument");
            Debug.Assert(_method.IsStatic, "must be a static method");
            _ilg.EmitLoadArg(0);
        }
 
        private Delegate CreateDelegate()
        {
            Debug.Assert(_method is DynamicMethod);
 
            return _method.CreateDelegate(_lambda.Type, new Closure(_boundConstants.ToArray(), null));
        }
 
#if FEATURE_COMPILE_TO_METHODBUILDER
        private FieldBuilder CreateStaticField(string name, Type type)
        {
            // We are emitting into someone else's type. We don't want name
            // conflicts, so choose a long name that is unlikely to conflict.
            // Naming scheme chosen here is similar to what the C# compiler
            // uses.
            return _typeBuilder.DefineField("<ExpressionCompilerImplementationDetails>{" + System.Threading.Interlocked.Increment(ref s_counter) + "}" + name, type, FieldAttributes.Static | FieldAttributes.Private);
        }
#endif
 
        /// <summary>
        /// Creates an uninitialized field suitable for private implementation details
        /// Works with DynamicMethods or TypeBuilders.
        /// </summary>
        private MemberExpression CreateLazyInitializedField<T>(string name)
        {
#if FEATURE_COMPILE_TO_METHODBUILDER
            if (_method is DynamicMethod)
#else
            Debug.Assert(_method is DynamicMethod);
#endif
            {
                return Utils.GetStrongBoxValueField(Expression.Constant(new StrongBox<T>()));
            }
#if FEATURE_COMPILE_TO_METHODBUILDER
            else
            {
                return Expression.Field(null, CreateStaticField(name, typeof(T)));
            }
#endif
        }
    }
}