|
// 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.Linq;
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 properties imported from a PE/module.
/// </summary>
internal class PEPropertySymbol
: PropertySymbol
{
private readonly string _name;
private readonly PENamedTypeSymbol _containingType;
private readonly PropertyDefinitionHandle _handle;
private readonly ImmutableArray<ParameterSymbol> _parameters;
private readonly RefKind _refKind;
private readonly TypeWithAnnotations _propertyTypeWithAnnotations;
private readonly PEMethodSymbol _getMethod;
private readonly PEMethodSymbol _setMethod;
#nullable enable
private UncommonFields? _uncommonFields;
#nullable disable
// CONSIDER: the parameters could be computed lazily (as in PEMethodSymbol).
// CONSIDER: if the parameters were computed lazily, ParameterCount could be overridden to fall back on the signature (as in PEMethodSymbol).
// Distinct accessibility value to represent unset.
private const int UnsetAccessibility = -1;
private int _declaredAccessibility = UnsetAccessibility;
private PackedFlags _flags;
private struct PackedFlags
{
// Layout:
// |....................c|o|d|uu|rr|c|n|s|
//
// s = special name flag. 1 bit
// n = runtime special name flag. 1 bit
// c = call methods directly flag. 1 bit
// r = Required member. 2 bits (1 bit for value + 1 completion bit).
// u = Unscoped ref. 2 bits (1 bit for value + 1 completion bit).
// d = Use site diagnostic flag. 1 bit
// o = Obsolete flag. 1 bit
// c = Custom attributes flag. 1 bit
// p = Overload resolution priority populated. 1 bit
private const int IsSpecialNameFlag = 1 << 0;
private const int IsRuntimeSpecialNameFlag = 1 << 1;
private const int CallMethodsDirectlyFlag = 1 << 2;
private const int HasRequiredMemberAttribute = 1 << 4;
private const int RequiredMemberCompletionBit = 1 << 5;
private const int HasUnscopedRefAttribute = 1 << 6;
private const int UnscopedRefCompletionBit = 1 << 7;
private const int IsUseSiteDiagnosticPopulatedBit = 1 << 8;
private const int IsObsoleteAttributePopulatedBit = 1 << 9;
private const int IsCustomAttributesPopulatedBit = 1 << 10;
private const int IsOverloadResolutionPriorityPopulatedBit = 1 << 11;
private int _bits;
public PackedFlags(bool isSpecialName, bool isRuntimeSpecialName, bool callMethodsDirectly)
{
_bits = (isSpecialName ? IsSpecialNameFlag : 0)
| (isRuntimeSpecialName ? IsRuntimeSpecialNameFlag : 0)
| (callMethodsDirectly ? CallMethodsDirectlyFlag : 0);
}
public void SetHasRequiredMemberAttribute(bool isRequired)
{
var bitsToSet = (isRequired ? HasRequiredMemberAttribute : 0) | RequiredMemberCompletionBit;
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public readonly bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute)
{
if ((_bits & RequiredMemberCompletionBit) != 0)
{
hasRequiredMemberAttribute = (_bits & HasRequiredMemberAttribute) != 0;
return true;
}
hasRequiredMemberAttribute = false;
return false;
}
public void SetHasUnscopedRefAttribute(bool unscopedRef)
{
var bitsToSet = (unscopedRef ? HasUnscopedRefAttribute : 0) | UnscopedRefCompletionBit;
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public readonly bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute)
{
if ((_bits & UnscopedRefCompletionBit) != 0)
{
hasUnscopedRefAttribute = (_bits & HasUnscopedRefAttribute) != 0;
return true;
}
hasUnscopedRefAttribute = false;
return false;
}
public readonly bool IsSpecialName => (_bits & IsSpecialNameFlag) != 0;
public readonly bool IsRuntimeSpecialName => (_bits & IsRuntimeSpecialNameFlag) != 0;
public readonly bool CallMethodsDirectly => (_bits & CallMethodsDirectlyFlag) != 0;
public void SetUseSiteDiagnosticPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsUseSiteDiagnosticPopulatedBit);
}
public bool IsUseSiteDiagnosticPopulated => (Volatile.Read(ref _bits) & IsUseSiteDiagnosticPopulatedBit) != 0;
public void SetObsoleteAttributePopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsObsoleteAttributePopulatedBit);
}
public bool IsObsoleteAttributePopulated => (Volatile.Read(ref _bits) & IsObsoleteAttributePopulatedBit) != 0;
public void SetCustomAttributesPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsCustomAttributesPopulatedBit);
}
public bool IsCustomAttributesPopulated => (Volatile.Read(ref _bits) & IsCustomAttributesPopulatedBit) != 0;
public void SetOverloadResolutionPriorityPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsOverloadResolutionPriorityPopulatedBit);
}
public bool IsOverloadResolutionPriorityPopulated => (Volatile.Read(ref _bits) & IsOverloadResolutionPriorityPopulatedBit) != 0;
}
/// <summary>
/// This type is used to hold lazily-initialized fields that many properties 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="_flags"/>.
/// 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="_flags"/>. 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 ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
public Tuple<CultureInfo, string> _lazyDocComment;
public CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;
public ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
public int _lazyOverloadResolutionPriority;
}
internal static PEPropertySymbol Create(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol containingType,
PropertyDefinitionHandle handle,
PEMethodSymbol getMethod,
PEMethodSymbol setMethod)
{
Debug.Assert((object)moduleSymbol != null);
Debug.Assert((object)containingType != null);
Debug.Assert(!handle.IsNil);
var metadataDecoder = new MetadataDecoder(moduleSymbol, containingType);
SignatureHeader callingConvention;
BadImageFormatException propEx;
var propertyParams = metadataDecoder.GetSignatureForProperty(handle, out callingConvention, out propEx);
Debug.Assert(propertyParams.Length > 0);
var returnInfo = propertyParams[0];
PEPropertySymbol result = returnInfo.CustomModifiers.IsDefaultOrEmpty && returnInfo.RefCustomModifiers.IsDefaultOrEmpty
? new PEPropertySymbol(moduleSymbol, containingType, handle, getMethod, setMethod, propertyParams, metadataDecoder)
: new PEPropertySymbolWithCustomModifiers(moduleSymbol, containingType, handle, getMethod, setMethod, propertyParams, metadataDecoder);
// A property should always have this modreq, and vice versa.
var isBad = (result.RefKind == RefKind.In) != result.RefCustomModifiers.HasInAttributeModifier();
if (propEx != null || isBad)
{
result.AccessUncommonFields()._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, result));
result._flags.SetUseSiteDiagnosticPopulated();
}
return result;
}
private PEPropertySymbol(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol containingType,
PropertyDefinitionHandle handle,
PEMethodSymbol getMethod,
PEMethodSymbol setMethod,
ParamInfo<TypeSymbol>[] propertyParams,
MetadataDecoder metadataDecoder)
{
_containingType = containingType;
var module = moduleSymbol.Module;
PropertyAttributes mdFlags = 0;
BadImageFormatException mrEx = null;
try
{
module.GetPropertyDefPropsOrThrow(handle, out _name, out mdFlags);
}
catch (BadImageFormatException e)
{
mrEx = e;
if ((object)_name == null)
{
_name = string.Empty;
}
}
_getMethod = getMethod;
_setMethod = setMethod;
_handle = handle;
SignatureHeader unusedCallingConvention;
BadImageFormatException getEx = null;
var getMethodParams = (object)getMethod == null ? null : metadataDecoder.GetSignatureForMethod(getMethod.Handle, out unusedCallingConvention, out getEx);
BadImageFormatException setEx = null;
var setMethodParams = (object)setMethod == null ? null : metadataDecoder.GetSignatureForMethod(setMethod.Handle, out unusedCallingConvention, out setEx);
// NOTE: property parameter names are not recorded in metadata, so we have to
// use the parameter names from one of the indexers
// NB: prefer setter names to getter names if both are present.
bool isBad;
_parameters = setMethodParams is null
? GetParameters(moduleSymbol, this, getMethod, propertyParams, getMethodParams, out isBad)
: GetParameters(moduleSymbol, this, setMethod, propertyParams, setMethodParams, out isBad);
if (getEx != null || setEx != null || mrEx != null || isBad)
{
AccessUncommonFields()._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this));
_flags.SetUseSiteDiagnosticPopulated();
}
var returnInfo = propertyParams[0];
var typeCustomModifiers = CSharpCustomModifier.Convert(returnInfo.CustomModifiers);
if (returnInfo.IsByRef)
{
if (moduleSymbol.Module.HasIsReadOnlyAttribute(handle))
{
_refKind = RefKind.RefReadOnly;
}
else
{
_refKind = RefKind.Ref;
}
}
else
{
_refKind = RefKind.None;
}
// CONSIDER: Can we make parameter type computation lazy?
TypeSymbol originalPropertyType = returnInfo.Type;
originalPropertyType = DynamicTypeDecoder.TransformType(originalPropertyType, typeCustomModifiers.Length, handle, moduleSymbol, _refKind);
originalPropertyType = NativeIntegerTypeDecoder.TransformType(originalPropertyType, handle, moduleSymbol, _containingType);
// Dynamify object type if necessary
originalPropertyType = originalPropertyType.AsDynamicIfNoPia(_containingType);
// We start without annotation (they will be decoded below)
var propertyTypeWithAnnotations = TypeWithAnnotations.Create(originalPropertyType, customModifiers: typeCustomModifiers);
// Decode nullable before tuple types to avoid converting between
// NamedTypeSymbol and TupleTypeSymbol unnecessarily.
// The containing type is passed to NullableTypeDecoder.TransformType to determine access
// because the property does not have explicit accessibility in metadata.
propertyTypeWithAnnotations = NullableTypeDecoder.TransformType(propertyTypeWithAnnotations, handle, moduleSymbol, accessSymbol: _containingType, nullableContext: _containingType);
propertyTypeWithAnnotations = TupleTypeDecoder.DecodeTupleTypesIfApplicable(propertyTypeWithAnnotations, handle, moduleSymbol);
_propertyTypeWithAnnotations = propertyTypeWithAnnotations;
// A property is bogus and must be accessed by calling its accessors directly if the
// accessor signatures do not agree, both with each other and with the property,
// or if it has parameters and is not an indexer or indexed property.
bool callMethodsDirectly = !DoSignaturesMatch(module, metadataDecoder, propertyParams, _getMethod, getMethodParams, _setMethod, setMethodParams) ||
MustCallMethodsDirectlyCore() ||
anyUnexpectedRequiredModifiers(propertyParams);
if (!callMethodsDirectly)
{
if ((object)_getMethod != null)
{
_getMethod.SetAssociatedProperty(this, MethodKind.PropertyGet);
}
if ((object)_setMethod != null)
{
_setMethod.SetAssociatedProperty(this, MethodKind.PropertySet);
}
}
_flags = new PackedFlags(
isSpecialName: (mdFlags & PropertyAttributes.SpecialName) != 0,
isRuntimeSpecialName: (mdFlags & PropertyAttributes.RTSpecialName) != 0,
callMethodsDirectly);
static bool anyUnexpectedRequiredModifiers(ParamInfo<TypeSymbol>[] propertyParams)
{
return propertyParams.Any(p => (!p.RefCustomModifiers.IsDefaultOrEmpty && p.RefCustomModifiers.Any(static m => !m.IsOptional && !m.Modifier.IsWellKnownTypeInAttribute())) ||
p.CustomModifiers.AnyRequired());
}
}
private UncommonFields AccessUncommonFields()
{
var retVal = _uncommonFields;
return retVal ?? InterlockedOperations.Initialize(ref _uncommonFields, createUncommonFields());
UncommonFields createUncommonFields()
{
var retVal = new UncommonFields();
if (!_flags.IsObsoleteAttributePopulated)
{
retVal._lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
}
if (!_flags.IsUseSiteDiagnosticPopulated)
{
retVal._lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;
}
if (_flags.IsCustomAttributesPopulated)
{
retVal._lazyCustomAttributes = ImmutableArray<CSharpAttributeData>.Empty;
}
return retVal;
}
}
private bool MustCallMethodsDirectlyCore()
{
if (this.RefKind != RefKind.None && _setMethod != null)
{
return true;
}
else if (this.ParameterCount == 0)
{
return false;
}
else if (this.IsIndexedProperty)
{
return this.IsStatic;
}
else if (this.IsIndexer)
{
return this.HasRefOrOutParameter();
}
else
{
return true;
}
}
public override Symbol ContainingSymbol
{
get
{
return _containingType;
}
}
public override NamedTypeSymbol ContainingType
{
get
{
return _containingType;
}
}
/// <remarks>
/// To facilitate lookup, all indexer symbols have the same name.
/// Check the MetadataName property to find the name we imported.
/// </remarks>
public override string Name
{
get { return this.IsIndexer ? WellKnownMemberNames.Indexer : _name; }
}
internal override bool HasSpecialName
{
get { return _flags.IsSpecialName; }
}
public override string MetadataName
{
get
{
return _name;
}
}
public override int MetadataToken
{
get { return MetadataTokens.GetToken(_handle); }
}
internal PropertyDefinitionHandle Handle
{
get
{
return _handle;
}
}
public override Accessibility DeclaredAccessibility
{
get
{
if (_declaredAccessibility == UnsetAccessibility)
{
Accessibility accessibility;
if (this.IsOverride)
{
// Determining the accessibility of an overriding property is tricky. It should be
// based on the accessibilities of the accessors, but the overriding property need
// not override both accessors. As a result, we may need to look at the accessors
// of an overridden method.
//
// One might assume that we could just go straight to the least-derived
// property (i.e. the original virtual property) and check its accessors, but
// that can yield incorrect results if the least-derived property is in a
// different assembly. For any overridden and (directly) overriding members, M and M',
// in different assemblies, A1 and A2, if M is protected internal, then M' must be
// protected internal if the internals of A1 are visible to A2 and protected otherwise.
//
// Therefore, if we cross an assembly boundary in the course of walking up the
// override chain, and if the overriding assembly cannot see the internals of the
// overridden assembly, then any protected internal accessors we find should be
// treated as protected, for the purposes of determining property accessibility.
//
// NOTE: This process has no effect on accessor accessibility - a protected internal
// accessor in another assembly will still have declared accessibility protected internal.
// The difference between the accessibilities of the overriding and overridden accessors
// will be accommodated later, when we check for CS0507 (ERR_CantChangeAccessOnOverride).
bool crossedAssemblyBoundaryWithoutInternalsVisibleTo = false;
Accessibility getAccessibility = Accessibility.NotApplicable;
Accessibility setAccessibility = Accessibility.NotApplicable;
PropertySymbol curr = this;
while (true)
{
if (getAccessibility == Accessibility.NotApplicable)
{
MethodSymbol getMethod = curr.GetMethod;
if ((object)getMethod != null)
{
Accessibility overriddenAccessibility = getMethod.DeclaredAccessibility;
getAccessibility = overriddenAccessibility == Accessibility.ProtectedOrInternal && crossedAssemblyBoundaryWithoutInternalsVisibleTo
? Accessibility.Protected
: overriddenAccessibility;
}
}
if (setAccessibility == Accessibility.NotApplicable)
{
MethodSymbol setMethod = curr.SetMethod;
if ((object)setMethod != null)
{
Accessibility overriddenAccessibility = setMethod.DeclaredAccessibility;
setAccessibility = overriddenAccessibility == Accessibility.ProtectedOrInternal && crossedAssemblyBoundaryWithoutInternalsVisibleTo
? Accessibility.Protected
: overriddenAccessibility;
}
}
if (getAccessibility != Accessibility.NotApplicable && setAccessibility != Accessibility.NotApplicable)
{
break;
}
PropertySymbol next = curr.OverriddenProperty;
if ((object)next == null)
{
break;
}
if (!crossedAssemblyBoundaryWithoutInternalsVisibleTo && !curr.ContainingAssembly.HasInternalAccessTo(next.ContainingAssembly))
{
crossedAssemblyBoundaryWithoutInternalsVisibleTo = true;
}
curr = next;
}
accessibility = PEPropertyOrEventHelpers.GetDeclaredAccessibilityFromAccessors(getAccessibility, setAccessibility);
}
else
{
accessibility = PEPropertyOrEventHelpers.GetDeclaredAccessibilityFromAccessors(this.GetMethod, this.SetMethod);
}
Interlocked.CompareExchange(ref _declaredAccessibility, (int)accessibility, UnsetAccessibility);
}
return (Accessibility)_declaredAccessibility;
}
}
public override bool IsExtern
{
get
{
// Some accessor extern.
return
((object)_getMethod != null && _getMethod.IsExtern) ||
((object)_setMethod != null && _setMethod.IsExtern);
}
}
public override bool IsAbstract
{
get
{
// Some accessor abstract.
return
((object)_getMethod != null && _getMethod.IsAbstract) ||
((object)_setMethod != null && _setMethod.IsAbstract);
}
}
public override bool IsSealed
{
get
{
// All accessors sealed.
return
((object)_getMethod == null || _getMethod.IsSealed) &&
((object)_setMethod == null || _setMethod.IsSealed);
}
}
public override bool IsVirtual
{
get
{
// Some accessor virtual (as long as another isn't override or abstract).
return !IsOverride && !IsAbstract &&
(((object)_getMethod != null && _getMethod.IsVirtual) ||
((object)_setMethod != null && _setMethod.IsVirtual));
}
}
public override bool IsOverride
{
get
{
// Some accessor override.
return
((object)_getMethod != null && _getMethod.IsOverride) ||
((object)_setMethod != null && _setMethod.IsOverride);
}
}
public override bool IsStatic
{
get
{
// All accessors static.
return
((object)_getMethod == null || _getMethod.IsStatic) &&
((object)_setMethod == null || _setMethod.IsStatic);
}
}
internal override bool IsRequired
{
get
{
if (!_flags.TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute))
{
var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule;
hasRequiredMemberAttribute = containingPEModuleSymbol.Module.HasAttribute(_handle, AttributeDescription.RequiredMemberAttribute);
_flags.SetHasRequiredMemberAttribute(hasRequiredMemberAttribute);
}
return hasRequiredMemberAttribute;
}
}
internal sealed override bool HasUnscopedRefAttribute
{
get
{
if (!_flags.TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute))
{
var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule;
hasUnscopedRefAttribute = containingPEModuleSymbol.Module.HasUnscopedRefAttribute(_handle);
_flags.SetHasUnscopedRefAttribute(hasUnscopedRefAttribute);
}
return hasUnscopedRefAttribute;
}
}
public override ImmutableArray<ParameterSymbol> Parameters
{
get { return _parameters; }
}
/// <remarks>
/// This property can return true for bogus indexers.
/// Rationale: If a type in metadata has a single, bogus indexer
/// and a source method tries to invoke it, then Dev10 reports a bogus
/// indexer rather than lack of an indexer.
/// </remarks>
public override bool IsIndexer
{
get
{
// NOTE: Dev10 appears to include static indexers in overload resolution
// for an array access expression, so it stands to reason that it considers
// them indexers.
if (this.ParameterCount > 0)
{
string defaultMemberName = _containingType.DefaultMemberName;
return _name == defaultMemberName || //NB: not Name property (break mutual recursion)
((object)this.GetMethod != null && this.GetMethod.Name == defaultMemberName) ||
((object)this.SetMethod != null && this.SetMethod.Name == defaultMemberName);
}
return false;
}
}
public override bool IsIndexedProperty
{
get
{
// Indexed property support is limited to types marked [ComImport],
// to match the native compiler where the feature was scoped to
// avoid supporting property groups.
return (this.ParameterCount > 0) && _containingType.IsComImport;
}
}
public override RefKind RefKind
{
get { return _refKind; }
}
public override TypeWithAnnotations TypeWithAnnotations
{
get { return _propertyTypeWithAnnotations; }
}
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get { return ImmutableArray<CustomModifier>.Empty; }
}
public override MethodSymbol GetMethod
{
get { return _getMethod; }
}
public override MethodSymbol SetMethod
{
get { return _setMethod; }
}
internal override Microsoft.Cci.CallingConvention CallingConvention
{
get
{
var metadataDecoder = new MetadataDecoder(_containingType.ContainingPEModule, _containingType);
return (Microsoft.Cci.CallingConvention)(metadataDecoder.GetSignatureHeaderForProperty(_handle).RawValue);
}
}
public override ImmutableArray<Location> Locations
{
get
{
return _containingType.ContainingPEModule.MetadataLocation.Cast<MetadataLocation, Location>();
}
}
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
return ImmutableArray<SyntaxReference>.Empty;
}
}
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
if (!_flags.IsCustomAttributesPopulated)
{
var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule;
ImmutableArray<CSharpAttributeData> attributes = containingPEModuleSymbol.GetCustomAttributesForToken(
_handle,
out _,
this.RefKind == RefKind.RefReadOnly ? AttributeDescription.IsReadOnlyAttribute : default,
out CustomAttributeHandle required,
AttributeDescription.RequiredMemberAttribute);
if (!attributes.IsEmpty)
{
ImmutableInterlocked.InterlockedInitialize(ref AccessUncommonFields()._lazyCustomAttributes, attributes);
}
_flags.SetCustomAttributesPopulated();
_flags.SetHasRequiredMemberAttribute(!required.IsNil);
}
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return ImmutableArray<CSharpAttributeData>.Empty;
}
else
{
var result = uncommonFields._lazyCustomAttributes;
if (result.IsDefault)
{
result = ImmutableArray<CSharpAttributeData>.Empty;
ImmutableInterlocked.InterlockedInitialize(ref uncommonFields._lazyCustomAttributes, result);
}
return result;
}
}
internal override IEnumerable<CSharpAttributeData> GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder)
{
return GetAttributes();
}
/// <summary>
/// Intended behavior: this property, P, explicitly implements an interface property, IP,
/// if any of the following is true:
///
/// 1) P.get explicitly implements IP.get and P.set explicitly implements IP.set
/// 2) P.get explicitly implements IP.get and there is no IP.set
/// 3) P.set explicitly implements IP.set and there is no IP.get
///
/// Extra or missing accessors will not result in errors, P will simply not report that
/// it explicitly implements IP.
/// </summary>
public override ImmutableArray<PropertySymbol> ExplicitInterfaceImplementations
{
get
{
if (((object)_getMethod == null || _getMethod.ExplicitInterfaceImplementations.Length == 0) &&
((object)_setMethod == null || _setMethod.ExplicitInterfaceImplementations.Length == 0))
{
return ImmutableArray<PropertySymbol>.Empty;
}
var propertiesWithImplementedGetters = PEPropertyOrEventHelpers.GetPropertiesForExplicitlyImplementedAccessor(_getMethod);
var propertiesWithImplementedSetters = PEPropertyOrEventHelpers.GetPropertiesForExplicitlyImplementedAccessor(_setMethod);
var builder = ArrayBuilder<PropertySymbol>.GetInstance();
foreach (var prop in propertiesWithImplementedGetters)
{
if (!prop.SetMethod.IsImplementable() || propertiesWithImplementedSetters.Contains(prop))
{
builder.Add(prop);
}
}
foreach (var prop in propertiesWithImplementedSetters)
{
// No need to worry about duplicates. If prop was added by the previous loop,
// then it must have a GetMethod.
if (!prop.GetMethod.IsImplementable())
{
builder.Add(prop);
}
}
return builder.ToImmutableAndFree();
}
}
internal override bool MustCallMethodsDirectly
{
get { return _flags.CallMethodsDirectly; }
}
private static bool DoSignaturesMatch(
PEModule module,
MetadataDecoder metadataDecoder,
ParamInfo<TypeSymbol>[] propertyParams,
PEMethodSymbol getMethod,
ParamInfo<TypeSymbol>[] getMethodParams,
PEMethodSymbol setMethod,
ParamInfo<TypeSymbol>[] setMethodParams)
{
Debug.Assert((getMethodParams == null) == ((object)getMethod == null));
Debug.Assert((setMethodParams == null) == ((object)setMethod == null));
bool hasGetMethod = getMethodParams != null;
bool hasSetMethod = setMethodParams != null;
if (hasGetMethod && !metadataDecoder.DoPropertySignaturesMatch(propertyParams, getMethodParams, comparingToSetter: false, compareParamByRef: true, compareReturnType: true))
{
return false;
}
if (hasSetMethod && !metadataDecoder.DoPropertySignaturesMatch(propertyParams, setMethodParams, comparingToSetter: true, compareParamByRef: true, compareReturnType: true))
{
return false;
}
if (hasGetMethod && hasSetMethod)
{
var lastPropertyParamIndex = propertyParams.Length - 1;
var getHandle = getMethodParams[lastPropertyParamIndex].Handle;
var setHandle = setMethodParams[lastPropertyParamIndex].Handle;
var getterHasParamArray = !getHandle.IsNil && module.HasParamArrayAttribute(getHandle);
var setterHasParamArray = !setHandle.IsNil && module.HasParamArrayAttribute(setHandle);
var getterHasParamCollection = !getHandle.IsNil && module.HasParamCollectionAttribute(getHandle);
var setterHasParamCollection = !setHandle.IsNil && module.HasParamCollectionAttribute(setHandle);
if (getterHasParamArray != setterHasParamArray || getterHasParamCollection != setterHasParamCollection)
{
return false;
}
if ((getMethod.IsExtern != setMethod.IsExtern) ||
// (getMethod.IsAbstract != setMethod.IsAbstract) || // NOTE: Dev10 accepts one abstract accessor
(getMethod.IsSealed != setMethod.IsSealed) ||
(getMethod.IsOverride != setMethod.IsOverride) ||
(getMethod.IsStatic != setMethod.IsStatic))
{
return false;
}
}
return true;
}
private static ImmutableArray<ParameterSymbol> GetParameters(
PEModuleSymbol moduleSymbol,
PEPropertySymbol property,
PEMethodSymbol accessor,
ParamInfo<TypeSymbol>[] propertyParams,
ParamInfo<TypeSymbol>[] accessorParams,
out bool anyParameterIsBad)
{
anyParameterIsBad = false;
// First parameter is the property type.
if (propertyParams.Length < 2)
{
return ImmutableArray<ParameterSymbol>.Empty;
}
var numAccessorParams = accessorParams.Length;
var parameters = new ParameterSymbol[propertyParams.Length - 1];
for (int i = 1; i < propertyParams.Length; i++) // from 1 to skip property/return type
{
// NOTE: this is a best guess at the Dev10 behavior. The actual behavior is
// in the unmanaged helper code that Dev10 uses to load the metadata.
var propertyParam = propertyParams[i];
ParameterHandle paramHandle;
Symbol nullableContext;
if (i < numAccessorParams)
{
paramHandle = accessorParams[i].Handle;
nullableContext = accessor;
}
else
{
paramHandle = propertyParam.Handle;
nullableContext = property;
}
var ordinal = i - 1;
bool isBad;
parameters[ordinal] = PEParameterSymbol.Create(moduleSymbol, property, accessor.IsMetadataVirtual(), ordinal, paramHandle, propertyParam, nullableContext, out isBad);
if (isBad)
{
anyParameterIsBad = true;
}
}
return parameters.AsImmutableOrNull();
}
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()
{
AssemblySymbol primaryDependency = PrimaryDependency;
if (!_flags.IsUseSiteDiagnosticPopulated)
{
var result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
var diag = deriveCompilerFeatureRequiredUseSiteInfo();
MergeUseSiteDiagnostics(ref diag, result.DiagnosticInfo);
result = result.AdjustDiagnosticInfo(diag);
if (result.DiagnosticInfo is not null || !result.SecondaryDependencies.IsNullOrEmpty())
{
AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromSentinel(PrimaryDependency, result);
}
_flags.SetUseSiteDiagnosticPopulated();
}
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return new UseSiteInfo<AssemblySymbol>(primaryDependency);
}
else
{
var result = uncommonFields._lazyCachedUseSiteInfo;
if (!result.IsInitialized)
{
uncommonFields._lazyCachedUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, new UseSiteInfo<AssemblySymbol>(primaryDependency));
result = uncommonFields._lazyCachedUseSiteInfo;
}
return result.ToUseSiteInfo(primaryDependency);
}
DiagnosticInfo deriveCompilerFeatureRequiredUseSiteInfo()
{
var containingType = (PENamedTypeSymbol)ContainingType;
PEModuleSymbol containingPEModule = _containingType.ContainingPEModule;
var decoder = new MetadataDecoder(containingPEModule, containingType);
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(
this,
containingPEModule,
Handle,
allowedFeatures: CompilerFeatureRequiredFeatures.None,
decoder);
if (diag != null)
{
return diag;
}
foreach (var param in Parameters)
{
diag = ((PEParameterSymbol)param).DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
}
return containingType.GetCompilerFeatureRequiredDiagnostic();
}
}
internal override ObsoleteAttributeData ObsoleteAttributeData
{
get
{
if (!_flags.IsObsoleteAttributePopulated)
{
var result = ObsoleteAttributeHelpers.GetObsoleteDataFromMetadata(_handle, (PEModuleSymbol)(this.ContainingModule), ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: false);
if (result != null)
{
result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyObsoleteAttributeData, result, ObsoleteAttributeData.Uninitialized);
}
_flags.SetObsoleteAttributePopulated();
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;
}
}
}
internal override bool HasRuntimeSpecialName
{
get
{
return _flags.IsRuntimeSpecialName;
}
}
internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
{
get { return null; }
}
internal override int TryGetOverloadResolutionPriority()
{
Debug.Assert(IsIndexer || IsIndexedProperty);
if (!_flags.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
_flags.SetOverloadResolutionPriorityPopulated();
}
return _uncommonFields?._lazyOverloadResolutionPriority ?? 0;
}
private sealed class PEPropertySymbolWithCustomModifiers : PEPropertySymbol
{
private readonly ImmutableArray<CustomModifier> _refCustomModifiers;
public PEPropertySymbolWithCustomModifiers(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol containingType,
PropertyDefinitionHandle handle,
PEMethodSymbol getMethod,
PEMethodSymbol setMethod,
ParamInfo<TypeSymbol>[] propertyParams,
MetadataDecoder metadataDecoder)
: base(moduleSymbol, containingType, handle, getMethod, setMethod,
propertyParams,
metadataDecoder)
{
var returnInfo = propertyParams[0];
_refCustomModifiers = CSharpCustomModifier.Convert(returnInfo.RefCustomModifiers);
}
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get { return _refCustomModifiers; }
}
}
}
}
|