File: Emitter\Model\MethodSymbolAdapter.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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    internal partial class
#if DEBUG
        MethodSymbolAdapter : SymbolAdapter,
#else
        MethodSymbol :
#endif 
        Cci.ITypeMemberReference,
        Cci.IMethodReference,
        Cci.IGenericMethodInstanceReference,
        Cci.ISpecializedMethodReference,
        Cci.ITypeDefinitionMember,
        Cci.IMethodDefinition
    {
        bool Cci.IDefinition.IsEncDeleted
            => false;
 
        Cci.IGenericMethodInstanceReference Cci.IMethodReference.AsGenericMethodInstanceReference
        {
            get
            {
                Debug.Assert(this.IsDefinitionOrDistinct());
 
                if (!AdaptedMethodSymbol.IsDefinition &&
                    AdaptedMethodSymbol.IsGenericMethod)
                {
                    return this;
                }
 
                return null;
            }
        }
 
        Cci.ISpecializedMethodReference Cci.IMethodReference.AsSpecializedMethodReference
        {
            get
            {
                Debug.Assert(this.IsDefinitionOrDistinct());
 
                if (!AdaptedMethodSymbol.IsDefinition &&
                    (!AdaptedMethodSymbol.IsGenericMethod || PEModuleBuilder.IsGenericType(AdaptedMethodSymbol.ContainingType)))
                {
                    Debug.Assert((object)AdaptedMethodSymbol.ContainingType != null &&
                            PEModuleBuilder.IsGenericType(AdaptedMethodSymbol.ContainingType));
                    return this;
                }
 
                return null;
            }
        }
 
        Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context)
        {
            return ResolvedMethodImpl(context);
        }
 
        Cci.ITypeReference Cci.ITypeMemberReference.GetContainingType(EmitContext context)
        {
            Debug.Assert(this.IsDefinitionOrDistinct());
 
            var synthesizedGlobalMethod = AdaptedMethodSymbol.OriginalDefinition as SynthesizedGlobalMethodSymbol;
            if ((object)synthesizedGlobalMethod != null)
            {
                return synthesizedGlobalMethod.ContainingPrivateImplementationDetailsType;
            }
 
            NamedTypeSymbol containingType = AdaptedMethodSymbol.ContainingType;
            var moduleBeingBuilt = (PEModuleBuilder)context.Module;
 
            return moduleBeingBuilt.Translate(containingType,
                syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNode,
                diagnostics: context.Diagnostics,
                needDeclaration: AdaptedMethodSymbol.IsDefinition);
        }
 
        void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor)
        {
            Debug.Assert(this.IsDefinitionOrDistinct());
 
            if (!AdaptedMethodSymbol.IsDefinition)
            {
                if (AdaptedMethodSymbol.IsGenericMethod)
                {
                    Debug.Assert(((Cci.IMethodReference)this).AsGenericMethodInstanceReference != null);
                    visitor.Visit((Cci.IGenericMethodInstanceReference)this);
                }
                else
                {
                    Debug.Assert(((Cci.IMethodReference)this).AsSpecializedMethodReference != null);
                    visitor.Visit((Cci.ISpecializedMethodReference)this);
                }
            }
            else
            {
                PEModuleBuilder moduleBeingBuilt = (PEModuleBuilder)visitor.Context.Module;
                if (AdaptedMethodSymbol.ContainingModule == moduleBeingBuilt.SourceModule)
                {
                    Debug.Assert(((Cci.IMethodReference)this).GetResolvedMethod(visitor.Context) != null);
                    visitor.Visit((Cci.IMethodDefinition)this);
                }
                else
                {
                    visitor.Visit((Cci.IMethodReference)this);
                }
            }
        }
 
        string Cci.INamedEntity.Name
        {
            get { return AdaptedMethodSymbol.MetadataName; }
        }
 
        bool Cci.IMethodReference.AcceptsExtraArguments
        {
            get
            {
                return AdaptedMethodSymbol.IsVararg;
            }
        }
 
        ushort Cci.IMethodReference.GenericParameterCount
        {
            get
            {
                return (ushort)AdaptedMethodSymbol.Arity;
            }
        }
 
        ushort Cci.ISignature.ParameterCount
        {
            get
            {
                return (ushort)AdaptedMethodSymbol.ParameterCount;
            }
        }
 
        Cci.IMethodDefinition Cci.IMethodReference.GetResolvedMethod(EmitContext context)
        {
            return ResolvedMethodImpl(context);
        }
 
        private Cci.IMethodDefinition ResolvedMethodImpl(EmitContext context)
        {
            Debug.Assert(this.IsDefinitionOrDistinct());
            PEModuleBuilder moduleBeingBuilt = (PEModuleBuilder)context.Module;
 
            if (AdaptedMethodSymbol.IsDefinition && // can't be generic instantiation
                AdaptedMethodSymbol.ContainingModule == moduleBeingBuilt.SourceModule) // must be declared in the module we are building
            {
                Debug.Assert((object)AdaptedMethodSymbol.PartialDefinitionPart == null); // must be definition
                return this;
            }
 
            return null;
        }
 
        ImmutableArray<Cci.IParameterTypeInformation> Cci.IMethodReference.ExtraParameters
        {
            get
            {
                return ImmutableArray<Cci.IParameterTypeInformation>.Empty;
            }
        }
 
        Cci.CallingConvention Cci.ISignature.CallingConvention
        {
            get
            {
                return AdaptedMethodSymbol.CallingConvention;
            }
        }
 
        ImmutableArray<Cci.IParameterTypeInformation> Cci.ISignature.GetParameters(EmitContext context)
        {
            Debug.Assert(this.IsDefinitionOrDistinct());
 
            PEModuleBuilder moduleBeingBuilt = (PEModuleBuilder)context.Module;
            if (AdaptedMethodSymbol.IsDefinition && AdaptedMethodSymbol.ContainingModule == moduleBeingBuilt.SourceModule)
            {
                return StaticCast<Cci.IParameterTypeInformation>.From(this.EnumerateDefinitionParameters());
            }
            else
            {
                return moduleBeingBuilt.Translate(AdaptedMethodSymbol.Parameters);
            }
        }
 
        private ImmutableArray<Cci.IParameterDefinition> EnumerateDefinitionParameters()
        {
            Debug.Assert(AdaptedMethodSymbol.Parameters.All(p => p.IsDefinition));
 
#if DEBUG
            return AdaptedMethodSymbol.Parameters.SelectAsArray<ParameterSymbol, Cci.IParameterDefinition>(p => p.GetCciAdapter());
#else
            return StaticCast<Cci.IParameterDefinition>.From(AdaptedMethodSymbol.Parameters);
#endif
        }
 
        ImmutableArray<Cci.ICustomModifier> Cci.ISignature.ReturnValueCustomModifiers
        {
            get
            {
                return ImmutableArray<Cci.ICustomModifier>.CastUp(AdaptedMethodSymbol.ReturnTypeWithAnnotations.CustomModifiers);
            }
        }
 
        ImmutableArray<Cci.ICustomModifier> Cci.ISignature.RefCustomModifiers
        {
            get
            {
                return ImmutableArray<Cci.ICustomModifier>.CastUp(AdaptedMethodSymbol.RefCustomModifiers);
            }
        }
 
        bool Cci.ISignature.ReturnValueIsByRef
        {
            get
            {
                return AdaptedMethodSymbol.RefKind.IsManagedReference();
            }
        }
 
        Cci.ITypeReference Cci.ISignature.GetType(EmitContext context)
        {
            return ((PEModuleBuilder)context.Module).Translate(AdaptedMethodSymbol.ReturnType,
                syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNode,
                diagnostics: context.Diagnostics);
        }
 
        IEnumerable<Cci.ITypeReference> Cci.IGenericMethodInstanceReference.GetGenericArguments(EmitContext context)
        {
            PEModuleBuilder moduleBeingBuilt = (PEModuleBuilder)context.Module;
 
            Debug.Assert(((Cci.IMethodReference)this).AsGenericMethodInstanceReference != null);
 
            foreach (var arg in AdaptedMethodSymbol.TypeArgumentsWithAnnotations)
            {
                Debug.Assert(arg.CustomModifiers.IsEmpty);
                yield return moduleBeingBuilt.Translate(arg.Type,
                                                        syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNode,
                                                        diagnostics: context.Diagnostics);
            }
        }
 
        Cci.IMethodReference Cci.IGenericMethodInstanceReference.GetGenericMethod(EmitContext context)
        {
            Debug.Assert(((Cci.IMethodReference)this).AsGenericMethodInstanceReference != null);
 
            NamedTypeSymbol container = AdaptedMethodSymbol.ContainingType;
 
            if (!PEModuleBuilder.IsGenericType(container))
            {
                // NoPia method might come through here.
                return ((PEModuleBuilder)context.Module).Translate(
                    (MethodSymbol)AdaptedMethodSymbol.OriginalDefinition,
                    syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNode,
                    diagnostics: context.Diagnostics,
                    needDeclaration: true);
            }
 
            MethodSymbol methodSymbol = AdaptedMethodSymbol.ConstructedFrom;
 
            return new SpecializedMethodReference(methodSymbol);
        }
 
        Cci.IMethodReference Cci.ISpecializedMethodReference.UnspecializedVersion
        {
            get
            {
                Debug.Assert(((Cci.IMethodReference)this).AsSpecializedMethodReference != null);
                return ((MethodSymbol)AdaptedMethodSymbol.OriginalDefinition).GetCciAdapter();
            }
        }
