File: Compiler\TypeCompilationState.cs
Web Access
Project: src\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;
                }
            }
        }
    }
}