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.Spill); continue; } Debug.Assert(local.RefKind == RefKind.None); StateMachineFieldSymbol field = null; if (ShouldPreallocateNonReusableProxy(local)) { // variable needs to be hoisted var fieldType = typeMap.SubstituteType(local.Type.TypeSymbol).TypeSymbol; 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, isThis: true); proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false)); if (PreserveInitialParameterValuesAndThreadId) { var initialThis = containingType.IsStructType() ? F.StateMachineField(containingType, GeneratedNames.StateMachineThisParameterProxyName(), isPublic: true, isThis: 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.TypeSymbol).TypeSymbol, parameter.Name, isPublic: !PreserveInitialParameterValuesAndThreadId); proxiesBuilder.Add(parameter, new CapturedToStateMachineFieldReplacement(proxyField, isReusable: false)); if (PreserveInitialParameterValuesAndThreadId) { var field = F.StateMachineField(typeMap.SubstituteType(parameter.Type.TypeSymbol).TypeSymbol, GeneratedNames.StateMachineParameterProxyFieldName(parameter.Name), isPublic: true); initialParameters.Add(parameter, new CapturedToStateMachineFieldReplacement(field, isReusable: false)); } } } } proxies = proxiesBuilder; }