|
// 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.Globalization;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// The class to represent all methods imported from a PE/module.
/// </summary>
internal sealed class PEMethodSymbol : MethodSymbol
{
/// <summary>
/// internal for testing purpose
/// </summary>
internal class SignatureData
{
public readonly SignatureHeader Header;
public readonly ImmutableArray<ParameterSymbol> Parameters;
public readonly PEParameterSymbol ReturnParam;
public SignatureData(SignatureHeader header, ImmutableArray<ParameterSymbol> parameters, PEParameterSymbol returnParam)
{
this.Header = header;
this.Parameters = parameters;
this.ReturnParam = returnParam;
}
}
// This type is used to compact many different bits of information efficiently.
private struct PackedFlags
{
// We currently pack everything into a 32-bit int with the following layout:
//
// |y|x|w|v|u|t|s|r|q|p|ooo|n|m|l|k|j|i|h|g|f|e|d|c|b|aaaaa|
//
// a = method kind. 5 bits.
// b = method kind populated. 1 bit.
//
// c = isExtensionMethod. 1 bit.
// d = isExtensionMethod populated. 1 bit.
//
// e = isExplicitFinalizerOverride. 1 bit.
// f = isExplicitClassOverride. 1 bit.
// g = isExplicitFinalizerOverride and isExplicitClassOverride populated. 1 bit.
// h = isObsoleteAttribute populated. 1 bit
// i = isCustomAttributesPopulated. 1 bit
// j = isUseSiteDiagnostic populated. 1 bit
// k = isConditional populated. 1 bit
// l = isOverriddenOrHiddenMembers populated. 1 bit
// m = isReadOnly. 1 bit.
// n = isReadOnlyPopulated. 1 bit.
// o = NullableContext. 3 bits.
// p = DoesNotReturn. 1 bit.
// q = DoesNotReturnPopulated. 1 bit.
// r = MemberNotNullPopulated. 1 bit.
// s = IsInitOnlyBit. 1 bit.
// t = IsInitOnlyPopulatedBit. 1 bit.
// u = IsUnmanagedCallersOnlyAttributePopulated. 1 bit.
// v = IsSetsRequiredMembersBit. 1 bit.
// w = IsSetsRequiredMembersPopulated. 1 bit.
// x = IsUnscopedRef. 1 bit.
// y = IsUnscopedRefPopulated. 1 bit.
// z = OverloadResolutionPriorityPopulated. 1 bit.
// 1 bits remain for future purposes.
private const int MethodKindOffset = 0;
private const int MethodKindMask = 0x1F;
private const int MethodKindIsPopulatedBit = 0x1 << 5;
private const int IsExtensionMethodBit = 0x1 << 6;
private const int IsExtensionMethodIsPopulatedBit = 0x1 << 7;
private const int IsExplicitFinalizerOverrideBit = 0x1 << 8;
private const int IsExplicitClassOverrideBit = 0x1 << 9;
private const int IsExplicitOverrideIsPopulatedBit = 0x1 << 10;
private const int IsObsoleteAttributePopulatedBit = 0x1 << 11;
private const int IsCustomAttributesPopulatedBit = 0x1 << 12;
private const int IsUseSiteDiagnosticPopulatedBit = 0x1 << 13;
private const int IsConditionalPopulatedBit = 0x1 << 14;
private const int IsOverriddenOrHiddenMembersPopulatedBit = 0x1 << 15;
private const int IsReadOnlyBit = 0x1 << 16;
private const int IsReadOnlyPopulatedBit = 0x1 << 17;
private const int NullableContextOffset = 18;
private const int NullableContextMask = 0x7;
private const int DoesNotReturnBit = 0x1 << 21;
private const int IsDoesNotReturnPopulatedBit = 0x1 << 22;
private const int IsMemberNotNullPopulatedBit = 0x1 << 23;
private const int IsInitOnlyBit = 0x1 << 24;
private const int IsInitOnlyPopulatedBit = 0x1 << 25;
private const int IsUnmanagedCallersOnlyAttributePopulatedBit = 0x1 << 26;
private const int HasSetsRequiredMembersBit = 0x1 << 27;
private const int HasSetsRequiredMembersPopulatedBit = 0x1 << 28;
private const int IsUnscopedRefBit = 0x1 << 29;
private const int IsUnscopedRefPopulatedBit = 0x1 << 30;
private const int OverloadResolutionPriorityPopulatedBit = 0x1 << 31;
private int _bits;
public MethodKind MethodKind
{
get
{
return (MethodKind)((_bits >> MethodKindOffset) & MethodKindMask);
}
set
{
Debug.Assert((int)value == ((int)value & MethodKindMask));
_bits = (_bits & ~(MethodKindMask << MethodKindOffset)) | (((int)value & MethodKindMask) << MethodKindOffset) | MethodKindIsPopulatedBit;
}
}
public bool MethodKindIsPopulated => (_bits & MethodKindIsPopulatedBit) != 0;
public bool IsExtensionMethod => (_bits & IsExtensionMethodBit) != 0;
public bool IsExtensionMethodIsPopulated => (_bits & IsExtensionMethodIsPopulatedBit) != 0;
public bool IsExplicitFinalizerOverride => (_bits & IsExplicitFinalizerOverrideBit) != 0;
public bool IsExplicitClassOverride => (_bits & IsExplicitClassOverrideBit) != 0;
public bool IsExplicitOverrideIsPopulated => (_bits & IsExplicitOverrideIsPopulatedBit) != 0;
public bool IsObsoleteAttributePopulated => (_bits & IsObsoleteAttributePopulatedBit) != 0;
public bool IsCustomAttributesPopulated => (_bits & IsCustomAttributesPopulatedBit) != 0;
public bool IsUseSiteDiagnosticPopulated => (_bits & IsUseSiteDiagnosticPopulatedBit) != 0;
public bool IsConditionalPopulated => (_bits & IsConditionalPopulatedBit) != 0;
public bool IsOverriddenOrHiddenMembersPopulated => (_bits & IsOverriddenOrHiddenMembersPopulatedBit) != 0;
public bool IsReadOnly => (_bits & IsReadOnlyBit) != 0;
public bool IsReadOnlyPopulated => (_bits & IsReadOnlyPopulatedBit) != 0;
public bool DoesNotReturn => (_bits & DoesNotReturnBit) != 0;
public bool IsDoesNotReturnPopulated => (_bits & IsDoesNotReturnPopulatedBit) != 0;
public bool IsMemberNotNullPopulated => (_bits & IsMemberNotNullPopulatedBit) != 0;
public bool IsInitOnly => (_bits & IsInitOnlyBit) != 0;
public bool IsInitOnlyPopulated => (_bits & IsInitOnlyPopulatedBit) != 0;
public bool IsUnmanagedCallersOnlyAttributePopulated => (_bits & IsUnmanagedCallersOnlyAttributePopulatedBit) != 0;
public bool HasSetsRequiredMembers => (_bits & HasSetsRequiredMembersBit) != 0;
public bool HasSetsRequiredMembersPopulated => (_bits & HasSetsRequiredMembersPopulatedBit) != 0;
public bool IsUnscopedRef => (_bits & IsUnscopedRefBit) != 0;
public bool IsUnscopedRefPopulated => (_bits & IsUnscopedRefPopulatedBit) != 0;
public bool IsOverloadResolutionPriorityPopulated => (_bits & OverloadResolutionPriorityPopulatedBit) != 0;
#if DEBUG
static PackedFlags()
{
// Verify masks are sufficient for values.
Debug.Assert(EnumUtilities.ContainsAllValues<MethodKind>(MethodKindMask));
Debug.Assert(EnumUtilities.ContainsAllValues<NullableContextKind>(NullableContextMask));
}
#endif
private static bool BitsAreUnsetOrSame(int bits, int mask)
{
return (bits & mask) == 0 || (bits & mask) == mask;
}
public void InitializeIsExtensionMethod(bool isExtensionMethod)
{
int bitsToSet = (isExtensionMethod ? IsExtensionMethodBit : 0) | IsExtensionMethodIsPopulatedBit;
Debug.Assert(BitsAreUnsetOrSame(_bits, bitsToSet));
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void InitializeIsReadOnly(bool isReadOnly)
{
int bitsToSet = (isReadOnly ? IsReadOnlyBit : 0) | IsReadOnlyPopulatedBit;
Debug.Assert(BitsAreUnsetOrSame(_bits, bitsToSet));
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void InitializeMethodKind(MethodKind methodKind)
{
Debug.Assert((int)methodKind == ((int)methodKind & MethodKindMask));
int bitsToSet = (((int)methodKind & MethodKindMask) << MethodKindOffset) | MethodKindIsPopulatedBit;
Debug.Assert(BitsAreUnsetOrSame(_bits, bitsToSet));
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void InitializeIsExplicitOverride(bool isExplicitFinalizerOverride, bool isExplicitClassOverride)
{
int bitsToSet =
(isExplicitFinalizerOverride ? IsExplicitFinalizerOverrideBit : 0) |
(isExplicitClassOverride ? IsExplicitClassOverrideBit : 0) |
IsExplicitOverrideIsPopulatedBit;
Debug.Assert(BitsAreUnsetOrSame(_bits, bitsToSet));
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void SetIsObsoleteAttributePopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsObsoleteAttributePopulatedBit);
}
public void SetIsCustomAttributesPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsCustomAttributesPopulatedBit);
}
public void SetIsUseSiteDiagnosticPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsUseSiteDiagnosticPopulatedBit);
}
public void SetIsConditionalAttributePopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsConditionalPopulatedBit);
}
public void SetIsOverriddenOrHiddenMembersPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsOverriddenOrHiddenMembersPopulatedBit);
}
public bool TryGetNullableContext(out byte? value)
{
return ((NullableContextKind)((_bits >> NullableContextOffset) & NullableContextMask)).TryGetByte(out value);
}
public bool SetNullableContext(byte? value)
{
return ThreadSafeFlagOperations.Set(ref _bits, (((int)value.ToNullableContextFlags() & NullableContextMask) << NullableContextOffset));
}
public bool InitializeDoesNotReturn(bool value)
{
int bitsToSet = IsDoesNotReturnPopulatedBit;
if (value) bitsToSet |= DoesNotReturnBit;
return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void SetIsMemberNotNullPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsMemberNotNullPopulatedBit);
}
public void InitializeIsInitOnly(bool isInitOnly)
{
int bitsToSet = (isInitOnly ? IsInitOnlyBit : 0) | IsInitOnlyPopulatedBit;
Debug.Assert(BitsAreUnsetOrSame(_bits, bitsToSet));
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void SetIsUnmanagedCallersOnlyAttributePopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsUnmanagedCallersOnlyAttributePopulatedBit);
}
public bool InitializeSetsRequiredMembersBit(bool value)
{
int bitsToSet = HasSetsRequiredMembersPopulatedBit;
if (value) bitsToSet |= HasSetsRequiredMembersBit;
return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public bool InitializeIsUnscopedRef(bool value)
{
int bitsToSet = IsUnscopedRefPopulatedBit;
if (value) bitsToSet |= IsUnscopedRefBit;
return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public void SetIsOverloadResolutionPriorityPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, OverloadResolutionPriorityPopulatedBit);
}
}
/// <summary>
/// This type is used to hold lazily-initialized fields that many methods will not need. We avoid creating it unless one of the fields is needed;
/// unfortunately, this means that we need to be careful of data races. The general pattern that we use is to check for a flag in <see cref="_packedFlags"/>.
/// If the flag for that field is set, and there was a positive result (ie, there are indeed custom attributes, or there is obsolete data), then it
/// is safe to rely on the data in the field. If the flag for a field is set but the result is empty (ie, there is no obsolete data), then we can be in
/// one of 3 scenarios:
/// <list type="number">
/// <item><see cref="_uncommonFields"/> is itself null. In this case, no race has occurred, and the consuming code can safely handle the lack of
/// <see cref="_uncommonFields"/> however it chooses.</item>
/// <item><see cref="_uncommonFields"/> is not null, and the backing field has been initialized to some empty value, such as
/// <see cref="ImmutableArray{T}.Empty"/>. In this case, again, no race has occurred, and the consuming code can simply trust the empty value.</item>
/// <item><see cref="_uncommonFields"/> is not null, and the backing field is uninitialized, either being <see langword="default" />, or is some
/// kind of sentinel value. In this case, a data race has occurred, and the consuming code must initialize the field to empty to bring it back
/// into scenario 2.</item>
/// </list>
/// </summary>
/// <remarks>
/// The initialization pattern for this type <b>must</b> follow the following pattern to make the safety guarantees above:
/// If the field initialization code determines that the backing field needs to be set to some non-empty value, it <b>must</b> first call <see cref="AccessUncommonFields"/>,
/// set the backing field using an atomic operation, and then set the flag in <see cref="_packedFlags"/>. This ensures that the field is always set before the flag is set.
/// If this order is reversed, the consuming code may see the flag set, but the field not initialized, and incorrectly assume that there is no data.
/// </remarks>
private sealed class UncommonFields
{
public ParameterSymbol _lazyThisParameter;
public Tuple<CultureInfo, string> _lazyDocComment;
public OverriddenOrHiddenMembersResult _lazyOverriddenOrHiddenMembersResult;
public ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
public ImmutableArray<string> _lazyConditionalAttributeSymbols;
public ObsoleteAttributeData _lazyObsoleteAttributeData;
public UnmanagedCallersOnlyAttributeData _lazyUnmanagedCallersOnlyAttributeData;
public CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo;
public ImmutableArray<string> _lazyNotNullMembers;
public ImmutableArray<string> _lazyNotNullMembersWhenTrue;
public ImmutableArray<string> _lazyNotNullMembersWhenFalse;
public MethodSymbol _lazyExplicitClassOverride;
public int _lazyOverloadResolutionPriority;
}
private UncommonFields AccessUncommonFields()
{
var retVal = _uncommonFields;
return retVal ?? InterlockedOperations.Initialize(ref _uncommonFields, createUncommonFields());
UncommonFields createUncommonFields()
{
var retVal = new UncommonFields();
if (!_packedFlags.IsObsoleteAttributePopulated)
{
retVal._lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
}
if (!_packedFlags.IsUnmanagedCallersOnlyAttributePopulated)
{
retVal._lazyUnmanagedCallersOnlyAttributeData = UnmanagedCallersOnlyAttributeData.Uninitialized;
}
//
// Do not set _lazyUseSiteDiagnostic !!!!
//
// "null" Indicates "no errors" or "unknown state",
// and we know which one of the states we have from IsUseSiteDiagnosticPopulated
//
// Setting _lazyUseSiteDiagnostic to a sentinel value here would introduce
// a number of extra states for various permutations of IsUseSiteDiagnosticPopulated, UncommonFields and _lazyUseSiteDiagnostic
// Some of them, in tight races, may lead to returning the sentinel as the diagnostics.
//
if (_packedFlags.IsCustomAttributesPopulated)
{
retVal._lazyCustomAttributes = ImmutableArray<CSharpAttributeData>.Empty;
}
if (_packedFlags.IsConditionalPopulated)
{
retVal._lazyConditionalAttributeSymbols = ImmutableArray<string>.Empty;
}
if (_packedFlags.IsOverriddenOrHiddenMembersPopulated)
{
retVal._lazyOverriddenOrHiddenMembersResult = OverriddenOrHiddenMembersResult.Empty;
}
if (_packedFlags.IsMemberNotNullPopulated)
{
retVal._lazyNotNullMembers = ImmutableArray<string>.Empty;
retVal._lazyNotNullMembersWhenTrue = ImmutableArray<string>.Empty;
retVal._lazyNotNullMembersWhenFalse = ImmutableArray<string>.Empty;
}
if (_packedFlags.IsExplicitOverrideIsPopulated)
{
retVal._lazyExplicitClassOverride = null;
}
return retVal;
}
}
private readonly MethodDefinitionHandle _handle;
private readonly string _name;
private readonly PENamedTypeSymbol _containingType;
private Symbol _associatedPropertyOrEventOpt;
private PackedFlags _packedFlags;
private readonly ushort _flags; // MethodAttributes
private readonly ushort _implFlags; // MethodImplAttributes
private ImmutableArray<TypeParameterSymbol> _lazyTypeParameters;
private SignatureData _lazySignature;
private ImmutableArray<MethodSymbol> _lazyExplicitMethodImplementations;
/// <summary>
/// A single field to hold optional auxiliary data.
/// In many scenarios it is possible to avoid allocating this, thus saving total space in <see cref="PEModuleSymbol"/>.
/// Even for lazily-computed values, it may be possible to avoid allocating <see cref="_uncommonFields"/> if
/// the computed value is a well-known "empty" value. In this case, bits in <see cref="_packedFlags"/> are used
/// to indicate that the lazy values have been computed and, if <see cref="_uncommonFields"/> is null, then
/// the "empty" value should be inferred.
/// </summary>
private UncommonFields _uncommonFields;
internal PEMethodSymbol(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol containingType,
MethodDefinitionHandle methodDef)
{
Debug.Assert((object)moduleSymbol != null);
Debug.Assert((object)containingType != null);
Debug.Assert(!methodDef.IsNil);
_handle = methodDef;
_containingType = containingType;
MethodAttributes localflags = 0;
try
{
int rva;
MethodImplAttributes implFlags;
moduleSymbol.Module.GetMethodDefPropsOrThrow(methodDef, out _name, out implFlags, out localflags, out rva);
Debug.Assert((uint)implFlags <= ushort.MaxValue);
_implFlags = (ushort)implFlags;
}
catch (BadImageFormatException)
{
if ((object)_name == null)
{
_name = string.Empty;
}
InitializeUseSiteDiagnostic(new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
}
Debug.Assert((uint)localflags <= ushort.MaxValue);
_flags = (ushort)localflags;
}
internal override bool TryGetThisParameter(out ParameterSymbol thisParameter)
{
thisParameter = IsStatic ? null :
_uncommonFields?._lazyThisParameter ?? InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyThisParameter, new ThisParameterSymbol(this));
return true;
}
public override Symbol ContainingSymbol => _containingType;
public override NamedTypeSymbol ContainingType => _containingType;
public override string Name => _name;
public override int MetadataToken => MetadataTokens.GetToken(_handle);
private bool HasFlag(MethodAttributes flag)
{
// flag must be exactly one bit
Debug.Assert(flag != 0 && ((ushort)flag & ((ushort)flag - 1)) == 0);
return ((ushort)flag & _flags) != 0;
}
// Exposed for testing purposes only
internal MethodAttributes Flags => (MethodAttributes)_flags;
internal override bool HasSpecialName => HasFlag(MethodAttributes.SpecialName);
internal override bool HasRuntimeSpecialName => HasFlag(MethodAttributes.RTSpecialName);
internal override MethodImplAttributes ImplementationAttributes => (MethodImplAttributes)_implFlags;
internal override bool RequiresSecurityObject => HasFlag(MethodAttributes.RequireSecObject);
// do not cache the result, the compiler doesn't use this (it's only exposed through public API):
public override DllImportData GetDllImportData() => HasFlag(MethodAttributes.PinvokeImpl)
? _containingType.ContainingPEModule.Module.GetDllImportData(_handle)
: null;
internal override bool ReturnValueIsMarshalledExplicitly => ReturnTypeParameter.IsMarshalledExplicitly;
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation => ReturnTypeParameter.MarshallingInformation;
internal override ImmutableArray<byte> ReturnValueMarshallingDescriptor => ReturnTypeParameter.MarshallingDescriptor;
internal override bool IsAccessCheckedOnOverride => HasFlag(MethodAttributes.CheckAccessOnOverride);
internal override bool HasDeclarativeSecurity => HasFlag(MethodAttributes.HasSecurity);
internal override IEnumerable<Cci.SecurityAttribute> GetSecurityInformation()
{
throw ExceptionUtilities.Unreachable();
}
public override Accessibility DeclaredAccessibility
{
get
{
switch (Flags & MethodAttributes.MemberAccessMask)
{
case MethodAttributes.Assembly:
return Accessibility.Internal;
case MethodAttributes.FamORAssem:
return Accessibility.ProtectedOrInternal;
case MethodAttributes.FamANDAssem:
return Accessibility.ProtectedAndInternal;
case MethodAttributes.Private:
case MethodAttributes.PrivateScope:
return Accessibility.Private;
case MethodAttributes.Public:
return Accessibility.Public;
case MethodAttributes.Family:
return Accessibility.Protected;
default:
return Accessibility.Private;
}
}
}
public override bool IsExtern => HasFlag(MethodAttributes.PinvokeImpl);
internal override bool IsExternal => IsExtern || (ImplementationAttributes & MethodImplAttributes.Runtime) != 0;
public override bool IsVararg => Signature.Header.CallingConvention == SignatureCallingConvention.VarArgs;
public override bool IsGenericMethod => Arity > 0;
public override bool IsAsync => false;
public override int Arity
{
get
{
if (!_lazyTypeParameters.IsDefault)
{
return _lazyTypeParameters.Length;
}
try
{
int parameterCount;
int typeParameterCount;
MetadataDecoder.GetSignatureCountsOrThrow(_containingType.ContainingPEModule.Module, _handle, out parameterCount, out typeParameterCount);
return typeParameterCount;
}
catch (BadImageFormatException)
{
return TypeParameters.Length;
}
}
}
internal MethodDefinitionHandle Handle => _handle;
// Has to have the abstract flag.
// NOTE: dev10 treats the method as abstract (i.e. requiring an impl in subtypes) event if it is not metadata virtual.
public override bool IsAbstract => HasFlag(MethodAttributes.Abstract);
// NOTE: abstract final methods are a bit strange. First, they don't
// PEVerify - there's a specific error message for that combination of modifiers.
// Second, if dev10 sees an abstract final method in a base class, it will report
// an error (CS0534) if it is not overridden. Third, dev10 does not report an
// error if it is overridden - it emits a virtual method without the newslot
// modifier as for a normal override. It is not clear how the runtime rules
// interpret this overriding method since the overridden method is invalid.
public override bool IsSealed => this.IsMetadataFinal &&
(this._containingType.IsInterface ?
this.IsAbstract && this.IsMetadataVirtual() && !this.IsMetadataNewSlot() :
!this.IsAbstract && this.IsOverride); //slowest check last
public override bool HidesBaseMethodsByName => !HasFlag(MethodAttributes.HideBySig);
// Has to be metadata virtual and cannot be a destructor. Cannot be either abstract or override.
// Final is a little special - if a method has the virtual, newslot, and final attr
// (and is not an explicit override) then we treat it as non-virtual for C# purposes.
public override bool IsVirtual => this.IsMetadataVirtual() && !this.IsDestructor && !this.IsMetadataFinal && !this.IsAbstract &&
(this._containingType.IsInterface ? (this.IsStatic || this.IsMetadataNewSlot()) : !this.IsOverride);
// Has to be metadata virtual and cannot be a destructor.
// Must either lack the newslot flag or be an explicit override (i.e. via the MethodImpl table).
//
// The IsExplicitClassOverride case is based on LangImporter::DefineMethodImplementations in the native compiler.
// ECMA-335
// 10.3.1 Introducing a virtual method
// If the definition is not marked newslot, the definition creates a new virtual method only
// if there is not virtual method of the same name and signature inherited from a base class.
//
// This means that a virtual method without NewSlot flag in a type that doesn't have a base
// is a new virtual method and doesn't override anything.
public override bool IsOverride =>
!this._containingType.IsInterface &&
this.IsMetadataVirtual() && !this.IsDestructor &&
((!this.IsMetadataNewSlot() && (object)_containingType.BaseTypeNoUseSiteDiagnostics != null) || this.IsExplicitClassOverride);
public override bool IsStatic => HasFlag(MethodAttributes.Static);
internal override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None) => HasFlag(MethodAttributes.Virtual);
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => HasFlag(MethodAttributes.NewSlot);
internal override bool IsMetadataFinal => HasFlag(MethodAttributes.Final);
private bool IsExplicitFinalizerOverride
{
get
{
if (!_packedFlags.IsExplicitOverrideIsPopulated)
{
var unused = this.ExplicitInterfaceImplementations;
Debug.Assert(_packedFlags.IsExplicitOverrideIsPopulated);
}
return _packedFlags.IsExplicitFinalizerOverride;
}
}
private bool IsExplicitClassOverride
{
get
{
if (!_packedFlags.IsExplicitOverrideIsPopulated)
{
var unused = this.ExplicitInterfaceImplementations;
Debug.Assert(_packedFlags.IsExplicitOverrideIsPopulated);
}
return _packedFlags.IsExplicitClassOverride;
}
}
private bool IsDestructor => this.MethodKind == MethodKind.Destructor;
public override bool ReturnsVoid => this.ReturnType.IsVoidType();
internal override int ParameterCount
{
get
{
if (_lazySignature != null)
{
return _lazySignature.Parameters.Length;
}
try
{
int parameterCount;
int typeParameterCount;
MetadataDecoder.GetSignatureCountsOrThrow(_containingType.ContainingPEModule.Module, _handle,
out parameterCount, out typeParameterCount);
return parameterCount;
}
catch (BadImageFormatException)
{
return Parameters.Length;
}
}
}
public override ImmutableArray<ParameterSymbol> Parameters => Signature.Parameters;
internal PEParameterSymbol ReturnTypeParameter => Signature.ReturnParam;
public override RefKind RefKind => Signature.ReturnParam.RefKind;
public override TypeWithAnnotations ReturnTypeWithAnnotations => Signature.ReturnParam.TypeWithAnnotations;
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => Signature.ReturnParam.FlowAnalysisAnnotations;
public override ImmutableHashSet<string> ReturnNotNullIfParameterNotNull => Signature.ReturnParam.NotNullIfParameterNotNull;
public override FlowAnalysisAnnotations FlowAnalysisAnnotations
{
get
{
if (!_packedFlags.IsDoesNotReturnPopulated)
{
var moduleSymbol = _containingType.ContainingPEModule;
bool doesNotReturn = moduleSymbol.Module.HasDoesNotReturnAttribute(_handle);
_packedFlags.InitializeDoesNotReturn(doesNotReturn);
}
return _packedFlags.DoesNotReturn ? FlowAnalysisAnnotations.DoesNotReturn : FlowAnalysisAnnotations.None;
}
}
internal override ImmutableArray<string> NotNullMembers
{
get
{
if (!_packedFlags.IsMemberNotNullPopulated)
{
PopulateMemberNotNullData();
}
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return ImmutableArray<string>.Empty;
}
else
{
var result = uncommonFields._lazyNotNullMembers;
return result.IsDefault ? ImmutableArray<string>.Empty : result;
}
}
}
private void PopulateMemberNotNullData()
{
var module = _containingType.ContainingPEModule.Module;
var memberNotNull = module.GetMemberNotNullAttributeValues(_handle);
Debug.Assert(!memberNotNull.IsDefault);
if (!memberNotNull.IsEmpty)
{
InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyNotNullMembers, memberNotNull);
}
var (memberNotNullWhenTrue, memberNotNullWhenFalse) = module.GetMemberNotNullWhenAttributeValues(_handle);
Debug.Assert(!memberNotNullWhenTrue.IsDefault);
if (!memberNotNullWhenTrue.IsEmpty)
{
InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyNotNullMembersWhenTrue, memberNotNullWhenTrue);
}
Debug.Assert(!memberNotNullWhenFalse.IsDefault);
if (!memberNotNullWhenFalse.IsEmpty)
{
InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyNotNullMembersWhenFalse, memberNotNullWhenFalse);
}
_packedFlags.SetIsMemberNotNullPopulated();
}
internal override ImmutableArray<string> NotNullWhenTrueMembers
{
get
{
if (!_packedFlags.IsMemberNotNullPopulated)
{
PopulateMemberNotNullData();
}
var uncommonFields = _uncommonFields;
if (uncommonFields is null)
{
return ImmutableArray<string>.Empty;
}
else
{
var result = uncommonFields._lazyNotNullMembersWhenTrue;
return result.IsDefault ? ImmutableArray<string>.Empty : result;
}
}
}
internal override ImmutableArray<string> NotNullWhenFalseMembers
{
get
{
if (!_packedFlags.IsMemberNotNullPopulated)
{
PopulateMemberNotNullData();
}
var uncommonFields = _uncommonFields;
if (uncommonFields is null)
{
return ImmutableArray<string>.Empty;
}
else
{
var result = uncommonFields._lazyNotNullMembersWhenFalse;
return result.IsDefault ? ImmutableArray<string>.Empty : result;
}
}
}
public override ImmutableArray<CustomModifier> RefCustomModifiers => Signature.ReturnParam.RefCustomModifiers;
/// <summary>
/// Associate the method with a particular property. Returns
/// false if the method is already associated with a property or event.
/// </summary>
internal bool SetAssociatedProperty(PEPropertySymbol propertySymbol, MethodKind methodKind)
{
Debug.Assert((methodKind == MethodKind.PropertyGet) || (methodKind == MethodKind.PropertySet));
return this.SetAssociatedPropertyOrEvent(propertySymbol, methodKind);
}
/// <summary>
/// Associate the method with a particular event. Returns
/// false if the method is already associated with a property or event.
/// </summary>
internal bool SetAssociatedEvent(PEEventSymbol eventSymbol, MethodKind methodKind)
{
Debug.Assert((methodKind == MethodKind.EventAdd) || (methodKind == MethodKind.EventRemove));
return this.SetAssociatedPropertyOrEvent(eventSymbol, methodKind);
}
private bool SetAssociatedPropertyOrEvent(Symbol propertyOrEventSymbol, MethodKind methodKind)
{
if ((object)_associatedPropertyOrEventOpt == null)
{
Debug.Assert(TypeSymbol.Equals(propertyOrEventSymbol.ContainingType, _containingType, TypeCompareKind.ConsiderEverything2));
// No locking required since SetAssociatedProperty/SetAssociatedEvent will only be called
// by the thread that created the method symbol (and will be called before the method
// symbol is added to the containing type members and available to other threads).
_associatedPropertyOrEventOpt = propertyOrEventSymbol;
// NOTE: may be overwriting an existing value.
Debug.Assert(
_packedFlags.MethodKind == default(MethodKind) ||
_packedFlags.MethodKind == MethodKind.Ordinary ||
_packedFlags.MethodKind == MethodKind.ExplicitInterfaceImplementation);
_packedFlags.MethodKind = methodKind;
return true;
}
return false;
}
/// <summary>
/// internal for testing purpose
/// </summary>
internal SignatureData Signature => _lazySignature ?? LoadSignature();
private SignatureData LoadSignature()
{
var moduleSymbol = _containingType.ContainingPEModule;
SignatureHeader signatureHeader;
BadImageFormatException mrEx;
ParamInfo<TypeSymbol>[] paramInfo = new MetadataDecoder(moduleSymbol, this).GetSignatureForMethod(_handle, out signatureHeader, out mrEx);
bool makeBad = (mrEx != null);
// If method is not generic, let's assign empty list for type parameters
if (!signatureHeader.IsGeneric &&
_lazyTypeParameters.IsDefault)
{
ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters,
ImmutableArray<TypeParameterSymbol>.Empty);
}
int count = paramInfo.Length - 1;
ImmutableArray<ParameterSymbol> @params;
bool isBadParameter;
if (count > 0)
{
var builder = ImmutableArray.CreateBuilder<ParameterSymbol>(count);
for (int i = 0; i < count; i++)
{
builder.Add(PEParameterSymbol.Create(
moduleSymbol, this, this.IsMetadataVirtual(), i,
paramInfo[i + 1], nullableContext: this, isReturn: false, out isBadParameter));
if (isBadParameter)
{
makeBad = true;
}
}
@params = builder.ToImmutable();
}
else
{
@params = ImmutableArray<ParameterSymbol>.Empty;
}
// Dynamify object type if necessary
var returnType = paramInfo[0].Type.AsDynamicIfNoPia(_containingType);
paramInfo[0].Type = returnType;
var returnParam = PEParameterSymbol.Create(
moduleSymbol, this, this.IsMetadataVirtual(), 0,
paramInfo[0], nullableContext: this, isReturn: true, out isBadParameter);
if (makeBad || isBadParameter)
{
InitializeUseSiteDiagnostic(new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
}
var signature = new SignatureData(signatureHeader, @params, returnParam);
return InterlockedOperations.Initialize(ref _lazySignature, signature);
}
public override BlobHandle MetadataSignatureHandle
{
get
{
try
{
return _containingType.ContainingPEModule.Module.GetMethodSignatureOrThrow(_handle);
}
catch (BadImageFormatException)
{
return default;
}
}
}
public override ImmutableArray<TypeParameterSymbol> TypeParameters
{
get
{
DiagnosticInfo diagnosticInfo = null;
var typeParams = EnsureTypeParametersAreLoaded(ref diagnosticInfo);
if (diagnosticInfo != null)
{
InitializeUseSiteDiagnostic(new UseSiteInfo<AssemblySymbol>(diagnosticInfo));
}
return typeParams;
}
}
private ImmutableArray<TypeParameterSymbol> EnsureTypeParametersAreLoaded(ref DiagnosticInfo diagnosticInfo)
{
var typeParams = _lazyTypeParameters;
if (!typeParams.IsDefault)
{
return typeParams;
}
return InterlockedOperations.Initialize(ref _lazyTypeParameters, LoadTypeParameters(ref diagnosticInfo));
}
private ImmutableArray<TypeParameterSymbol> LoadTypeParameters(ref DiagnosticInfo diagnosticInfo)
{
try
{
var moduleSymbol = _containingType.ContainingPEModule;
var gpHandles = moduleSymbol.Module.GetGenericParametersForMethodOrThrow(_handle);
if (gpHandles.Count == 0)
{
return ImmutableArray<TypeParameterSymbol>.Empty;
}
else
{
var ownedParams = ImmutableArray.CreateBuilder<TypeParameterSymbol>(gpHandles.Count);
for (int i = 0; i < gpHandles.Count; i++)
{
ownedParams.Add(new PETypeParameterSymbol(moduleSymbol, this, (ushort)i, gpHandles[i]));
}
return ownedParams.ToImmutable();
}
}
catch (BadImageFormatException)
{
diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this);
return ImmutableArray<TypeParameterSymbol>.Empty;
}
}
public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => IsGenericMethod ? GetTypeParametersAsTypeArguments() : ImmutableArray<TypeWithAnnotations>.Empty;
public override Symbol AssociatedSymbol => _associatedPropertyOrEventOpt;
public override bool IsExtensionMethod
{
get
{
// This is also populated by loading attributes, but
// loading attributes is more expensive, so we should only do it if
// attributes are requested.
if (!_packedFlags.IsExtensionMethodIsPopulated)
{
bool isExtensionMethod = false;
if (this.MethodKind == MethodKind.Ordinary && IsValidExtensionMethodSignature()
&& this.ContainingType.MightContainExtensionMethods)
{
var moduleSymbol = _containingType.ContainingPEModule;
isExtensionMethod = moduleSymbol.Module.HasExtensionAttribute(_handle, ignoreCase: false);
}
_packedFlags.InitializeIsExtensionMethod(isExtensionMethod);
}
return _packedFlags.IsExtensionMethod;
}
}
public override ImmutableArray<Location> Locations => _containingType.ContainingPEModule.MetadataLocation.Cast<MetadataLocation, Location>();
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => ImmutableArray<SyntaxReference>.Empty;
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
if (!_packedFlags.IsCustomAttributesPopulated)
{
// Compute the value
var attributeData = default(ImmutableArray<CSharpAttributeData>);
var containingPEModuleSymbol = _containingType.ContainingPEModule;
// Could this possibly be an extension method?
bool isExtensionAlreadySet = _packedFlags.IsExtensionMethodIsPopulated;
bool checkForExtension = isExtensionAlreadySet
? _packedFlags.IsExtensionMethod
: this.MethodKind == MethodKind.Ordinary
&& IsValidExtensionMethodSignature()
&& _containingType.MightContainExtensionMethods;
bool isReadOnlyAlreadySet = _packedFlags.IsReadOnlyPopulated;
bool checkForIsReadOnly = isReadOnlyAlreadySet
? _packedFlags.IsReadOnly
: IsValidReadOnlyTarget;
bool checkForRequiredMembers = this.ShouldCheckRequiredMembers() && this.ContainingType.HasAnyRequiredMembers;
bool isExtensionMethod = false;
bool isReadOnly = false;
if (checkForExtension || checkForIsReadOnly || checkForRequiredMembers)
{
attributeData = containingPEModuleSymbol.GetCustomAttributesForToken(_handle,
filteredOutAttribute1: out CustomAttributeHandle extensionAttribute,
filterOut1: AttributeDescription.CaseSensitiveExtensionAttribute,
filteredOutAttribute2: out CustomAttributeHandle isReadOnlyAttribute,
filterOut2: AttributeDescription.IsReadOnlyAttribute,
filteredOutAttribute3: out _,
filterOut3: (checkForRequiredMembers && DeriveCompilerFeatureRequiredDiagnostic() is null) ? AttributeDescription.CompilerFeatureRequiredAttribute : default,
filteredOutAttribute4: out _,
filterOut4: (checkForRequiredMembers && ObsoleteAttributeData is null) ? AttributeDescription.ObsoleteAttribute : default,
filteredOutAttribute5: out _,
filterOut5: default,
filteredOutAttribute6: out _,
filterOut6: default);
isExtensionMethod = !extensionAttribute.IsNil;
isReadOnly = !isReadOnlyAttribute.IsNil;
}
else
{
containingPEModuleSymbol.LoadCustomAttributes(_handle,
ref attributeData);
}
if (!isExtensionAlreadySet)
{
_packedFlags.InitializeIsExtensionMethod(isExtensionMethod);
}
if (!isReadOnlyAlreadySet)
{
_packedFlags.InitializeIsReadOnly(isReadOnly);
}
// Store the result in uncommon fields only if it's not empty.
Debug.Assert(!attributeData.IsDefault);
if (!attributeData.IsEmpty)
{
attributeData = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyCustomAttributes, attributeData);
}
_packedFlags.SetIsCustomAttributesPopulated();
return attributeData;
}
// Retrieve cached or inferred value.
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return ImmutableArray<CSharpAttributeData>.Empty;
}
else
{
var attributeData = uncommonFields._lazyCustomAttributes;
return attributeData.IsDefault
? InterlockedOperations.Initialize(ref uncommonFields._lazyCustomAttributes, ImmutableArray<CSharpAttributeData>.Empty)
: attributeData;
}
}
internal override IEnumerable<CSharpAttributeData> GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder) => GetAttributes();
public override ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes() => Signature.ReturnParam.GetAttributes();
internal override byte? GetNullableContextValue()
{
byte? value;
if (!_packedFlags.TryGetNullableContext(out value))
{
value = _containingType.ContainingPEModule.Module.HasNullableContextAttribute(_handle, out byte arg) ?
arg :
_containingType.GetNullableContextValue();
_packedFlags.SetNullableContext(value);
}
return value;
}
internal override byte? GetLocalNullableContextValue()
{
throw ExceptionUtilities.Unreachable();
}
public override MethodKind MethodKind
{
get
{
if (!_packedFlags.MethodKindIsPopulated)
{
_packedFlags.InitializeMethodKind(this.ComputeMethodKind());
}
return _packedFlags.MethodKind;
}
}
private bool IsValidExtensionMethodSignature()
{
if (!this.IsStatic)
{
return false;
}
var parameters = this.Parameters;
if (parameters.Length == 0)
{
return false;
}
var parameter = parameters[0];
switch (parameter.RefKind)
{
case RefKind.None:
case RefKind.Ref:
case RefKind.In:
case RefKind.RefReadOnlyParameter:
return !parameter.IsParams;
default:
return false;
}
}
private bool IsValidUserDefinedOperatorSignature(int parameterCount)
{
if (this.ReturnsVoid || this.IsGenericMethod || this.IsVararg || this.ParameterCount != parameterCount || this.IsParams())
{
return false;
}
if (this.ParameterRefKinds.IsDefault)
{
return true;
}
foreach (var kind in this.ParameterRefKinds)
{
switch (kind)
{
case RefKind.None:
case RefKind.In:
continue;
case RefKind.Out:
case RefKind.Ref:
case RefKind.RefReadOnlyParameter:
return false;
default:
throw ExceptionUtilities.UnexpectedValue(kind);
}
}
return true;
}
private MethodKind ComputeMethodKind()
{
if (this.HasSpecialName)
{
if (_name.StartsWith(".", StringComparison.Ordinal))
{
// 10.5.1 Instance constructor
// An instance constructor shall be an instance (not static or virtual) method,
// it shall be named .ctor, and marked instance, rtspecialname, and specialname (§15.4.2.6).
// An instance constructor can have parameters, but shall not return a value.
// An instance constructor cannot take generic type parameters.
// 10.5.3 Type initializer
// This method shall be static, take no parameters, return no value,
// be marked with rtspecialname and specialname (§15.4.2.6), and be named .cctor.
if ((Flags & (MethodAttributes.RTSpecialName | MethodAttributes.Virtual)) == MethodAttributes.RTSpecialName &&
_name.Equals(this.IsStatic ? WellKnownMemberNames.StaticConstructorName : WellKnownMemberNames.InstanceConstructorName) &&
this.ReturnsVoid && this.Arity == 0)
{
if (this.IsStatic)
{
if (Parameters.Length == 0)
{
return MethodKind.StaticConstructor;
}
}
else
{
return MethodKind.Constructor;
}
}
return MethodKind.Ordinary;
}
if (!this.HasRuntimeSpecialName && this.IsStatic && this.DeclaredAccessibility == Accessibility.Public)
{
switch (_name)
{
case WellKnownMemberNames.CheckedAdditionOperatorName:
case WellKnownMemberNames.AdditionOperatorName:
case WellKnownMemberNames.BitwiseAndOperatorName:
case WellKnownMemberNames.BitwiseOrOperatorName:
case WellKnownMemberNames.CheckedDivisionOperatorName:
case WellKnownMemberNames.DivisionOperatorName:
case WellKnownMemberNames.EqualityOperatorName:
case WellKnownMemberNames.ExclusiveOrOperatorName:
case WellKnownMemberNames.GreaterThanOperatorName:
case WellKnownMemberNames.GreaterThanOrEqualOperatorName:
case WellKnownMemberNames.InequalityOperatorName:
case WellKnownMemberNames.LeftShiftOperatorName:
case WellKnownMemberNames.LessThanOperatorName:
case WellKnownMemberNames.LessThanOrEqualOperatorName:
case WellKnownMemberNames.ModulusOperatorName:
case WellKnownMemberNames.CheckedMultiplyOperatorName:
case WellKnownMemberNames.MultiplyOperatorName:
case WellKnownMemberNames.RightShiftOperatorName:
case WellKnownMemberNames.UnsignedRightShiftOperatorName:
case WellKnownMemberNames.CheckedSubtractionOperatorName:
case WellKnownMemberNames.SubtractionOperatorName:
return IsValidUserDefinedOperatorSignature(2) ? MethodKind.UserDefinedOperator : MethodKind.Ordinary;
case WellKnownMemberNames.CheckedDecrementOperatorName:
case WellKnownMemberNames.DecrementOperatorName:
case WellKnownMemberNames.FalseOperatorName:
case WellKnownMemberNames.CheckedIncrementOperatorName:
case WellKnownMemberNames.IncrementOperatorName:
case WellKnownMemberNames.LogicalNotOperatorName:
case WellKnownMemberNames.OnesComplementOperatorName:
case WellKnownMemberNames.TrueOperatorName:
case WellKnownMemberNames.CheckedUnaryNegationOperatorName:
case WellKnownMemberNames.UnaryNegationOperatorName:
case WellKnownMemberNames.UnaryPlusOperatorName:
return IsValidUserDefinedOperatorSignature(1) ? MethodKind.UserDefinedOperator : MethodKind.Ordinary;
case WellKnownMemberNames.ImplicitConversionName:
case WellKnownMemberNames.ExplicitConversionName:
case WellKnownMemberNames.CheckedExplicitConversionName:
return IsValidUserDefinedOperatorSignature(1) ? MethodKind.Conversion : MethodKind.Ordinary;
//case WellKnownMemberNames.ConcatenateOperatorName:
//case WellKnownMemberNames.ExponentOperatorName:
//case WellKnownMemberNames.IntegerDivisionOperatorName:
//case WellKnownMemberNames.LikeOperatorName:
//// Non-C#-supported overloaded operator
//return MethodKind.Ordinary;
}
return MethodKind.Ordinary;
}
}
if (!this.IsStatic)
{
switch (_name)
{
case WellKnownMemberNames.DestructorName:
if ((this.ContainingType.TypeKind == TypeKind.Class && this.IsRuntimeFinalizer(skipFirstMethodKindCheck: true)) ||
this.IsExplicitFinalizerOverride)
{
return MethodKind.Destructor;
}
break;
case WellKnownMemberNames.DelegateInvokeName:
if (_containingType.TypeKind == TypeKind.Delegate)
{
return MethodKind.DelegateInvoke;
}
break;
}
}
// Note: this is expensive, so check it last
// Note: method being processed may have an explicit method .override but still be
// publicly accessible, the decision here is being made based on the method's name
if (!SyntaxFacts.IsValidIdentifier(this.Name) && !this.ExplicitInterfaceImplementations.IsEmpty)
{
return MethodKind.ExplicitInterfaceImplementation;
}
return MethodKind.Ordinary;
}
internal override Cci.CallingConvention CallingConvention => (Cci.CallingConvention)Signature.Header.RawValue;
public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations
{
get
{
var explicitInterfaceImplementations = _lazyExplicitMethodImplementations;
if (!explicitInterfaceImplementations.IsDefault)
{
return explicitInterfaceImplementations;
}
var moduleSymbol = _containingType.ContainingPEModule;
// Context: we need the containing type of this method as context so that we can substitute appropriately into
// any generic interfaces that we might be explicitly implementing. There is no reason to pass in the method
// context, however, because any method type parameters will belong to the implemented (i.e. interface) method,
// which we do not yet know.
var explicitlyOverriddenMethods = new MetadataDecoder(moduleSymbol, _containingType).GetExplicitlyOverriddenMethods(_containingType.Handle, _handle, this.ContainingType);
//avoid allocating a builder in the common case
var anyToRemove = false;
var sawObjectFinalize = false;
foreach (var method in explicitlyOverriddenMethods)
{
if (!method.ContainingType.IsInterface)
{
anyToRemove = true;
sawObjectFinalize =
(method.ContainingType.SpecialType == SpecialType.System_Object &&
method.Name == WellKnownMemberNames.DestructorName && // Cheaper than MethodKind.
method.MethodKind == MethodKind.Destructor);
}
if (anyToRemove && sawObjectFinalize)
{
break;
}
}
explicitInterfaceImplementations = explicitlyOverriddenMethods;
if (anyToRemove)
{
var explicitInterfaceImplementationsBuilder = ArrayBuilder<MethodSymbol>.GetInstance();
foreach (var method in explicitlyOverriddenMethods)
{
if (method.ContainingType.IsInterface)
{
explicitInterfaceImplementationsBuilder.Add(method);
}
}
explicitInterfaceImplementations = explicitInterfaceImplementationsBuilder.ToImmutableAndFree();
MethodSymbol uniqueClassOverride = null;
foreach (MethodSymbol method in explicitlyOverriddenMethods)
{
if (method.ContainingType.IsClassType())
{
if (uniqueClassOverride is { })
{
// not unique
uniqueClassOverride = null;
break;
}
uniqueClassOverride = method;
}
}
if (uniqueClassOverride is { })
{
Interlocked.CompareExchange(ref AccessUncommonFields()._lazyExplicitClassOverride, uniqueClassOverride, null);
}
}
// CONSIDER: what we'd really like to do is set this bit only in cases where the explicitly
// overridden method matches the method that will be returned by MethodSymbol.OverriddenMethod.
// Unfortunately, this MethodSymbol will not be sufficiently constructed (need IsOverride and MethodKind,
// which depend on this property) to determine which method OverriddenMethod will return.
_packedFlags.InitializeIsExplicitOverride(isExplicitFinalizerOverride: sawObjectFinalize, isExplicitClassOverride: anyToRemove);
return InterlockedOperations.Initialize(ref _lazyExplicitMethodImplementations, explicitInterfaceImplementations);
}
}
/// <summary>
/// If a methodimpl record indicates a unique overridden method, that method. Otherwise null.
/// </summary>
internal MethodSymbol ExplicitlyOverriddenClassMethod
{
get
{
return IsExplicitClassOverride ? AccessUncommonFields()._lazyExplicitClassOverride : null;
}
}
internal override bool IsDeclaredReadOnly
{
get
{
if (!_packedFlags.IsReadOnlyPopulated)
{
bool isReadOnly = false;
if (IsValidReadOnlyTarget)
{
var moduleSymbol = _containingType.ContainingPEModule;
isReadOnly = moduleSymbol.Module.HasIsReadOnlyAttribute(_handle);
}
_packedFlags.InitializeIsReadOnly(isReadOnly);
}
return _packedFlags.IsReadOnly;
}
}
internal override bool IsInitOnly
{
get
{
if (!_packedFlags.IsInitOnlyPopulated)
{
bool isInitOnly = !this.IsStatic &&
this.MethodKind == MethodKind.PropertySet &&
ReturnTypeWithAnnotations.CustomModifiers.HasIsExternalInitModifier();
_packedFlags.InitializeIsInitOnly(isInitOnly);
}
return _packedFlags.IsInitOnly;
}
}
public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return PEDocumentationCommentUtils.GetDocumentationComment(this, _containingType.ContainingPEModule, preferredCulture, cancellationToken, ref AccessUncommonFields()._lazyDocComment);
}
internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
if (!_packedFlags.IsUseSiteDiagnosticPopulated)
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(PrimaryDependency);
CalculateUseSiteDiagnostic(ref result);
var diagnosticInfo = result.DiagnosticInfo;
MergeUseSiteDiagnostics(ref diagnosticInfo, DeriveCompilerFeatureRequiredDiagnostic());
EnsureTypeParametersAreLoaded(ref diagnosticInfo);
if (diagnosticInfo == null &&
_containingType.ContainingPEModule.RefSafetyRulesVersion == PEModuleSymbol.RefSafetyRulesAttributeVersion.UnrecognizedAttribute)
{
diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_UnrecognizedRefSafetyRulesAttributeVersion, this);
}
if (diagnosticInfo == null && GetUnmanagedCallersOnlyAttributeData(forceComplete: true) is UnmanagedCallersOnlyAttributeData data)
{
Debug.Assert(!ReferenceEquals(data, UnmanagedCallersOnlyAttributeData.Uninitialized));
Debug.Assert(!ReferenceEquals(data, UnmanagedCallersOnlyAttributeData.AttributePresentDataNotBound));
if (CheckAndReportValidUnmanagedCallersOnlyTarget(node: null, diagnostics: null))
{
diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this);
}
}
if (diagnosticInfo == null && this.ShouldCheckRequiredMembers() && ContainingType.HasRequiredMembersError)
{
diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_RequiredMembersInvalid, ContainingType);
}
return InitializeUseSiteDiagnostic(result.AdjustDiagnosticInfo(diagnosticInfo));
}
return GetCachedUseSiteInfo();
}
private DiagnosticInfo DeriveCompilerFeatureRequiredDiagnostic()
{
var containingModule = _containingType.ContainingPEModule;
var decoder = new MetadataDecoder(containingModule, this);
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, containingModule, Handle, allowedFeatures: MethodKind == MethodKind.Constructor ? CompilerFeatureRequiredFeatures.RequiredMembers : CompilerFeatureRequiredFeatures.None, decoder);
if (diag != null)
{
return diag;
}
diag = Signature.ReturnParam.DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
foreach (var param in Parameters)
{
diag = ((PEParameterSymbol)param).DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
}
foreach (var typeParam in TypeParameters)
{
diag = ((PETypeParameterSymbol)typeParam).DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
}
return _containingType.GetCompilerFeatureRequiredDiagnostic();
}
private UseSiteInfo<AssemblySymbol> GetCachedUseSiteInfo()
{
return (_uncommonFields?._lazyCachedUseSiteInfo ?? default).ToUseSiteInfo(PrimaryDependency);
}
private UseSiteInfo<AssemblySymbol> InitializeUseSiteDiagnostic(UseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (_packedFlags.IsUseSiteDiagnosticPopulated)
{
return GetCachedUseSiteInfo();
}
if (useSiteInfo.DiagnosticInfo is object || !useSiteInfo.SecondaryDependencies.IsNullOrEmpty())
{
useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromDefault(PrimaryDependency, useSiteInfo);
}
_packedFlags.SetIsUseSiteDiagnosticPopulated();
return useSiteInfo;
}
internal override ImmutableArray<string> GetAppliedConditionalSymbols()
{
if (!_packedFlags.IsConditionalPopulated)
{
var result = _containingType.ContainingPEModule.Module.GetConditionalAttributeValues(_handle);
Debug.Assert(!result.IsDefault);
if (!result.IsEmpty)
{
result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyConditionalAttributeSymbols, result);
}
_packedFlags.SetIsConditionalAttributePopulated();
return result;
}
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return ImmutableArray<string>.Empty;
}
else
{
var result = uncommonFields._lazyConditionalAttributeSymbols;
return result.IsDefault
? InterlockedOperations.Initialize(ref uncommonFields._lazyConditionalAttributeSymbols, ImmutableArray<string>.Empty)
: result;
}
}
protected override bool HasSetsRequiredMembersImpl
{
get
{
Debug.Assert(MethodKind == MethodKind.Constructor);
if (!_packedFlags.HasSetsRequiredMembersPopulated)
{
var result = _containingType.ContainingPEModule.Module.HasAttribute(_handle, AttributeDescription.SetsRequiredMembersAttribute);
_packedFlags.InitializeSetsRequiredMembersBit(result);
}
return _packedFlags.HasSetsRequiredMembers;
}
}
internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree)
{
throw ExceptionUtilities.Unreachable();
}
internal override ObsoleteAttributeData ObsoleteAttributeData
{
get
{
if (!_packedFlags.IsObsoleteAttributePopulated)
{
var result = ObsoleteAttributeHelpers.GetObsoleteDataFromMetadata(_handle, (PEModuleSymbol)ContainingModule, ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: MethodKind == MethodKind.Constructor);
if (result != null)
{
result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyObsoleteAttributeData, result, ObsoleteAttributeData.Uninitialized);
}
_packedFlags.SetIsObsoleteAttributePopulated();
return result;
}
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return null;
}
else
{
var result = uncommonFields._lazyObsoleteAttributeData;
return ReferenceEquals(result, ObsoleteAttributeData.Uninitialized)
? InterlockedOperations.Initialize(ref uncommonFields._lazyObsoleteAttributeData, initializedValue: null, ObsoleteAttributeData.Uninitialized)
: result;
}
}
}
#nullable enable
internal override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete)
{
if (!_packedFlags.IsUnmanagedCallersOnlyAttributePopulated)
{
var containingModule = (PEModuleSymbol)ContainingModule;
var unmanagedCallersOnlyData = containingModule.Module.TryGetUnmanagedCallersOnlyAttribute(_handle, new MetadataDecoder(containingModule),
static (name, value, isField) => MethodSymbol.TryDecodeUnmanagedCallersOnlyCallConvsField(name, value, isField, location: null, diagnostics: null));
Debug.Assert(!ReferenceEquals(unmanagedCallersOnlyData, UnmanagedCallersOnlyAttributeData.Uninitialized)
&& !ReferenceEquals(unmanagedCallersOnlyData, UnmanagedCallersOnlyAttributeData.AttributePresentDataNotBound));
var result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyUnmanagedCallersOnlyAttributeData,
unmanagedCallersOnlyData,
UnmanagedCallersOnlyAttributeData.Uninitialized);
_packedFlags.SetIsUnmanagedCallersOnlyAttributePopulated();
return result;
}
return _uncommonFields?._lazyUnmanagedCallersOnlyAttributeData;
}
#nullable disable
internal override bool GenerateDebugInfo => false;
internal override OverriddenOrHiddenMembersResult OverriddenOrHiddenMembers
{
get
{
if (!_packedFlags.IsOverriddenOrHiddenMembersPopulated)
{
var result = base.OverriddenOrHiddenMembers;
Debug.Assert(result != null);
if (result != OverriddenOrHiddenMembersResult.Empty)
{
result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyOverriddenOrHiddenMembersResult, result);
}
_packedFlags.SetIsOverriddenOrHiddenMembersPopulated();
return result;
}
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return OverriddenOrHiddenMembersResult.Empty;
}
return uncommonFields._lazyOverriddenOrHiddenMembersResult ?? InterlockedOperations.Initialize(ref uncommonFields._lazyOverriddenOrHiddenMembersResult, OverriddenOrHiddenMembersResult.Empty);
}
}
internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
{
throw ExceptionUtilities.Unreachable();
}
internal override void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
{
throw ExceptionUtilities.Unreachable();
}
public override bool AreLocalsZeroed
{
get { throw ExceptionUtilities.Unreachable(); }
}
// perf, not correctness
internal override CSharpCompilation DeclaringCompilation => null;
// Internal for unit test
internal bool TestIsExtensionBitSet => _packedFlags.IsExtensionMethodIsPopulated;
// Internal for unit test
internal bool TestIsExtensionBitTrue => _packedFlags.IsExtensionMethod;
internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable();
internal sealed override bool HasUnscopedRefAttribute
{
get
{
if (!_packedFlags.IsUnscopedRefPopulated)
{
var moduleSymbol = _containingType.ContainingPEModule;
bool unscopedRef = moduleSymbol.Module.HasUnscopedRefAttribute(_handle);
_packedFlags.InitializeIsUnscopedRef(unscopedRef);
}
return _packedFlags.IsUnscopedRef;
}
}
internal sealed override bool UseUpdatedEscapeRules => ContainingModule.UseUpdatedEscapeRules;
internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument)
{
builderArgument = _containingType.ContainingPEModule.TryDecodeAttributeWithTypeArgument(this.Handle, AttributeDescription.AsyncMethodBuilderAttribute);
return builderArgument is not null;
}
internal override int TryGetOverloadResolutionPriority()
{
if (!_packedFlags.IsOverloadResolutionPriorityPopulated)
{
if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority) &&
priority != 0)
{
Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0);
}
#if DEBUG
else
{
// 0 is the default if nothing is present in metadata, and we don't care about preserving the difference between "not present" and "set to the default value".
Debug.Assert(_uncommonFields is null or { _lazyOverloadResolutionPriority: 0 });
}
#endif
_packedFlags.SetIsOverloadResolutionPriorityPopulated();
}
return _uncommonFields?._lazyOverloadResolutionPriority ?? 0;
}
}
}
|