#nullable enable
        Cci.ITypeDefinition Cci.ITypeDefinitionMember.ContainingTypeDefinition
        {
            get
            {
                CheckDefinitionInvariant();
 
                if (AdaptedMethodSymbol.OriginalDefinition is SynthesizedGlobalMethodSymbol synthesizedGlobalMethod)
                {
                    return synthesizedGlobalMethod.ContainingPrivateImplementationDetailsType;
                }
 
                return AdaptedMethodSymbol.ContainingType.GetCciAdapter();
            }
        }
 
        Cci.TypeMemberVisibility Cci.ITypeDefinitionMember.Visibility
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.MetadataVisibility;
            }
        }
 
        bool Cci.IMethodDefinition.HasBody
        {
            get
            {
                CheckDefinitionInvariant();
                return Cci.DefaultImplementations.HasBody(this);
            }
        }
 
        Cci.IMethodBody? Cci.IMethodDefinition.GetBody(EmitContext context)
        {
            CheckDefinitionInvariant();
            return ((PEModuleBuilder)context.Module).GetMethodBody(AdaptedMethodSymbol);
        }
 
        IEnumerable<Cci.IGenericMethodParameter> Cci.IMethodDefinition.GenericParameters
        {
            get
            {
                CheckDefinitionInvariant();
 
                foreach (var @param in AdaptedMethodSymbol.TypeParameters)
                {
                    Debug.Assert(@param.IsDefinition);
                    yield return @param.GetCciAdapter();
                }
            }
        }
 
        bool Cci.IMethodDefinition.HasDeclarativeSecurity
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.HasDeclarativeSecurity;
            }
        }
 
        IEnumerable<Cci.SecurityAttribute> Cci.IMethodDefinition.SecurityAttributes
        {
            get
            {
                CheckDefinitionInvariant();
                Debug.Assert(AdaptedMethodSymbol.HasDeclarativeSecurity);
                return AdaptedMethodSymbol.GetSecurityInformation();
            }
        }
 
        bool Cci.IMethodDefinition.IsAbstract
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.IsAbstract;
            }
        }
 
        bool Cci.IMethodDefinition.IsAccessCheckedOnOverride
        {
            get
            {
                CheckDefinitionInvariant();
 
                return AdaptedMethodSymbol.IsAccessCheckedOnOverride;
            }
        }
 
        bool Cci.IMethodDefinition.IsConstructor
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.MethodKind == MethodKind.Constructor;
            }
        }
 
        bool Cci.IMethodDefinition.IsExternal
        {
            get
            {
                CheckDefinitionInvariant();
 
                return AdaptedMethodSymbol.IsExternal;
            }
        }
 
        bool Cci.IMethodDefinition.IsHiddenBySignature
        {
            get
            {
                CheckDefinitionInvariant();
                return !AdaptedMethodSymbol.HidesBaseMethodsByName;
            }
        }
 
        bool Cci.IMethodDefinition.IsNewSlot
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.IsMetadataNewSlot();
            }
        }
 
        bool Cci.IMethodDefinition.IsPlatformInvoke
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.GetDllImportData() != null;
            }
        }
