File: System\Linq\Expressions\Compiler\HoistedLocals.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.Collections.ObjectModel;
using System.Dynamic.Utils;
using System.Runtime.CompilerServices;
 
namespace System.Linq.Expressions.Compiler
{
    // Suppose we have something like:
    //
    //    (string s)=>()=>s.
    //
    // We wish to generate the outer as:
    //
    //      Func<string> OuterMethod(Closure closure, string s)
    //      {
    //          object[] locals = new object[1];
    //          locals[0] = new StrongBox<string>();
    //          ((StrongBox<string>)locals[0]).Value = s;
    //          return ((DynamicMethod)closure.Constants[0]).CreateDelegate(typeof(Func<string>), new Closure(null, locals));
    //      }
    //
    // ... and the inner as:
    //
    //      string InnerMethod(Closure closure)
    //      {
    //          object[] locals = closure.Locals;
    //          return ((StrongBox<string>)locals[0]).Value;
    //      }
    //
    // This class tracks that "s" was hoisted into a closure, as the 0th
    // element in the array
    //
    /// <summary>
    /// Stores information about locals and arguments that are hoisted into
    /// the closure array because they're referenced in an inner lambda.
    ///
    /// This class is sometimes emitted as a runtime constant for internal
    /// use to hoist variables/parameters in quoted expressions
    ///
    /// Invariant: this class stores no mutable state
    /// </summary>
    internal sealed class HoistedLocals
    {
        // The parent locals, if any
        internal readonly HoistedLocals? Parent;
 
        // A mapping of hoisted variables to their indexes in the array
        internal readonly ReadOnlyDictionary<Expression, int> Indexes;
 
        // The variables, in the order they appear in the array
        internal readonly ReadOnlyCollection<ParameterExpression> Variables;
 
        // A virtual variable for accessing this locals array
        internal readonly ParameterExpression SelfVariable;
 
        internal HoistedLocals(HoistedLocals? parent, ReadOnlyCollection<ParameterExpression> vars)
        {
            if (parent != null)
            {
                // Add the parent locals array as the 0th element in the array
                vars = vars.AddFirst(parent.SelfVariable);
            }
 
            if (vars.Count != 0)
            {
                Dictionary<Expression, int> indexes = new Dictionary<Expression, int>(vars.Count);
                for (int i = 0; i < vars.Count; i++)
                {
                    indexes.Add(vars[i], i);
                }
 
                Indexes = new ReadOnlyDictionary<Expression, int>(indexes);
            }
            else
            {
                Indexes = ReadOnlyDictionary<Expression, int>.Empty;
            }
 
            Parent = parent;
            Variables = vars;
            SelfVariable = Expression.Variable(typeof(object[]), name: null);
        }
 
        internal ParameterExpression? ParentVariable => Parent?.SelfVariable;
 
        internal static object[] GetParent(object[] locals)
        {
            return ((StrongBox<object[]>)locals[0]).Value!;
        }
    }
}