Ejemplo n.º 1
0
            private void MakeAndAssignEnvironments()
            {
                VisitScopeTree(ScopeTree, scope =>
                {
                    // Currently all variables declared in the same scope are added
                    // to the same closure environment
                    var variablesInEnvironment = scope.DeclaredVariables;

                    // Don't create empty environments
                    if (variablesInEnvironment.Count == 0)
                    {
                        return;
                    }

                    // First walk the nested scopes to find all closures which
                    // capture variables from this scope. They all need to capture
                    // this environment. This includes closures which captured local
                    // functions that capture those variables, so multiple passes may
                    // be needed. This will also decide if the environment is a struct
                    // or a class.

                    // If we are in a variant interface, runtime might not consider the
                    // method synthesized directly within the interface as variant safe.
                    // For simplicity we do not perform precise analysis whether this would
                    // definitely be the case. If we are in a variant interface, we always force
                    // creation of a display class.
                    bool isStruct = VarianceSafety.GetEnclosingVariantInterface(_topLevelMethod) is null;
                    var closures  = new SetWithInsertionOrder <Closure>();
                    bool addedItem;

                    // This loop is O(n), where n is the length of the chain
                    //   L_1 <- L_2 <- L_3 ...
                    // where L_1 represents a local function that directly captures the current
                    // environment, L_2 represents a local function that directly captures L_1,
                    // L_3 represents a local function that captures L_2, and so on.
                    //
                    // Each iteration of the loop runs a visitor that is proportional to the
                    // number of closures in nested scopes, so we hope that the total number
                    // of nested functions and function chains is small in any real-world code.
                    do
                    {
                        addedItem = false;
                        VisitClosures(scope, (closureScope, closure) =>
                        {
                            if (!closures.Contains(closure) &&
                                (closure.CapturedVariables.Overlaps(scope.DeclaredVariables) ||
                                 closure.CapturedVariables.Overlaps(closures.Select(c => c.OriginalMethodSymbol))))
                            {
                                closures.Add(closure);
                                addedItem = true;
                                isStruct &= CanTakeRefParameters(closure.OriginalMethodSymbol);
                            }
                        });
                    } while (addedItem == true);

                    // Next create the environment and add it to the declaration scope
                    var env = new ClosureEnvironment(variablesInEnvironment, isStruct);
                    scope.DeclaredEnvironments.Add(env);

                    _topLevelMethod.TryGetThisParameter(out var thisParam);
                    foreach (var closure in closures)
                    {
                        closure.CapturedEnvironments.Add(env);
                        if (thisParam != null && env.CapturedVariables.Contains(thisParam))
                        {
                            closure.CapturesThis = true;
                        }
                    }
                });
            }
Ejemplo n.º 2
0
            /// <summary>
            /// We may have ended up with a closure environment containing only
            /// 'this'. This is basically equivalent to the containing type itself,
            /// so we can inline the 'this' parameter into environments that
            /// reference this one or lower closures directly onto the containing
            /// type.
            /// </summary>
            private void InlineThisOnlyEnvironments()
            {
                // First make sure 'this' even exists
                if (!_topLevelMethod.TryGetThisParameter(out var thisParam) ||
                    thisParam == null)
                {
                    return;
                }

                var topLevelEnvs = ScopeTree.DeclaredEnvironments;

                // If it does exist, 'this' is always in the top-level environment
                if (topLevelEnvs.Count == 0)
                {
                    return;
                }

                Debug.Assert(topLevelEnvs.Count == 1);
                var env = topLevelEnvs[0];

                // The environment must contain only 'this' to be inlined
                if (env.CapturedVariables.Count > 1 ||
                    !env.CapturedVariables.Contains(thisParam))
                {
                    return;
                }

                if (env.IsStruct)
                {
                    // If everything that captures the 'this' environment
                    // lives in the containing type, we can remove the env
                    bool cantRemove = CheckClosures(ScopeTree, (scope, closure) =>
                    {
                        return(closure.CapturedEnvironments.Contains(env) &&
                               closure.ContainingEnvironmentOpt != null);
                    });

                    if (!cantRemove)
                    {
                        RemoveEnv();
                    }
                }
                // If we are in a variant interface, runtime might not consider the
                // method synthesized directly within the interface as variant safe.
                // For simplicity we do not perform precise analysis whether this would
                // definitely be the case. If we are in a variant interface, we always force
                // creation of a display class.
                else if (VarianceSafety.GetEnclosingVariantInterface(_topLevelMethod) is null)
                {
                    // Class-based 'this' closures can move member functions to
                    // the top-level type and environments which capture the 'this'
                    // environment can capture 'this' directly.
                    // Note: the top-level type is treated as the initial containing
                    // environment, so by removing the 'this' environment, all
                    // nested environments which captured a pointer to the 'this'
                    // environment will now capture 'this'
                    RemoveEnv();
                    VisitClosures(ScopeTree, (scope, closure) =>
                    {
                        if (closure.ContainingEnvironmentOpt == env)
                        {
                            closure.ContainingEnvironmentOpt = null;
                        }
                    });
                }

                void RemoveEnv()
                {
                    topLevelEnvs.RemoveAt(topLevelEnvs.IndexOf(env));
                    VisitClosures(ScopeTree, (scope, closure) =>
                    {
                        var index = closure.CapturedEnvironments.IndexOf(env);
                        if (index >= 0)
                        {
                            closure.CapturedEnvironments.RemoveAt(index);
                        }
                    });
                }
            }