/// <summary> /// Rewrite an async method into a state machine type. /// </summary> internal static BoundStatement Rewrite( BoundStatement body, MethodSymbol method, int methodOrdinal, VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics, out AsyncStateMachine stateMachineType) { if (!method.IsAsync) { stateMachineType = null; return body; } // The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class. var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct; var bodyWithAwaitLifted = AwaitExpressionSpiller.Rewrite(body, method, compilationState, diagnostics); stateMachineType = new AsyncStateMachine(slotAllocatorOpt, compilationState, method, methodOrdinal, typeKind); compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType); var rewriter = new AsyncRewriter(bodyWithAwaitLifted, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics); if (!rewriter.VerifyPresenceOfRequiredAPIs()) { return body; } return rewriter.Rewrite(); }
internal AsyncMethodToStateMachineRewriter( MethodSymbol method, int methodOrdinal, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, IReadOnlySet<Symbol> hoistedVariables, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, DiagnosticBag diagnostics) : base(F, method, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false) { _method = method; _asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; _asyncMethodBuilderField = builder; _exprReturnLabel = F.GenerateLabel("exprReturn"); _exitLabel = F.GenerateLabel("exitLabel"); _exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation) ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, syntax: F.Syntax, kind: SynthesizedLocalKind.AsyncMethodReturnValue) : null; _dynamicFactory = new LoweredDynamicOperationFactory(F, methodOrdinal); _awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>(TypeSymbol.EqualsIgnoringDynamicComparer); _nextAwaiterId = slotAllocatorOpt?.PreviousAwaiterSlotCount ?? 0; }
private static string MakeName(VariableSlotAllocator slotAllocatorOpt, SyntaxNode scopeSyntaxOpt, MethodDebugId methodId, int closureOrdinal) { if (scopeSyntaxOpt == null) { // Display class is shared among static non-generic lambdas accross generations, method ordinal is -1 in that case. // A new display class of a static generic lambda is created for each method and each generation. return GeneratedNames.MakeStaticLambdaDisplayClassName(methodId.Ordinal, methodId.Generation); } int previousClosureOrdinal; if (slotAllocatorOpt != null && slotAllocatorOpt.TryGetPreviousClosure(scopeSyntaxOpt, out previousClosureOrdinal)) { methodId = slotAllocatorOpt.PreviousMethodId; closureOrdinal = previousClosureOrdinal; } // If we haven't found existing closure in the previous generation, use the current generation method ordinal. // That is, don't try to reuse previous generation method ordinal as that might create name conflict. // E.g. // Gen0 Gen1 // F() { new closure } // ordinal 0 // G() { } // ordinal 0 G() { new closure } // ordinal 1 // // In the example above G is updated and F is added. // G's ordinal in Gen0 is 0. If we used that ordinal for updated G's new closure it would conflict with F's ordinal. Debug.Assert(methodId.Ordinal >= 0); return GeneratedNames.MakeLambdaDisplayClassName(methodId.Ordinal, methodId.Generation, closureOrdinal); }
protected StateMachineRewriter( BoundStatement body, MethodSymbol method, SynthesizedContainer stateMachineType, VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics) { Debug.Assert(body != null); Debug.Assert(method != null); Debug.Assert(stateMachineType != null); Debug.Assert(compilationState != null); Debug.Assert(diagnostics != null); this.body = body; this.method = method; this.stateMachineType = stateMachineType; this.slotAllocatorOpt = slotAllocatorOpt; this.synthesizedLocalOrdinals = new SynthesizedLocalOrdinalsDispenser(); this.diagnostics = diagnostics; this.F = new SyntheticBoundNodeFactory(method, body.Syntax, compilationState, diagnostics); Debug.Assert(F.CurrentType == method.ContainingType); Debug.Assert(F.Syntax == body.Syntax); }
public AsyncStateMachine(VariableSlotAllocator variableAllocatorOpt, TypeCompilationState compilationState, MethodSymbol asyncMethod, int asyncMethodOrdinal, TypeKind typeKind) : base(variableAllocatorOpt, compilationState, asyncMethod, asyncMethodOrdinal) { // TODO: report use-site errors on these types _typeKind = typeKind; _interfaces = ImmutableArray.Create(asyncMethod.DeclaringCompilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_IAsyncStateMachine)); _constructor = new AsyncConstructor(this); }
protected MethodToClassRewriter(VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics) { Debug.Assert(compilationState != null); Debug.Assert(diagnostics != null); this.CompilationState = compilationState; this.Diagnostics = diagnostics; this.slotAllocatorOpt = slotAllocatorOpt; }
public LocalSlotManager(VariableSlotAllocator slotAllocatorOpt) { _slotAllocatorOpt = slotAllocatorOpt; // Add placeholders for pre-allocated locals. // The actual identities are populated if/when the locals are reused. if (slotAllocatorOpt != null) { _lazyAllLocals = new ArrayBuilder <Cci.ILocalDefinition>(); slotAllocatorOpt.AddPreviousLocals(_lazyAllLocals); } }
private AsyncRewriter( BoundStatement body, MethodSymbol method, int methodOrdinal, AsyncStateMachine stateMachineType, VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics) : base(body, method, stateMachineType, slotAllocatorOpt, compilationState, diagnostics) { _constructedSuccessfully = AsyncMethodBuilderMemberCollection.TryCreate(F, method, this.stateMachineType.TypeMap, out _asyncMethodBuilderMemberCollection); _methodOrdinal = methodOrdinal; _ignoreAccessibility = compilationState.ModuleBuilderOpt.IgnoreAccessibility; }
internal static MethodBody GenerateMethodBody( PEModuleBuilder moduleBuilder, SourceRoutineSymbol routine, int methodOrdinal, //ImmutableArray<LambdaDebugInfo> lambdaDebugInfo, //ImmutableArray<ClosureDebugInfo> closureDebugInfo, //StateMachineTypeSymbol stateMachineTypeOpt, VariableSlotAllocator variableSlotAllocatorOpt, DiagnosticBag diagnostics, //ImportChain importChainOpt, bool emittingPdb) { return GenerateMethodBody(moduleBuilder, routine, (builder) => { DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance(); var optimization = moduleBuilder.Compilation.Options.OptimizationLevel; var codeGen = new CodeGenerator(routine, builder, moduleBuilder, diagnosticsForThisMethod, optimization, emittingPdb); //if (diagnosticsForThisMethod.HasAnyErrors()) //{ // // we are done here. Since there were errors we should not emit anything. // return null; //} // We need to save additional debugging information for MoveNext of an async state machine. //var stateMachineMethod = method as SynthesizedStateMachineMethod; //bool isStateMachineMoveNextMethod = stateMachineMethod != null && method.Name == WellKnownMemberNames.MoveNextMethodName; //if (isStateMachineMoveNextMethod && stateMachineMethod.StateMachineType.KickoffMethod.IsAsync) //{ // int asyncCatchHandlerOffset; // ImmutableArray<int> asyncYieldPoints; // ImmutableArray<int> asyncResumePoints; // codeGen.Generate(out asyncCatchHandlerOffset, out asyncYieldPoints, out asyncResumePoints); // var kickoffMethod = stateMachineMethod.StateMachineType.KickoffMethod; // // The exception handler IL offset is used by the debugger to treat exceptions caught by the marked catch block as "user unhandled". // // This is important for async void because async void exceptions generally result in the process being terminated, // // but without anything useful on the call stack. Async Task methods on the other hand return exceptions as the result of the Task. // // So it is undesirable to consider these exceptions "user unhandled" since there may well be user code that is awaiting the task. // // This is a heuristic since it's possible that there is no user code awaiting the task. // asyncDebugInfo = new Cci.AsyncMethodBodyDebugInfo(kickoffMethod, kickoffMethod.ReturnsVoid ? asyncCatchHandlerOffset : -1, asyncYieldPoints, asyncResumePoints); //} //else { codeGen.Generate(); } }, variableSlotAllocatorOpt, diagnostics, emittingPdb); }
private IteratorRewriter( BoundStatement body, MethodSymbol method, bool isEnumerable, IteratorStateMachine stateMachineType, VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics) : base(body, method, stateMachineType, slotAllocatorOpt, compilationState, diagnostics) { // the element type may contain method type parameters, which are now alpha-renamed into type parameters of the generated class _elementType = stateMachineType.ElementType; _isEnumerable = isEnumerable; }
private int _nextFinalizeState = StateMachineStates.FinishedStateMachine - 1; // -3 internal IteratorMethodToStateMachineRewriter( SyntheticBoundNodeFactory F, MethodSymbol originalMethod, FieldSymbol state, FieldSymbol current, IReadOnlySet<Symbol> hoistedVariables, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, DiagnosticBag diagnostics) : base(F, originalMethod, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false) { _current = current; }
internal LambdaFrame(VariableSlotAllocator slotAllocatorOpt, MethodSymbol topLevelMethod, MethodDebugId methodId, CSharpSyntaxNode scopeSyntaxOpt, int closureOrdinal) : base(MakeName(slotAllocatorOpt, scopeSyntaxOpt, methodId, closureOrdinal), topLevelMethod) { _topLevelMethod = topLevelMethod; _constructor = new LambdaFrameConstructor(this); this.ClosureOrdinal = closureOrdinal; // static lambdas technically have the class scope so the scope syntax is null if (scopeSyntaxOpt == null) { _staticConstructor = new SynthesizedStaticConstructor(this); var cacheVariableName = GeneratedNames.MakeCachedFrameInstanceFieldName(); _singletonCache = new SynthesizedLambdaCacheFieldSymbol(this, this, cacheVariableName, topLevelMethod, isReadOnly: true, isStatic: true); } AssertIsLambdaScopeSyntax(scopeSyntaxOpt); this.ScopeSyntaxOpt = scopeSyntaxOpt; }
/// <summary> /// Rewrite an iterator method into a state machine class. /// </summary> internal static BoundStatement Rewrite( BoundStatement body, MethodSymbol method, int methodOrdinal, VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics, out IteratorStateMachine stateMachineType) { TypeSymbol elementType = method.IteratorElementType; if ((object)elementType == null) { stateMachineType = null; return body; } // Figure out what kind of iterator we are generating. bool isEnumerable; switch (method.ReturnType.OriginalDefinition.SpecialType) { case SpecialType.System_Collections_IEnumerable: case SpecialType.System_Collections_Generic_IEnumerable_T: isEnumerable = true; break; case SpecialType.System_Collections_IEnumerator: case SpecialType.System_Collections_Generic_IEnumerator_T: isEnumerable = false; break; default: throw ExceptionUtilities.UnexpectedValue(method.ReturnType.OriginalDefinition.SpecialType); } stateMachineType = new IteratorStateMachine(slotAllocatorOpt, compilationState, method, methodOrdinal, isEnumerable, elementType); compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType); var rewriter = new IteratorRewriter(body, method, isEnumerable, stateMachineType, slotAllocatorOpt, compilationState, diagnostics); if (!rewriter.VerifyPresenceOfRequiredAPIs()) { return body; } return rewriter.Rewrite(); }
public IteratorStateMachine(VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, MethodSymbol iteratorMethod, int iteratorMethodOrdinal, bool isEnumerable, TypeSymbol elementType) : base(slotAllocatorOpt, compilationState, iteratorMethod, iteratorMethodOrdinal) { this.ElementType = TypeMap.SubstituteType(elementType); var interfaces = ArrayBuilder<NamedTypeSymbol>.GetInstance(); if (isEnumerable) { interfaces.Add(ContainingAssembly.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T).Construct(ElementType)); interfaces.Add(ContainingAssembly.GetSpecialType(SpecialType.System_Collections_IEnumerable)); } interfaces.Add(ContainingAssembly.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerator_T).Construct(ElementType)); interfaces.Add(ContainingAssembly.GetSpecialType(SpecialType.System_IDisposable)); interfaces.Add(ContainingAssembly.GetSpecialType(SpecialType.System_Collections_IEnumerator)); _interfaces = interfaces.ToImmutableAndFree(); _constructor = new IteratorConstructor(this); }
private AsyncRewriter( BoundStatement body, MethodSymbol method, int methodOrdinal, AsyncStateMachine stateMachineType, VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, DiagnosticBag diagnostics) : base(body, method, stateMachineType, slotAllocatorOpt, compilationState, diagnostics) { try { _constructedSuccessfully = AsyncMethodBuilderMemberCollection.TryCreate(F, method, this.stateMachineType.TypeMap, out _asyncMethodBuilderMemberCollection); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diagnostics.Add(ex.Diagnostic); _constructedSuccessfully = false; } _methodOrdinal = methodOrdinal; _ignoreAccessibility = compilationState.ModuleBuilderOpt.IgnoreAccessibility; }
/// <summary> /// Generates method body that calls another method. /// Used for wrapping a method call into a method, e.g. an entry point. /// </summary> internal static MethodBody GenerateMethodBody( PEModuleBuilder moduleBuilder, MethodSymbol routine, Action<ILBuilder> builder, VariableSlotAllocator variableSlotAllocatorOpt, DiagnosticBag diagnostics, bool emittingPdb) { var compilation = moduleBuilder.Compilation; var localSlotManager = new LocalSlotManager(variableSlotAllocatorOpt); var optimizations = compilation.Options.OptimizationLevel; DebugDocumentProvider debugDocumentProvider = null; if (emittingPdb) { debugDocumentProvider = (path, basePath) => moduleBuilder.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile); } ILBuilder il = new ILBuilder(moduleBuilder, localSlotManager, optimizations); try { Cci.AsyncMethodBodyDebugInfo asyncDebugInfo = null; builder(il); // il.Realize(); // var localVariables = il.LocalSlotManager.LocalsInOrder(); if (localVariables.Length > 0xFFFE) { //diagnosticsForThisMethod.Add(ErrorCode.ERR_TooManyLocals, method.Locations.First()); } //if (diagnosticsForThisMethod.HasAnyErrors()) //{ // // we are done here. Since there were errors we should not emit anything. // return null; //} //// We will only save the IL builders when running tests. //if (moduleBuilder.SaveTestData) //{ // moduleBuilder.SetMethodTestData(method, builder.GetSnapshot()); //} // Only compiler-generated MoveNext methods have iterator scopes. See if this is one. var stateMachineHoistedLocalScopes = default(ImmutableArray<Cci.StateMachineHoistedLocalScope>); //if (isStateMachineMoveNextMethod) //{ // stateMachineHoistedLocalScopes = builder.GetHoistedLocalScopes(); //} var stateMachineHoistedLocalSlots = default(ImmutableArray<EncHoistedLocalInfo>); var stateMachineAwaiterSlots = default(ImmutableArray<Cci.ITypeReference>); //if (optimizations == OptimizationLevel.Debug && stateMachineTypeOpt != null) //{ // Debug.Assert(method.IsAsync || method.IsIterator); // GetStateMachineSlotDebugInfo(moduleBuilder, moduleBuilder.GetSynthesizedFields(stateMachineTypeOpt), variableSlotAllocatorOpt, diagnosticsForThisMethod, out stateMachineHoistedLocalSlots, out stateMachineAwaiterSlots); // Debug.Assert(!diagnostics.HasAnyErrors()); //} return new MethodBody( il.RealizedIL, il.MaxStack, (Cci.IMethodDefinition)routine.PartialDefinitionPart ?? routine, variableSlotAllocatorOpt?.MethodId ?? new DebugId(0, moduleBuilder.CurrentGenerationOrdinal), localVariables, il.RealizedSequencePoints, debugDocumentProvider, il.RealizedExceptionHandlers, il.GetAllScopes(), il.HasDynamicLocal, null, // importScopeOpt, ImmutableArray<LambdaDebugInfo>.Empty, // lambdaDebugInfo, ImmutableArray<ClosureDebugInfo>.Empty, // closureDebugInfo, null, //stateMachineTypeOpt?.Name, stateMachineHoistedLocalScopes, stateMachineHoistedLocalSlots, stateMachineAwaiterSlots, asyncDebugInfo); } finally { // Basic blocks contain poolable builders for IL and sequence points. Free those back // to their pools. il.FreeBasicBlocks(); //// Remember diagnostics. //diagnostics.AddRange(diagnosticsForThisMethod); //diagnosticsForThisMethod.Free(); } }
public StateMachineTypeSymbol(VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, MethodSymbol kickoffMethod, int kickoffMethodOrdinal) : base(MakeName(slotAllocatorOpt, compilationState, kickoffMethod, kickoffMethodOrdinal), kickoffMethod) { Debug.Assert(kickoffMethod != null); this.KickoffMethod = kickoffMethod; }
private static string MakeName(VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, MethodSymbol kickoffMethod, int kickoffMethodOrdinal) { return slotAllocatorOpt?.PreviousStateMachineTypeName ?? GeneratedNames.MakeStateMachineTypeName(kickoffMethod.Name, kickoffMethodOrdinal, compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal); }
// new: public MethodToStateMachineRewriter( SyntheticBoundNodeFactory F, MethodSymbol originalMethod, FieldSymbol state, IReadOnlySet<Symbol> hoistedVariables, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, DiagnosticBag diagnostics, bool useFinalizerBookkeeping) : base(slotAllocatorOpt, F.CompilationState, diagnostics) { Debug.Assert(F != null); Debug.Assert(originalMethod != null); Debug.Assert(state != null); Debug.Assert(nonReusableLocalProxies != null); Debug.Assert(diagnostics != null); Debug.Assert(hoistedVariables != null); Debug.Assert(nextFreeHoistedLocalSlot >= 0); this.F = F; this.stateField = state; this.cachedState = F.SynthesizedLocal(F.SpecialType(SpecialType.System_Int32), syntax: F.Syntax, kind: SynthesizedLocalKind.StateMachineCachedState); _useFinalizerBookkeeping = useFinalizerBookkeeping; _hasFinalizerState = useFinalizerBookkeeping; this.OriginalMethod = originalMethod; _hoistedVariables = hoistedVariables; _synthesizedLocalOrdinals = synthesizedLocalOrdinals; _nextFreeHoistedLocalSlot = nextFreeHoistedLocalSlot; foreach (var proxy in nonReusableLocalProxies) { this.proxies.Add(proxy.Key, proxy.Value); } }
// new: public MethodToStateMachineRewriter( SyntheticBoundNodeFactory F, MethodSymbol originalMethod, FieldSymbol state, IReadOnlySet<Symbol> hoistedVariables, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, DiagnosticBag diagnostics, bool useFinalizerBookkeeping) : base(slotAllocatorOpt, F.CompilationState, diagnostics) { Debug.Assert(F != null); Debug.Assert(originalMethod != null); Debug.Assert(state != null); Debug.Assert(nonReusableLocalProxies != null); Debug.Assert(diagnostics != null); Debug.Assert(hoistedVariables != null); Debug.Assert(nextFreeHoistedLocalSlot >= 0); this.F = F; this.stateField = state; this.cachedState = F.SynthesizedLocal(F.SpecialType(SpecialType.System_Int32), syntax: F.Syntax, kind: SynthesizedLocalKind.StateMachineCachedState); _useFinalizerBookkeeping = useFinalizerBookkeeping; _hasFinalizerState = useFinalizerBookkeeping; this.OriginalMethod = originalMethod; _hoistedVariables = hoistedVariables; _synthesizedLocalOrdinals = synthesizedLocalOrdinals; _nextFreeHoistedLocalSlot = nextFreeHoistedLocalSlot; foreach (var proxy in nonReusableLocalProxies) { this.proxies.Add(proxy.Key, proxy.Value); } // create cache local for reference type "this" in Release var thisParameter = originalMethod.ThisParameter; CapturedSymbolReplacement thisProxy; if ((object)thisParameter != null && thisParameter.Type.IsReferenceType && proxies.TryGetValue(thisParameter, out thisProxy) && F.Compilation.Options.OptimizationLevel == OptimizationLevel.Release) { BoundExpression thisProxyReplacement = thisProxy.Replacement(F.Syntax, frameType => F.This()); this.cachedThis = F.SynthesizedLocal(thisProxyReplacement.Type, syntax: F.Syntax, kind: SynthesizedLocalKind.FrameCache); } }