#nullable disable
        Cci.IPlatformInvokeInformation Cci.IMethodDefinition.PlatformInvokeData
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.GetDllImportData();
            }
        }
 
        System.Reflection.MethodImplAttributes Cci.IMethodDefinition.GetImplementationAttributes(EmitContext context)
        {
            CheckDefinitionInvariant();
            return AdaptedMethodSymbol.ImplementationAttributes;
        }
 
        bool Cci.IMethodDefinition.IsRuntimeSpecial
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.HasRuntimeSpecialName;
            }
        }
 
        bool Cci.IMethodDefinition.IsSealed
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.IsMetadataFinal;
            }
        }
 
        bool Cci.IMethodDefinition.IsSpecialName
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.HasSpecialName;
            }
        }
 
        bool Cci.IMethodDefinition.IsStatic
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.IsStatic;
            }
        }
 
        bool Cci.IMethodDefinition.IsVirtual
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.IsMetadataVirtual();
            }
        }
 
        ImmutableArray<Cci.IParameterDefinition> Cci.IMethodDefinition.Parameters
        {
            get
            {
                CheckDefinitionInvariant();
                return EnumerateDefinitionParameters();
            }
        }
 
        bool Cci.IMethodDefinition.RequiresSecurityObject
        {
            get
            {
                CheckDefinitionInvariant();
 
                return AdaptedMethodSymbol.RequiresSecurityObject;
            }
        }
 
        IEnumerable<Cci.ICustomAttribute> Cci.IMethodDefinition.GetReturnValueAttributes(EmitContext context)
        {
            CheckDefinitionInvariant();
 
            ImmutableArray<CSharpAttributeData> userDefined = AdaptedMethodSymbol.GetReturnTypeAttributes();
            ArrayBuilder<SynthesizedAttributeData> synthesized = null;
            AdaptedMethodSymbol.AddSynthesizedReturnTypeAttributes((PEModuleBuilder)context.Module, ref synthesized);
 
            // Note that callers of this method (CCI and ReflectionEmitter) have to enumerate 
            // all items of the returned iterator, otherwise the synthesized ArrayBuilder may leak.
            return AdaptedMethodSymbol.GetCustomAttributesToEmit(userDefined, synthesized, isReturnType: true, emittingAssemblyAttributesInNetModule: false);
        }
 
        bool Cci.IMethodDefinition.ReturnValueIsMarshalledExplicitly
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.ReturnValueIsMarshalledExplicitly;
            }
        }
 
        Cci.IMarshallingInformation Cci.IMethodDefinition.ReturnValueMarshallingInformation
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.ReturnValueMarshallingInformation;
            }
        }
 
        ImmutableArray<byte> Cci.IMethodDefinition.ReturnValueMarshallingDescriptor
        {
            get
            {
                CheckDefinitionInvariant();
                return AdaptedMethodSymbol.ReturnValueMarshallingDescriptor;
            }
        }
 
        Cci.INamespace Cci.IMethodDefinition.ContainingNamespace
        {
            get
            {
                return AdaptedMethodSymbol.ContainingNamespace.GetCciAdapter();
            }
        }
    }
 
    internal partial class MethodSymbol
    {
#if DEBUG
        private MethodSymbolAdapter _lazyAdapter;
 
        protected sealed override SymbolAdapter GetCciAdapterImpl() => GetCciAdapter();
 
        internal new MethodSymbolAdapter GetCciAdapter()
        {
            if (_lazyAdapter is null)
            {
                return InterlockedOperations.Initialize(ref _lazyAdapter, CreateCciAdapter());
            }
 
            return _lazyAdapter;
        }
 
        protected virtual MethodSymbolAdapter CreateCciAdapter()
        {
            return new MethodSymbolAdapter(this);
        }
#else
        internal MethodSymbol AdaptedMethodSymbol => this;
 
        internal new MethodSymbol GetCciAdapter()
        {
            return this;
        }
#endif
 
        internal virtual bool IsAccessCheckedOnOverride
        {
            get
            {
                CheckDefinitionInvariant();
 
                // Enforce C#'s notion of internal virtual
                // If the method is private or internal and virtual but not final
                // Set the new bit to indicate that it can only be overridden
                // by classes that can normally access this member.
                Accessibility accessibility = this.DeclaredAccessibility;
                return (accessibility == Accessibility.Private ||
                        accessibility == Accessibility.ProtectedAndInternal ||
                        accessibility == Accessibility.Internal)
                       && this.IsMetadataVirtual() && !this.IsMetadataFinal;
            }
        }
 
        internal virtual bool IsExternal
        {
            get
            {
                CheckDefinitionInvariant();
 
                // Delegate methods are implemented by the runtime.
                // Note that we don't force methods marked with MethodImplAttributes.InternalCall or MethodImplAttributes.Runtime
                // to be external, so it is possible to mark methods with bodies by these flags. It's up to the VM to interpret these flags
                // and throw runtime exception if they are applied incorrectly.
                return this.IsExtern || (object)ContainingType != null && ContainingType.TypeKind == TypeKind.Delegate;
            }
        }
 
        /// <summary>
        /// This method indicates whether or not the runtime will regard the method
        /// as newslot (as indicated by the presence of the "newslot" modifier in the
        /// signature).
        /// WARN WARN WARN: We won't have a final value for this until declaration
        /// diagnostics have been computed for all <see cref="SourceMemberContainerTypeSymbol"/>s, so pass
        /// ignoringInterfaceImplementationChanges: true if you need a value sooner
        /// and aren't concerned about tweaks made to satisfy interface implementation 
        /// requirements.
        /// NOTE: Not ignoring changes can only result in a value that is more true.
        /// </summary>
        internal abstract bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false);
 
        internal virtual bool HasRuntimeSpecialName
        {
            get
            {
                CheckDefinitionInvariant();
                return this.MethodKind == MethodKind.Constructor
                    || this.MethodKind == MethodKind.StaticConstructor;
            }
        }
 
        internal virtual bool IsMetadataFinal
        {
            get
            {
                // destructors should override this behavior
                Debug.Assert(this.MethodKind != MethodKind.Destructor);
 
                return this.IsSealed ||
                    (this.IsMetadataVirtual() &&
                     !(this.IsVirtual || this.IsOverride || this.IsAbstract || this.MethodKind == MethodKind.Destructor));
            }
        }
 
        /// <summary>
        /// This method indicates whether or not the runtime will regard the method
        /// as virtual (as indicated by the presence of the "virtual" modifier in the
        /// signature).
        /// WARN WARN WARN: We won't have a final value for this until declaration
        /// diagnostics have been computed for all <see cref="SourceMemberContainerTypeSymbol"/>s, so pass
        /// option: <see cref="IsMetadataVirtualOption.IgnoreInterfaceImplementationChanges"/> if you need a value sooner
        /// and aren't concerned about tweaks made to satisfy interface implementation 
        /// requirements.
        /// NOTE: Not ignoring changes can only result in a value that is more true.
        ///
        /// Use IsMetadataVirtualOption.ForceCompleteIfNeeded in DEBUG/assertion code
        /// to get the final value.
        /// </summary>
        internal abstract bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None);
 
        internal enum IsMetadataVirtualOption
        {
            None = 0,
            IgnoreInterfaceImplementationChanges,
#if DEBUG
            ForceCompleteIfNeeded
#endif
        }
 
        internal virtual bool ReturnValueIsMarshalledExplicitly
        {
            get
            {
                CheckDefinitionInvariant();
                return this.ReturnValueMarshallingInformation != null;
            }
        }
 
        internal virtual ImmutableArray<byte> ReturnValueMarshallingDescriptor
        {
            get
            {
                CheckDefinitionInvariant();
                return default(ImmutableArray<byte>);
            }
        }
    }
 
#if DEBUG
    internal partial class MethodSymbolAdapter
    {
        internal MethodSymbolAdapter(MethodSymbol underlyingMethodSymbol)
        {
            AdaptedMethodSymbol = underlyingMethodSymbol;
 
            if (underlyingMethodSymbol is NativeIntegerMethodSymbol)
            {
                // Emit should use underlying symbol only.
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        internal sealed override Symbol AdaptedSymbol => AdaptedMethodSymbol;
        internal MethodSymbol AdaptedMethodSymbol { get; }
    }
#endif
}