|
// 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.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// The class to represent all fields imported from a PE/module.
/// </summary>
internal sealed class PEFieldSymbol : FieldSymbol
{
private struct PackedFlags
{
// Layout:
// |........................|qq|rr|v|fffff|
//
// f = FlowAnalysisAnnotations. 5 bits (4 value bits + 1 completion bit).
// v = IsVolatile 1 bit
// r = RefKind 2 bits
// q = Required members. 2 bits (1 value bit + 1 completion bit).
private const int HasDisallowNullAttribute = 0x1 << 0;
private const int HasAllowNullAttribute = 0x1 << 1;
private const int HasMaybeNullAttribute = 0x1 << 2;
private const int HasNotNullAttribute = 0x1 << 3;
private const int FlowAnalysisAnnotationsCompletionBit = 0x1 << 4;
private const int IsVolatileBit = 0x1 << 5;
private const int RefKindOffset = 6;
private const int RefKindMask = 0x3;
private const int HasRequiredMemberAttribute = 0x1 << 8;
private const int RequiredMemberCompletionBit = 0x1 << 9;
private int _bits;
public bool SetFlowAnalysisAnnotations(FlowAnalysisAnnotations value)
{
Debug.Assert((value & ~(FlowAnalysisAnnotations.DisallowNull | FlowAnalysisAnnotations.AllowNull | FlowAnalysisAnnotations.MaybeNull | FlowAnalysisAnnotations.NotNull)) == 0);
int bitsToSet = FlowAnalysisAnnotationsCompletionBit;
if ((value & FlowAnalysisAnnotations.DisallowNull) != 0) bitsToSet |= PackedFlags.HasDisallowNullAttribute;
if ((value & FlowAnalysisAnnotations.AllowNull) != 0) bitsToSet |= PackedFlags.HasAllowNullAttribute;
if ((value & FlowAnalysisAnnotations.MaybeNull) != 0) bitsToSet |= PackedFlags.HasMaybeNullAttribute;
if ((value & FlowAnalysisAnnotations.NotNull) != 0) bitsToSet |= PackedFlags.HasNotNullAttribute;
return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value)
{
int theBits = _bits; // Read this.bits once to ensure the consistency of the value and completion flags.
value = FlowAnalysisAnnotations.None;
if ((theBits & PackedFlags.HasDisallowNullAttribute) != 0) value |= FlowAnalysisAnnotations.DisallowNull;
if ((theBits & PackedFlags.HasAllowNullAttribute) != 0) value |= FlowAnalysisAnnotations.AllowNull;
if ((theBits & PackedFlags.HasMaybeNullAttribute) != 0) value |= FlowAnalysisAnnotations.MaybeNull;
if ((theBits & PackedFlags.HasNotNullAttribute) != 0) value |= FlowAnalysisAnnotations.NotNull;
var result = (theBits & FlowAnalysisAnnotationsCompletionBit) != 0;
Debug.Assert(value == 0 || result);
return result;
}
public void SetIsVolatile(bool isVolatile)
{
if (isVolatile) ThreadSafeFlagOperations.Set(ref _bits, IsVolatileBit);
Debug.Assert(IsVolatile == isVolatile);
}
public bool IsVolatile => (_bits & IsVolatileBit) != 0;
public void SetRefKind(RefKind refKind)
{
int bits = ((int)refKind & RefKindMask) << RefKindOffset;
if (bits != 0) ThreadSafeFlagOperations.Set(ref _bits, bits);
Debug.Assert(RefKind == refKind);
}
public RefKind RefKind => (RefKind)((_bits >> RefKindOffset) & RefKindMask);
public bool SetHasRequiredMemberAttribute(bool isRequired)
{
int bitsToSet = RequiredMemberCompletionBit | (isRequired ? HasRequiredMemberAttribute : 0);
return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute)
{
if ((_bits & RequiredMemberCompletionBit) != 0)
{
hasRequiredMemberAttribute = (_bits & HasRequiredMemberAttribute) != 0;
return true;
}
hasRequiredMemberAttribute = false;
return false;
}
}
private readonly FieldDefinitionHandle _handle;
private readonly string _name;
private readonly FieldAttributes _flags;
private readonly PENamedTypeSymbol _containingType;
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
private ConstantValue _lazyConstantValue = Microsoft.CodeAnalysis.ConstantValue.Unset; // Indicates an uninitialized ConstantValue
private Tuple<CultureInfo, string> _lazyDocComment;
private CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;
private ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
private TypeWithAnnotations.Boxed _lazyType;
private int _lazyFixedSize;
private NamedTypeSymbol _lazyFixedImplementationType;
private PEEventSymbol _associatedEventOpt;
private PackedFlags _packedFlags;
private ImmutableArray<CustomModifier> _lazyRefCustomModifiers;
internal PEFieldSymbol(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol containingType,
FieldDefinitionHandle fieldDef)
{
Debug.Assert((object)moduleSymbol != null);
Debug.Assert((object)containingType != null);
Debug.Assert(!fieldDef.IsNil);
_handle = fieldDef;
_containingType = containingType;
_packedFlags = new PackedFlags();
try
{
moduleSymbol.Module.GetFieldDefPropsOrThrow(fieldDef, out _name, out _flags);
}
catch (BadImageFormatException)
{
if ((object)_name == null)
{
_name = String.Empty;
}
_lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this));
}
}
public override Symbol ContainingSymbol
{
get
{
return _containingType;
}
}
public override NamedTypeSymbol ContainingType
{
get
{
return _containingType;
}
}
public override string Name
{
get
{
return _name;
}
}
public override int MetadataToken
{
get { return MetadataTokens.GetToken(_handle); }
}
internal FieldAttributes Flags
{
get
{
return _flags;
}
}
internal override bool HasSpecialName
{
get
{
return (_flags & FieldAttributes.SpecialName) != 0;
}
}
internal override bool HasRuntimeSpecialName
{
get
{
return (_flags & FieldAttributes.RTSpecialName) != 0;
}
}
internal override bool IsNotSerialized
{
get
{
#pragma warning disable SYSLIB0050 // 'TypeAttributes.Serializable' is obsolete
return (_flags & FieldAttributes.NotSerialized) != 0;
#pragma warning restore SYSLIB0050
}
}
internal override MarshalPseudoCustomAttributeData MarshallingInformation
{
get
{
// the compiler doesn't need full marshalling information, just the unmanaged type or descriptor
return null;
}
}
internal override bool IsMarshalledExplicitly
{
get
{
return ((_flags & FieldAttributes.HasFieldMarshal) != 0);
}
}
internal override UnmanagedType MarshallingType
{
get
{
if ((_flags & FieldAttributes.HasFieldMarshal) == 0)
{
return 0;
}
return _containingType.ContainingPEModule.Module.GetMarshallingType(_handle);
}
}
internal override ImmutableArray<byte> MarshallingDescriptor
{
get
{
if ((_flags & FieldAttributes.HasFieldMarshal) == 0)
{
return default(ImmutableArray<byte>);
}
return _containingType.ContainingPEModule.Module.GetMarshallingDescriptor(_handle);
}
}
internal override int? TypeLayoutOffset
{
get
{
return _containingType.ContainingPEModule.Module.GetFieldOffset(_handle);
}
}
internal FieldDefinitionHandle Handle
{
get
{
return _handle;
}
}
/// <summary>
/// Mark this field as the backing field of a field-like event.
/// The caller will also ensure that it is excluded from the member list of
/// the containing type (as it would be in source).
/// </summary>
internal void SetAssociatedEvent(PEEventSymbol eventSymbol)
{
Debug.Assert((object)eventSymbol != null);
Debug.Assert(TypeSymbol.Equals(eventSymbol.ContainingType, _containingType, TypeCompareKind.ConsiderEverything2));
// This should always be true in valid metadata - there should only
// be one event with a given name in a given type.
if ((object)_associatedEventOpt == null)
{
// No locking required since this method will only be called by the thread that created
// the field symbol (and will be called before the field symbol is added to the containing
// type members and available to other threads).
_associatedEventOpt = eventSymbol;
}
}
private void EnsureSignatureIsLoaded()
{
if (_lazyType == null)
{
var moduleSymbol = _containingType.ContainingPEModule;
FieldInfo<TypeSymbol> fieldInfo = new MetadataDecoder(moduleSymbol, _containingType).DecodeFieldSignature(_handle);
TypeSymbol typeSymbol = fieldInfo.Type;
ImmutableArray<CustomModifier> customModifiersArray = CSharpCustomModifier.Convert(fieldInfo.CustomModifiers);
typeSymbol = DynamicTypeDecoder.TransformType(typeSymbol, customModifiersArray.Length, _handle, moduleSymbol);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, _handle, moduleSymbol, _containingType);
// We start without annotations
var type = TypeWithAnnotations.Create(typeSymbol, customModifiers: customModifiersArray);
// Decode nullable before tuple types to avoid converting between
// NamedTypeSymbol and TupleTypeSymbol unnecessarily.
type = NullableTypeDecoder.TransformType(type, _handle, moduleSymbol, accessSymbol: this, nullableContext: _containingType);
type = TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, _handle, moduleSymbol);
RefKind refKind = fieldInfo.IsByRef ?
moduleSymbol.Module.HasIsReadOnlyAttribute(_handle) ? RefKind.RefReadOnly : RefKind.Ref :
RefKind.None;
_packedFlags.SetRefKind(refKind);
_packedFlags.SetIsVolatile(customModifiersArray.Any(static m => !m.IsOptional && ((CSharpCustomModifier)m).ModifierSymbol.SpecialType == SpecialType.System_Runtime_CompilerServices_IsVolatile));
TypeSymbol fixedElementType;
int fixedSize;
if (customModifiersArray.IsEmpty && IsFixedBuffer(out fixedSize, out fixedElementType))
{
_lazyFixedSize = fixedSize;
_lazyFixedImplementationType = type.Type as NamedTypeSymbol;
type = TypeWithAnnotations.Create(new PointerTypeSymbol(TypeWithAnnotations.Create(fixedElementType)));
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyRefCustomModifiers, CSharpCustomModifier.Convert(fieldInfo.RefCustomModifiers));
Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null);
}
}
private bool IsFixedBuffer(out int fixedSize, out TypeSymbol fixedElementType)
{
fixedSize = 0;
fixedElementType = null;
string elementTypeName;
int bufferSize;
PEModuleSymbol containingPEModule = this.ContainingPEModule;
if (containingPEModule.Module.HasFixedBufferAttribute(_handle, out elementTypeName, out bufferSize))
{
var decoder = new MetadataDecoder(containingPEModule);
var elementType = decoder.GetTypeSymbolForSerializedType(elementTypeName);
if (elementType.FixedBufferElementSizeInBytes() != 0)
{
fixedSize = bufferSize;
fixedElementType = elementType;
return true;
}
}
return false;
}
private PEModuleSymbol ContainingPEModule
{
get
{
return ((PENamespaceSymbol)ContainingNamespace).ContainingPEModule;
}
}
public override RefKind RefKind
{
get
{
EnsureSignatureIsLoaded();
return _packedFlags.RefKind;
}
}
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get
{
EnsureSignatureIsLoaded();
return _lazyRefCustomModifiers;
}
}
internal override TypeWithAnnotations GetFieldType(ConsList<FieldSymbol> fieldsBeingBound)
{
EnsureSignatureIsLoaded();
return _lazyType.Value;
}
public override FlowAnalysisAnnotations FlowAnalysisAnnotations
{
get
{
FlowAnalysisAnnotations value;
if (!_packedFlags.TryGetFlowAnalysisAnnotations(out value))
{
value = DecodeFlowAnalysisAttributes(_containingType.ContainingPEModule.Module, _handle);
_packedFlags.SetFlowAnalysisAnnotations(value);
}
return value;
}
}
private static FlowAnalysisAnnotations DecodeFlowAnalysisAttributes(PEModule module, FieldDefinitionHandle handle)
{
FlowAnalysisAnnotations annotations = FlowAnalysisAnnotations.None;
if (module.HasAttribute(handle, AttributeDescription.AllowNullAttribute)) annotations |= FlowAnalysisAnnotations.AllowNull;
if (module.HasAttribute(handle, AttributeDescription.DisallowNullAttribute)) annotations |= FlowAnalysisAnnotations.DisallowNull;
if (module.HasAttribute(handle, AttributeDescription.MaybeNullAttribute)) annotations |= FlowAnalysisAnnotations.MaybeNull;
if (module.HasAttribute(handle, AttributeDescription.NotNullAttribute)) annotations |= FlowAnalysisAnnotations.NotNull;
return annotations;
}
public override bool IsFixedSizeBuffer
{
get
{
EnsureSignatureIsLoaded();
return (object)_lazyFixedImplementationType != null;
}
}
public override int FixedSize
{
get
{
EnsureSignatureIsLoaded();
return _lazyFixedSize;
}
}
internal override NamedTypeSymbol FixedImplementationType(PEModuleBuilder emitModule)
{
EnsureSignatureIsLoaded();
return _lazyFixedImplementationType;
}
public override Symbol AssociatedSymbol
{
get
{
return _associatedEventOpt;
}
}
public override bool IsReadOnly
{
get
{
return (_flags & FieldAttributes.InitOnly) != 0;
}
}
public override bool IsVolatile
{
get
{
EnsureSignatureIsLoaded();
return _packedFlags.IsVolatile;
}
}
public override bool IsConst
{
get
{
return (_flags & FieldAttributes.Literal) != 0 || GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false) != null;
}
}
internal override ConstantValue GetConstantValue(ConstantFieldsInProgress inProgress, bool earlyDecodingWellKnownAttributes)
{
if (_lazyConstantValue == Microsoft.CodeAnalysis.ConstantValue.Unset)
{
ConstantValue value = null;
if ((_flags & FieldAttributes.Literal) != 0)
{
value = _containingType.ContainingPEModule.Module.GetConstantFieldValue(_handle);
}
// If this is a Decimal, the constant value may come from DecimalConstantAttribute
if (this.Type.SpecialType == SpecialType.System_Decimal)
{
ConstantValue defaultValue;
if (_containingType.ContainingPEModule.Module.HasDecimalConstantAttribute(Handle, out defaultValue))
{
value = defaultValue;
}
}
Interlocked.CompareExchange(
ref _lazyConstantValue,
value,
Microsoft.CodeAnalysis.ConstantValue.Unset);
}
return _lazyConstantValue;
}
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 Accessibility DeclaredAccessibility
{
get
{
var access = Accessibility.Private;
switch (_flags & FieldAttributes.FieldAccessMask)
{
case FieldAttributes.Assembly:
access = Accessibility.Internal;
break;
case FieldAttributes.FamORAssem:
access = Accessibility.ProtectedOrInternal;
break;
case FieldAttributes.FamANDAssem:
access = Accessibility.ProtectedAndInternal;
break;
case FieldAttributes.Private:
case FieldAttributes.PrivateScope:
access = Accessibility.Private;
break;
case FieldAttributes.Public:
access = Accessibility.Public;
break;
case FieldAttributes.Family:
access = Accessibility.Protected;
break;
default:
access = Accessibility.Private;
break;
}
return access;
}
}
public override bool IsStatic
{
get
{
return (_flags & FieldAttributes.Static) != 0;
}
}
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
if (_lazyCustomAttributes.IsDefault)
{
var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule;
var attributes = containingPEModuleSymbol.GetCustomAttributesForToken(
_handle,
out _,
FilterOutDecimalConstantAttribute() ? AttributeDescription.DecimalConstantAttribute : default,
out CustomAttributeHandle required,
AttributeDescription.RequiredMemberAttribute);
ImmutableInterlocked.InterlockedInitialize(ref _lazyCustomAttributes, attributes);
_packedFlags.SetHasRequiredMemberAttribute(!required.IsNil);
}
return _lazyCustomAttributes;
}
private bool FilterOutDecimalConstantAttribute()
{
ConstantValue value;
return this.Type.SpecialType == SpecialType.System_Decimal &&
(object)(value = GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false)) != null &&
value.Discriminator == ConstantValueTypeDiscriminator.Decimal;
}
internal override IEnumerable<CSharpAttributeData> GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder)
{
foreach (CSharpAttributeData attribute in GetAttributes())
{
yield return attribute;
}
// Yield hidden attributes last, order might be important.
if (FilterOutDecimalConstantAttribute())
{
var containingPEModuleSymbol = _containingType.ContainingPEModule;
yield return new PEAttributeData(containingPEModuleSymbol,
containingPEModuleSymbol.Module.FindLastTargetAttribute(_handle, AttributeDescription.DecimalConstantAttribute).Handle);
}
}
public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return PEDocumentationCommentUtils.GetDocumentationComment(this, _containingType.ContainingPEModule, preferredCulture, cancellationToken, ref _lazyDocComment);
}
internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
AssemblySymbol primaryDependency = PrimaryDependency;
if (!_lazyCachedUseSiteInfo.IsInitialized)
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
if (RefKind != RefKind.None &&
(IsFixedSizeBuffer || Type.IsRefLikeOrAllowsRefLikeType()))
{
MergeUseSiteInfo(ref result, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
}
deriveCompilerFeatureRequiredUseSiteInfo(ref result);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}
return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);
void deriveCompilerFeatureRequiredUseSiteInfo(ref UseSiteInfo<AssemblySymbol> result)
{
var containingType = (PENamedTypeSymbol)ContainingType;
PEModuleSymbol containingPEModule = _containingType.ContainingPEModule;
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(
this,
containingPEModule,
Handle,
allowedFeatures: CompilerFeatureRequiredFeatures.None,
new MetadataDecoder(containingPEModule, containingType));
diag ??= containingType.GetCompilerFeatureRequiredDiagnostic();
if (diag != null)
{
result = new UseSiteInfo<AssemblySymbol>(diag);
}
}
}
internal override ObsoleteAttributeData ObsoleteAttributeData
{
get
{
ObsoleteAttributeHelpers.InitializeObsoleteDataFromMetadata(ref _lazyObsoleteAttributeData, _handle, (PEModuleSymbol)(this.ContainingModule), ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: false);
return _lazyObsoleteAttributeData;
}
}
internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
{
get { return null; }
}
internal override bool IsRequired
{
get
{
if (!_packedFlags.TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute))
{
hasRequiredMemberAttribute = ContainingPEModule.Module.HasAttribute(_handle, AttributeDescription.RequiredMemberAttribute);
_packedFlags.SetHasRequiredMemberAttribute(hasRequiredMemberAttribute);
}
return hasRequiredMemberAttribute;
}
}
}
}
|