Exemple #1
0
        private IReadOnlyDictionary <Symbol, CapturedSymbolReplacement> CreateNonReusableLocalProxies(MultiDictionary <Symbol, CSharpSyntaxNode> variablesCaptured)
        {
            var proxies = new Dictionary <Symbol, CapturedSymbolReplacement>();

            var typeMap = stateMachineType.TypeMap;

            var orderedCaptured =
                from local in variablesCaptured.Keys
                orderby local.Name, (local.Locations.Length == 0) ? 0 : local.Locations[0].SourceSpan.Start
            select local;

            foreach (var capturedVariable in orderedCaptured)
            {
                if (capturedVariable.Kind == SymbolKind.Local)
                {
                    var local = (LocalSymbol)capturedVariable;
                    if (local.SynthesizedLocalKind == SynthesizedLocalKind.None ||
                        local.SynthesizedLocalKind == SynthesizedLocalKind.LambdaDisplayClass)
                    {
                        // create proxies for user-defined variables and for lambda closures:
                        Debug.Assert(local.RefKind == RefKind.None);
                        proxies.Add(local, MakeNonReusableLocalProxy(typeMap, variablesCaptured, local));
                    }
                }
                else
                {
                    var parameter = (ParameterSymbol)capturedVariable;
                    if (parameter.IsThis)
                    {
                        var proxyField = F.StateMachineField(method.ContainingType, GeneratedNames.ThisProxyName(), isPublic: true);
                        proxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField, isReusable: false));

                        if (PreserveInitialParameterValues)
                        {
                            var initialThis = method.ContainingType.IsStructType() ?
                                              F.StateMachineField(method.ContainingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true) : proxyField;

                            initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement(initialThis, isReusable: false));
                        }
                    }
                    else
                    {
                        var proxyField = F.StateMachineField(typeMap.SubstituteType(parameter.Type), parameter.Name, isPublic: true);
                        proxies.Add(parameter, new CapturedToFrameSymbolReplacement(proxyField, isReusable: false));

                        if (PreserveInitialParameterValues)
                        {
                            string proxyName = GeneratedNames.StateMachineParameterProxyName(parameter.Name);
                            initialParameters.Add(parameter, new CapturedToFrameSymbolReplacement(
                                                      F.StateMachineField(typeMap.SubstituteType(parameter.Type), proxyName, isPublic: true),
                                                      isReusable: false));
                        }

                        if (parameter.Type.IsRestrictedType())
                        {
                            // CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method
                            diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, parameter.Locations[0], parameter.Type);
                        }
                    }
                }
            }

            return(proxies);
        }
        private void CreateNonReusableLocalProxies(
            IEnumerable <Symbol> variablesToHoist,
            out IReadOnlyDictionary <Symbol, CapturedSymbolReplacement> proxies,
            out int nextFreeHoistedLocalSlot)
        {
            var proxiesBuilder = new Dictionary <Symbol, CapturedSymbolReplacement>();

            var  typeMap             = stateMachineType.TypeMap;
            bool isDebugBuild        = F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug;
            bool mapToPreviousFields = isDebugBuild && slotAllocatorOpt != null;

            nextFreeHoistedLocalSlot = mapToPreviousFields ? slotAllocatorOpt.PreviousHoistedLocalSlotCount : 0;

            foreach (var variable in variablesToHoist)
            {
                Debug.Assert(variable.Kind == SymbolKind.Local || variable.Kind == SymbolKind.Parameter);

                if (variable.Kind == SymbolKind.Local)
                {
                    var local           = (LocalSymbol)variable;
                    var synthesizedKind = local.SynthesizedKind;

                    if (!synthesizedKind.MustSurviveStateMachineSuspension())
                    {
                        continue;
                    }

                    // no need to hoist constants
                    if (local.IsConst)
                    {
                        continue;
                    }

                    if (local.RefKind != RefKind.None)
                    {
                        // we'll create proxies for these variables later:
                        Debug.Assert(synthesizedKind == SynthesizedLocalKind.AwaitSpill);
                        continue;
                    }

                    Debug.Assert(local.RefKind == RefKind.None);
                    StateMachineFieldSymbol field = null;

                    if (!local.SynthesizedKind.IsSlotReusable(F.Compilation.Options.OptimizationLevel))
                    {
                        // variable needs to be hoisted
                        var fieldType = typeMap.SubstituteType(local.Type).Type;

                        LocalDebugId id;
                        int          slotIndex = -1;

                        if (isDebugBuild)
                        {
                            // Calculate local debug id.
                            //
                            // EnC: When emitting the baseline (gen 0) the id is stored in a custom debug information attached to the kickoff method.
                            //      When emitting a delta the id is only used to map to the existing field in the previous generation.
                            SyntaxNode declaratorSyntax = local.GetDeclaratorSyntax();
                            int        syntaxOffset     = this.method.CalculateLocalSyntaxOffset(declaratorSyntax.SpanStart, declaratorSyntax.SyntaxTree);
                            int        ordinal          = synthesizedLocalOrdinals.AssignLocalOrdinal(synthesizedKind, syntaxOffset);
                            id = new LocalDebugId(syntaxOffset, ordinal);

                            // map local id to the previous id, if available:
                            int previousSlotIndex;
                            if (mapToPreviousFields && slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex(
                                    declaratorSyntax,
                                    F.ModuleBuilderOpt.Translate(fieldType, declaratorSyntax, diagnostics),
                                    synthesizedKind,
                                    id,
                                    diagnostics,
                                    out previousSlotIndex))
                            {
                                slotIndex = previousSlotIndex;
                            }
                        }
                        else
                        {
                            id = LocalDebugId.None;
                        }

                        if (slotIndex == -1)
                        {
                            slotIndex = nextFreeHoistedLocalSlot++;
                        }

                        string fieldName = GeneratedNames.MakeHoistedLocalFieldName(synthesizedKind, slotIndex, local.Name);
                        field = F.StateMachineField(fieldType, fieldName, new LocalSlotDebugInfo(synthesizedKind, id), slotIndex);
                    }

                    if (field != null)
                    {
                        proxiesBuilder.Add(local, new CapturedToStateMachineFieldReplacement(field, isReusable: false));
                    }
                }
                else
                {
                    var parameter = (ParameterSymbol)variable;
                    if (parameter.IsThis)
                    {
                        var containingType = method.ContainingType;
                        var proxyField     = F.StateMachineField(containingType, GeneratedNames.ThisProxyFieldName(), isPublic: true);
                        proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false));

                        if (PreserveInitialParameterValues)
                        {
                            var initialThis = containingType.IsStructType() ?
                                              F.StateMachineField(containingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true) : proxyField;

                            initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(initialThis, isReusable: false));
                        }
                    }
                    else
                    {
                        // The field needs to be public iff it is initialized directly from the kickoff method
                        // (i.e. not for IEnumerable which loads the values from parameter proxies).
                        var proxyField = F.StateMachineField(typeMap.SubstituteType(parameter.Type).Type, parameter.Name, isPublic: !PreserveInitialParameterValues);
                        proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false));

                        if (PreserveInitialParameterValues)
                        {
                            var field = F.StateMachineField(typeMap.SubstituteType(parameter.Type).Type, GeneratedNames.StateMachineParameterProxyFieldName(parameter.Name), isPublic: true);
                            initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(field, isReusable: false));
                        }
                    }
                }
            }

            proxies = proxiesBuilder;
        }