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; }