| File: Compiler\TypeCompilationState.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// Represents the state of compilation of one particular type. /// This includes, for example, a collection of synthesized methods created during lowering. /// </summary> /// <remarks> /// WARNING: Note that the collection class is not thread-safe and will /// need to be revised if emit phase is changed to support multithreading when /// translating a particular type. /// </remarks> internal sealed class TypeCompilationState { /// <summary> Synthesized method info </summary> internal readonly struct MethodWithBody { public readonly MethodSymbol Method; public readonly BoundStatement Body; public readonly ImportChain? ImportChain; internal MethodWithBody(MethodSymbol method, BoundStatement body, ImportChain? importChain) { RoslynDebug.Assert(method != null); RoslynDebug.Assert(body != null); this.Method = method; this.Body = body; this.ImportChain = importChain; } } /// <summary> Flat array of created methods, non-empty if not-null </summary> private ArrayBuilder<MethodWithBody>? _synthesizedMethods; /// <summary> /// Map of wrapper methods created for base access of base type virtual methods from /// other classes (like those created for lambdas...); actually each method symbol will /// only need one wrapper to call it non-virtually. /// </summary> private Dictionary<MethodSymbol, MethodSymbol>? _wrappers; /// <summary> /// Type symbol being compiled, or null if we compile a synthesized type that doesn't have a symbol (e.g. PrivateImplementationDetails). /// </summary> private readonly NamedTypeSymbol? _typeOpt; /// <summary> /// The builder for generating code, or null if not in emit phase. /// </summary> public readonly PEModuleBuilder? ModuleBuilderOpt; /// <summary> /// Any generated methods that don't suppress debug info will use this /// list of debug imports. /// </summary> public ImportChain? CurrentImportChain; public readonly CSharpCompilation Compilation; public SynthesizedClosureEnvironment? StaticLambdaFrame; public DelegateCacheContainer? ConcreteDelegateCacheContainer; /// <summary> /// A graph of method->method references for this(...) constructor initializers. /// Used to detect and report initializer cycles. /// </summary> private SmallDictionary<MethodSymbol, MethodSymbol>? _constructorInitializers; public TypeCompilationState(NamedTypeSymbol? typeOpt, CSharpCompilation compilation, PEModuleBuilder? moduleBuilderOpt) { this.Compilation = compilation; _typeOpt = typeOpt; this.ModuleBuilderOpt = moduleBuilderOpt; } /// <summary> /// The type for which this compilation state is being used. /// </summary> public NamedTypeSymbol Type { get { // NOTE: currently it can be null if only private implementation type methods are compiled. // There should be no caller of this method in that case. Debug.Assert(_typeOpt is { }); return _typeOpt; } } /// <summary> /// The type passed to the runtime binder as context. /// </summary> public NamedTypeSymbol? DynamicOperationContextType { get { return this.ModuleBuilderOpt?.GetDynamicOperationContextType(this.Type); } } [MemberNotNullWhen(true, nameof(ModuleBuilderOpt))] public bool Emitting { get { return ModuleBuilderOpt != null; } } public ArrayBuilder<MethodWithBody>? SynthesizedMethods { get { return _synthesizedMethods; } set { Debug.Assert(_synthesizedMethods == null); _synthesizedMethods = value; } } /// <summary> /// Add a 'regular' synthesized method. /// </summary> public void AddSynthesizedMethod(MethodSymbol method, BoundStatement body) { if (_synthesizedMethods == null) { _synthesizedMethods = ArrayBuilder<MethodWithBody>.GetInstance(); } _synthesizedMethods.Add(new MethodWithBody(method, body, CurrentImportChain)); } /// <summary> /// Add a 'wrapper' synthesized method and map it to the original one so it can be reused. /// </summary> /// <remarks> /// Wrapper methods are created for base access of base type virtual methods from /// other classes (like those created for lambdas...). /// </remarks> public void AddMethodWrapper(MethodSymbol method, MethodSymbol wrapper, BoundStatement body) { this.AddSynthesizedMethod(wrapper, body); if (_wrappers == null) { _wrappers = new Dictionary<MethodSymbol, MethodSymbol>(); } _wrappers.Add(method, wrapper); } /// <summary> The index of the next wrapped method to be used </summary> public int NextWrapperMethodIndex { get { return _wrappers == null ? 0 : _wrappers.Count; } } /// <summary> /// Get a 'wrapper' method for the original one. /// </summary> /// <remarks> /// Wrapper methods are created for base access of base type virtual methods from /// other classes (like those created for lambdas...). /// </remarks> public MethodSymbol? GetMethodWrapper(MethodSymbol method) { MethodSymbol? wrapper = null; return _wrappers != null && _wrappers.TryGetValue(method, out wrapper) ? wrapper : null; } /// <summary> Free resources allocated for this method collection </summary> public void Free() { if (_synthesizedMethods != null) { _synthesizedMethods.Free(); _synthesizedMethods = null; } _wrappers = null; _constructorInitializers = null; } /// <summary> /// Report an error if adding the edge (method1, method2) to the ctor-initializer /// graph would add a new cycle to that graph. /// </summary> /// <param name="method1">a calling ctor</param> /// <param name="method2">the chained-to ctor</param> /// <param name="syntax">where to report a cyclic error if needed</param> /// <param name="diagnostics">a diagnostic bag for receiving the diagnostic</param> internal void ReportCtorInitializerCycles(MethodSymbol method1, MethodSymbol method2, SyntaxNode syntax, BindingDiagnosticBag diagnostics) { // precondition and postcondition: the graph _constructorInitializers is acyclic. // If adding the edge (method1, method2) would induce a cycle, we report an error // and do not add it to the set of edges. If it would not induce a cycle we add // it to the set of edges and return. if (method1 == method2) { // direct recursion is diagnosed elsewhere throw ExceptionUtilities.Unreachable(); } if (_constructorInitializers == null) { _constructorInitializers = new SmallDictionary<MethodSymbol, MethodSymbol>(); _constructorInitializers.Add(method1, method2); return; } MethodSymbol? next = method2; while (true) { if (_constructorInitializers.TryGetValue(next, out next)) { RoslynDebug.Assert((object)next != null); if (method1 == next) { // We found a (new) cycle containing the edge (method1, method2). Report an // error and do not add the edge. diagnostics.Add(ErrorCode.ERR_IndirectRecursiveConstructorCall, syntax.Location, method1); return; } } else { // we've reached the end of the path without finding a cycle. Add the new edge. _constructorInitializers.Add(method1, method2); return; } } } } }