|
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// Represents a net-module imported from a PE. Can be a primary module of an assembly.
/// </summary>
internal sealed class PEModuleSymbol : NonMissingModuleSymbol
{
/// <summary>
/// Owning AssemblySymbol. This can be a PEAssemblySymbol or a SourceAssemblySymbol.
/// </summary>
private readonly AssemblySymbol _assemblySymbol;
private readonly int _ordinal;
/// <summary>
/// A Module object providing metadata.
/// </summary>
private readonly PEModule _module;
/// <summary>
/// Global namespace.
/// </summary>
private readonly PENamespaceSymbol _globalNamespace;
#nullable enable
/// <summary>
/// Cache the symbol for well-known type System.Type because we use it frequently
/// (for attributes).
/// </summary>
private NamedTypeSymbol? _lazySystemTypeSymbol;
private NamedTypeSymbol? _lazyEventRegistrationTokenSymbol;
private NamedTypeSymbol? _lazyEventRegistrationTokenTableSymbol;
#nullable disable
/// <summary>
/// The same value as ConcurrentDictionary.DEFAULT_CAPACITY
/// </summary>
private const int DefaultTypeMapCapacity = 31;
/// <summary>
/// This is a map from TypeDef handle to the target <see cref="TypeSymbol"/>.
/// It is used by <see cref="MetadataDecoder"/> to speed up type reference resolution
/// for metadata coming from this module. The map is lazily populated
/// as we load types from the module.
/// </summary>
internal readonly ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> TypeHandleToTypeMap =
new ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol>(concurrencyLevel: 2, capacity: DefaultTypeMapCapacity);
/// <summary>
/// This is a map from TypeRef row id to the target <see cref="TypeSymbol"/>.
/// It is used by <see cref="MetadataDecoder"/> to speed up type reference resolution
/// for metadata coming from this module. The map is lazily populated
/// by <see cref="MetadataDecoder"/> as we resolve TypeRefs from the module.
/// </summary>
internal readonly ConcurrentDictionary<TypeReferenceHandle, TypeSymbol> TypeRefHandleToTypeMap =
new ConcurrentDictionary<TypeReferenceHandle, TypeSymbol>(concurrencyLevel: 2, capacity: DefaultTypeMapCapacity);
internal readonly ImmutableArray<MetadataLocation> MetadataLocation;
internal readonly MetadataImportOptions ImportOptions;
/// <summary>
/// Module's custom attributes
/// </summary>
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
/// <summary>
/// Module's assembly attributes
/// </summary>
private ImmutableArray<CSharpAttributeData> _lazyAssemblyAttributes;
// Type names from module
private ICollection<string> _lazyTypeNames;
// Namespace names from module
private ICollection<string> _lazyNamespaceNames;
private enum NullableMemberMetadata
{
Unknown = 0,
Public,
Internal,
All,
}
private NullableMemberMetadata _lazyNullableMemberMetadata;
internal enum RefSafetyRulesAttributeVersion
{
Uninitialized = 0,
NoAttribute,
Version11,
UnrecognizedAttribute,
}
private RefSafetyRulesAttributeVersion _lazyRefSafetyRulesAttributeVersion;
#nullable enable
private DiagnosticInfo? _lazyCachedCompilerFeatureRequiredDiagnosticInfo = CSDiagnosticInfo.EmptyErrorInfo;
private ObsoleteAttributeData? _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
#nullable disable
internal PEModuleSymbol(PEAssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal)
: this((AssemblySymbol)assemblySymbol, module, importOptions, ordinal)
{
Debug.Assert(ordinal >= 0);
}
internal PEModuleSymbol(SourceAssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal)
: this((AssemblySymbol)assemblySymbol, module, importOptions, ordinal)
{
Debug.Assert(ordinal > 0);
}
internal PEModuleSymbol(RetargetingAssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal)
: this((AssemblySymbol)assemblySymbol, module, importOptions, ordinal)
{
Debug.Assert(ordinal > 0);
}
private PEModuleSymbol(AssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal)
{
Debug.Assert((object)assemblySymbol != null);
Debug.Assert(module != null);
_assemblySymbol = assemblySymbol;
_ordinal = ordinal;
_module = module;
this.ImportOptions = importOptions;
_globalNamespace = new PEGlobalNamespaceSymbol(this);
this.MetadataLocation = ImmutableArray.Create<MetadataLocation>(new MetadataLocation(this));
}
public sealed override bool AreLocalsZeroed
{
get
{
throw ExceptionUtilities.Unreachable();
}
}
internal override int Ordinal
{
get
{
return _ordinal;
}
}
internal override Machine Machine
{
get
{
return _module.Machine;
}
}
internal override bool Bit32Required
{
get
{
return _module.Bit32Required;
}
}
internal PEModule Module
{
get
{
return _module;
}
}
public override NamespaceSymbol GlobalNamespace
{
get { return _globalNamespace; }
}
public override string Name
{
get
{
return _module.Name;
}
}
private static EntityHandle Token
{
get
{
return EntityHandle.ModuleDefinition;
}
}
public override Symbol ContainingSymbol
{
get
{
return _assemblySymbol;
}
}
public override AssemblySymbol ContainingAssembly
{
get
{
return _assemblySymbol;
}
}
public override ImmutableArray<Location> Locations
{
get
{
return this.MetadataLocation.Cast<MetadataLocation, Location>();
}
}
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
if (_lazyCustomAttributes.IsDefault)
{
this.LoadCustomAttributes(Token, ref _lazyCustomAttributes);
}
return _lazyCustomAttributes;
}
internal ImmutableArray<CSharpAttributeData> GetAssemblyAttributes()
{
if (_lazyAssemblyAttributes.IsDefault)
{
ArrayBuilder<CSharpAttributeData> moduleAssemblyAttributesBuilder = null;
string corlibName = ContainingAssembly.CorLibrary.Name;
EntityHandle assemblyMSCorLib = Module.GetAssemblyRef(corlibName);
if (!assemblyMSCorLib.IsNil)
{
foreach (var qualifier in Cci.MetadataWriter.dummyAssemblyAttributeParentQualifier)
{
EntityHandle typerefAssemblyAttributesGoHere =
Module.GetTypeRef(
assemblyMSCorLib,
Cci.MetadataWriter.dummyAssemblyAttributeParentNamespace,
Cci.MetadataWriter.dummyAssemblyAttributeParentName + qualifier);
if (!typerefAssemblyAttributesGoHere.IsNil)
{
try
{
foreach (var customAttributeHandle in Module.GetCustomAttributesOrThrow(typerefAssemblyAttributesGoHere))
{
if (moduleAssemblyAttributesBuilder == null)
{
moduleAssemblyAttributesBuilder = new ArrayBuilder<CSharpAttributeData>();
}
moduleAssemblyAttributesBuilder.Add(new PEAttributeData(this, customAttributeHandle));
}
}
catch (BadImageFormatException)
{ }
}
}
}
ImmutableInterlocked.InterlockedCompareExchange(
ref _lazyAssemblyAttributes,
(moduleAssemblyAttributesBuilder != null) ? moduleAssemblyAttributesBuilder.ToImmutableAndFree() : ImmutableArray<CSharpAttributeData>.Empty,
default(ImmutableArray<CSharpAttributeData>));
}
return _lazyAssemblyAttributes;
}
internal void LoadCustomAttributes(EntityHandle token, ref ImmutableArray<CSharpAttributeData> customAttributes)
{
var loaded = GetCustomAttributesForToken(token);
ImmutableInterlocked.InterlockedInitialize(ref customAttributes, loaded);
}
public bool TryGetNonEmptyCustomAttributes(EntityHandle handle, out CustomAttributeHandleCollection attributes)
{
try
{
attributes = Module.MetadataReader.GetCustomAttributes(handle);
return attributes.Count != 0;
}
catch (BadImageFormatException)
{
attributes = default;
return false;
}
}
public bool AttributeMatchesFilter(CustomAttributeHandle handle, AttributeDescription filter)
=> filter.Signatures != null && Module.GetTargetAttributeSignatureIndex(handle, filter) != -1;
internal ImmutableArray<CSharpAttributeData> GetCustomAttributesForToken(EntityHandle token)
{
if (!TryGetNonEmptyCustomAttributes(token, out var customAttributeHandles))
return [];
return customAttributeHandles.SelectAsArray(static (handle, @this) => (CSharpAttributeData)new PEAttributeData(@this, handle), this);
}
internal bool HasAnyCustomAttributes(EntityHandle token)
{
try
{
foreach (var attr in _module.GetCustomAttributesOrThrow(token))
{
return true;
}
}
catch (BadImageFormatException)
{ }
return false;
}
internal TypeSymbol TryDecodeAttributeWithTypeArgument(EntityHandle handle, AttributeDescription attributeDescription)
{
string typeName;
if (_module.HasStringValuedAttribute(handle, attributeDescription, out typeName))
{
return new MetadataDecoder(this).GetTypeSymbolForSerializedType(typeName);
}
return null;
}
internal void OnNewTypeDeclarationsLoaded(
Dictionary<ReadOnlyMemory<char>, ImmutableArray<PENamedTypeSymbol>> typesDict)
{
bool keepLookingForDeclaredCorTypes = (_ordinal == 0 && _assemblySymbol.KeepLookingForDeclaredSpecialTypes);
foreach (var types in typesDict.Values)
{
foreach (var type in types)
{
if (type.IsExtension)
{
// This symbol represents an extension block, not the marker type itself.
continue;
}
bool added;
added = TypeHandleToTypeMap.TryAdd(type.Handle, type);
Debug.Assert(added);
// Register newly loaded COR types
if (keepLookingForDeclaredCorTypes && type.SpecialType != SpecialType.None)
{
_assemblySymbol.RegisterDeclaredSpecialType(type);
keepLookingForDeclaredCorTypes = _assemblySymbol.KeepLookingForDeclaredSpecialTypes;
}
}
}
}
internal override ICollection<string> TypeNames
{
get
{
if (_lazyTypeNames == null)
{
Interlocked.CompareExchange(ref _lazyTypeNames, _module.TypeNames.AsCaseSensitiveCollection(), null);
}
return _lazyTypeNames;
}
}
internal override ICollection<string> NamespaceNames
{
get
{
if (_lazyNamespaceNames == null)
{
Interlocked.CompareExchange(ref _lazyNamespaceNames, _module.NamespaceNames.AsCaseSensitiveCollection(), null);
}
return _lazyNamespaceNames;
}
}
internal override ImmutableArray<byte> GetHash(AssemblyHashAlgorithm algorithmId)
{
return _module.GetHash(algorithmId);
}
internal DocumentationProvider DocumentationProvider
{
get
{
var assembly = _assemblySymbol as PEAssemblySymbol;
if ((object)assembly != null)
{
return assembly.DocumentationProvider;
}
else
{
return DocumentationProvider.Default;
}
}
}
#nullable enable
internal NamedTypeSymbol EventRegistrationToken
{
get
{
if ((object?)_lazyEventRegistrationTokenSymbol == null)
{
Interlocked.CompareExchange(ref _lazyEventRegistrationTokenSymbol,
GetTypeSymbolForWellKnownType(
WellKnownType.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationToken
),
null);
Debug.Assert((object)_lazyEventRegistrationTokenSymbol != null);
}
return _lazyEventRegistrationTokenSymbol;
}
}
internal NamedTypeSymbol EventRegistrationTokenTable_T
{
get
{
if ((object?)_lazyEventRegistrationTokenTableSymbol == null)
{
Interlocked.CompareExchange(ref _lazyEventRegistrationTokenTableSymbol,
GetTypeSymbolForWellKnownType(
WellKnownType.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T
),
null);
Debug.Assert((object)_lazyEventRegistrationTokenTableSymbol != null);
}
return _lazyEventRegistrationTokenTableSymbol;
}
}
internal NamedTypeSymbol SystemTypeSymbol
{
get
{
if ((object?)_lazySystemTypeSymbol == null)
{
Interlocked.CompareExchange(ref _lazySystemTypeSymbol,
GetTypeSymbolForWellKnownType(WellKnownType.System_Type),
null);
Debug.Assert((object)_lazySystemTypeSymbol != null);
}
return _lazySystemTypeSymbol;
}
}
private NamedTypeSymbol GetTypeSymbolForWellKnownType(WellKnownType type)
{
MetadataTypeName emittedName = MetadataTypeName.FromFullName(type.GetMetadataName(), useCLSCompliantNameArityEncoding: true);
// First, check this module
NamedTypeSymbol? currentModuleResult = this.LookupTopLevelMetadataType(ref emittedName);
Debug.Assert(currentModuleResult?.IsErrorType() != true);
if (currentModuleResult is not null)
{
Debug.Assert(isAcceptableSystemTypeSymbol(currentModuleResult));
// It doesn't matter if there's another of this type in a referenced assembly -
// we prefer the one in the current module.
return currentModuleResult;
}
// If we didn't find it in this module, check the referenced assemblies
NamedTypeSymbol? referencedAssemblyResult = null;
foreach (AssemblySymbol assembly in this.GetReferencedAssemblySymbols())
{
NamedTypeSymbol currResult = assembly.LookupDeclaredOrForwardedTopLevelMetadataType(ref emittedName, visitedAssemblies: null);
if (isAcceptableSystemTypeSymbol(currResult))
{
if ((object?)referencedAssemblyResult == null)
{
referencedAssemblyResult = currResult;
}
else
{
// CONSIDER: setting result to null will result in a MissingMetadataTypeSymbol
// being returned. Do we want to differentiate between no result and ambiguous
// results? There doesn't seem to be an existing error code for "duplicate well-
// known type".
if (!TypeSymbol.Equals(referencedAssemblyResult, currResult, TypeCompareKind.ConsiderEverything2))
{
referencedAssemblyResult = null;
}
break;
}
}
}
if ((object?)referencedAssemblyResult != null)
{
Debug.Assert(isAcceptableSystemTypeSymbol(referencedAssemblyResult));
return referencedAssemblyResult;
}
return new MissingMetadataTypeSymbol.TopLevel(this, ref emittedName);
static bool isAcceptableSystemTypeSymbol(NamedTypeSymbol candidate)
{
return candidate.Kind != SymbolKind.ErrorType || !(candidate is MissingMetadataTypeSymbol);
}
}
#nullable disable
internal override bool HasAssemblyCompilationRelaxationsAttribute
{
get
{
// This API is called only for added modules. Assembly level attributes from added modules are
// copied to the resulting assembly and that is done by using CSharpAttributeData for them.
// Therefore, it is acceptable to implement this property by using the same CSharpAttributeData
// objects rather than trying to avoid creating them and going to metadata directly.
ImmutableArray<CSharpAttributeData> assemblyAttributes = GetAssemblyAttributes();
return assemblyAttributes.IndexOfAttribute(AttributeDescription.CompilationRelaxationsAttribute) >= 0;
}
}
internal override bool HasAssemblyRuntimeCompatibilityAttribute
{
get
{
// This API is called only for added modules. Assembly level attributes from added modules are
// copied to the resulting assembly and that is done by using CSharpAttributeData for them.
// Therefore, it is acceptable to implement this property by using the same CSharpAttributeData
// objects rather than trying to avoid creating them and going to metadata directly.
ImmutableArray<CSharpAttributeData> assemblyAttributes = GetAssemblyAttributes();
return assemblyAttributes.IndexOfAttribute(AttributeDescription.RuntimeCompatibilityAttribute) >= 0;
}
}
internal override CharSet? DefaultMarshallingCharSet
{
get
{
// not used by the compiler
throw ExceptionUtilities.Unreachable();
}
}
internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
{
get { return null; }
}
#nullable enable
internal NamedTypeSymbol LookupTopLevelMetadataTypeWithNoPiaLocalTypeUnification(ref MetadataTypeName emittedName, out bool isNoPiaLocalType)
{
NamedTypeSymbol? result;
var scope = (PENamespaceSymbol?)this.GlobalNamespace.LookupNestedNamespace(emittedName.NamespaceSegmentsMemory);
if ((object?)scope == null)
{
// We failed to locate the namespace
result = null;
}
else
{
result = scope.LookupMetadataType(ref emittedName);
Debug.Assert(result?.IsErrorType() != true);
if (result is null)
{
result = scope.UnifyIfNoPiaLocalType(ref emittedName);
if (result is not null)
{
isNoPiaLocalType = true;
return result;
}
}
}
isNoPiaLocalType = false;
return result ?? new MissingMetadataTypeSymbol.TopLevel(this, ref emittedName);
}
#nullable disable
/// <summary>
/// Returns a tuple of the assemblies this module forwards the given type to.
/// </summary>
/// <param name="fullName">Type to look up.</param>
/// <returns>A tuple of the forwarded to assemblies.</returns>
/// <remarks>
/// The returned assemblies may also forward the type.
/// </remarks>
internal (AssemblySymbol FirstSymbol, AssemblySymbol SecondSymbol) GetAssembliesForForwardedType(ref MetadataTypeName fullName)
{
string matchedName;
(int firstIndex, int secondIndex) = this.Module.GetAssemblyRefsForForwardedType(fullName.FullName, ignoreCase: false, matchedName: out matchedName);
if (firstIndex < 0)
{
return (null, null);
}
AssemblySymbol firstSymbol = GetReferencedAssemblySymbol(firstIndex);
if (secondIndex < 0)
{
return (firstSymbol, null);
}
AssemblySymbol secondSymbol = GetReferencedAssemblySymbol(secondIndex);
return (firstSymbol, secondSymbol);
}
#nullable enable
internal IEnumerable<NamedTypeSymbol> GetForwardedTypes()
{
foreach (KeyValuePair<string, (int FirstIndex, int SecondIndex)> forwarder in Module.GetForwardedTypes())
{
var name = MetadataTypeName.FromFullName(forwarder.Key);
Debug.Assert(forwarder.Value.FirstIndex >= 0, "First index should never be negative");
AssemblySymbol firstSymbol = this.GetReferencedAssemblySymbol(forwarder.Value.FirstIndex);
Debug.Assert((object)firstSymbol != null, "Invalid indexes (out of bound) are discarded during reading metadata in PEModule.EnsureForwardTypeToAssemblyMap()");
if (forwarder.Value.SecondIndex >= 0)
{
var secondSymbol = this.GetReferencedAssemblySymbol(forwarder.Value.SecondIndex);
Debug.Assert((object)secondSymbol != null, "Invalid indexes (out of bound) are discarded during reading metadata in PEModule.EnsureForwardTypeToAssemblyMap()");
yield return ContainingAssembly.CreateMultipleForwardingErrorTypeSymbol(ref name, this, firstSymbol, secondSymbol);
}
else
{
yield return firstSymbol.LookupDeclaredOrForwardedTopLevelMetadataType(ref name, visitedAssemblies: null);
}
}
}
#nullable disable
public override ModuleMetadata GetMetadata() => _module.GetNonDisposableMetadata();
internal bool ShouldDecodeNullableAttributes(Symbol symbol)
{
Debug.Assert(symbol is object);
Debug.Assert(symbol.IsDefinition);
Debug.Assert((object)symbol.ContainingModule == this);
if (_lazyNullableMemberMetadata == NullableMemberMetadata.Unknown)
{
_lazyNullableMemberMetadata = _module.HasNullablePublicOnlyAttribute(Token, out bool includesInternals) ?
(includesInternals ? NullableMemberMetadata.Internal : NullableMemberMetadata.Public) :
NullableMemberMetadata.All;
}
NullableMemberMetadata nullableMemberMetadata = _lazyNullableMemberMetadata;
if (nullableMemberMetadata == NullableMemberMetadata.All)
{
return true;
}
if (AccessCheck.IsEffectivelyPublicOrInternal(symbol, out bool isInternal))
{
switch (nullableMemberMetadata)
{
case NullableMemberMetadata.Public:
return !isInternal;
case NullableMemberMetadata.Internal:
return true;
default:
throw ExceptionUtilities.UnexpectedValue(nullableMemberMetadata);
}
}
return false;
}
#nullable enable
internal DiagnosticInfo? GetCompilerFeatureRequiredDiagnostic()
{
if (_lazyCachedCompilerFeatureRequiredDiagnosticInfo == CSDiagnosticInfo.EmptyErrorInfo)
{
Interlocked.CompareExchange(
ref _lazyCachedCompilerFeatureRequiredDiagnosticInfo,
PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, this, Token, CompilerFeatureRequiredFeatures.None, new MetadataDecoder(this)),
CSDiagnosticInfo.EmptyErrorInfo);
}
return _lazyCachedCompilerFeatureRequiredDiagnosticInfo ?? (_assemblySymbol as PEAssemblySymbol)?.GetCompilerFeatureRequiredDiagnostic();
}
public override bool HasUnsupportedMetadata
=> GetCompilerFeatureRequiredDiagnostic()?.Code == (int)ErrorCode.ERR_UnsupportedCompilerFeature || base.HasUnsupportedMetadata;
internal override bool UseUpdatedEscapeRules
=> RefSafetyRulesVersion == RefSafetyRulesAttributeVersion.Version11;
internal RefSafetyRulesAttributeVersion RefSafetyRulesVersion
{
get
{
if (_lazyRefSafetyRulesAttributeVersion == RefSafetyRulesAttributeVersion.Uninitialized)
{
_lazyRefSafetyRulesAttributeVersion = getAttributeVersion();
}
return _lazyRefSafetyRulesAttributeVersion;
RefSafetyRulesAttributeVersion getAttributeVersion()
{
if (_module.HasRefSafetyRulesAttribute(Token, out int version, out bool foundAttributeType))
{
return version == 11
? RefSafetyRulesAttributeVersion.Version11
: RefSafetyRulesAttributeVersion.UnrecognizedAttribute;
}
return foundAttributeType
? RefSafetyRulesAttributeVersion.UnrecognizedAttribute
: RefSafetyRulesAttributeVersion.NoAttribute;
}
}
}
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
get
{
if (_lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized)
{
var experimentalData = _module.TryDecodeExperimentalAttributeData(Token, new MetadataDecoder(this));
Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, experimentalData, ObsoleteAttributeData.Uninitialized);
}
return _lazyObsoleteAttributeData;
}
}
}
}
|