diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index c81c19a6017005..fbe4b73791710d 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,11 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 27 +#define READYTORUN_MAJOR_VERSION 28 #define READYTORUN_MINOR_VERSION 0x0000 #define MINIMUM_READYTORUN_MAJOR_VERSION 26 +#define READYTORUN_TYPEMAP_TAGGED_TYPES_MAJOR_VERSION 28 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -68,6 +69,8 @@ // R2R Version 26 changes ARM64 NativeVarInfo register encoding to include V0-V31 // R2R Version 26.1 adds READYTORUN_FIXUP_StoreMultiCallableAddrOfCode for storing a method's MultiCallableAddrOfCode into a location in the R2R image (used on WebAssembly) // R2R Version 27 redefines READYTORUN_FIXUP_DeclaringTypeHandle to be encoded as a method signature instead of a pair of type signatures +// R2R Version 28 adds tagged fixup or serialized-name type references to the ExternalTypeMaps and ProxyTypeMaps sections. +// Older supported versions use untagged import-section and fixup-index pairs in these sections. struct READYTORUN_CORE_HEADER { diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index 33f47c9cb339a1..a60454d7c02416 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -11,7 +11,7 @@ struct ReadyToRunHeaderConstants { static const uint32_t Signature = 0x00525452; // 'RTR' - static const uint32_t CurrentMajorVersion = 27; + static const uint32_t CurrentMajorVersion = 28; static const uint32_t CurrentMinorVersion = 0; }; diff --git a/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs index d929eab6a773f4..541445ea041bda 100644 --- a/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs @@ -24,6 +24,94 @@ private enum TypeMapAttributeKind TypeMapAssociation } + private readonly struct TypeWithSerializedName + { + public TypeWithSerializedName(TypeDesc type, string serializedName) + { + Type = type; + SerializedName = serializedName; + } + + public TypeDesc Type { get; } + public string SerializedName { get; } + } + + private readonly struct TypeMapCustomAttributeTypeProvider : ICustomAttributeTypeProvider + { + private readonly CustomAttributeTypeProvider _provider; + + public TypeMapCustomAttributeTypeProvider(EcmaModule module) + { + _provider = new CustomAttributeTypeProvider(module); + } + + public TypeWithSerializedName GetPrimitiveType(PrimitiveTypeCode typeCode) + => new TypeWithSerializedName(_provider.GetPrimitiveType(typeCode), null); + + public TypeWithSerializedName GetSystemType() + => new TypeWithSerializedName(_provider.GetSystemType(), null); + + public TypeWithSerializedName GetSZArrayType(TypeWithSerializedName elementType) + => new TypeWithSerializedName(_provider.GetSZArrayType(elementType.Type), null); + + public TypeWithSerializedName GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) + => new TypeWithSerializedName(_provider.GetTypeFromDefinition(reader, handle, rawTypeKind), null); + + public TypeWithSerializedName GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) + => new TypeWithSerializedName(_provider.GetTypeFromReference(reader, handle, rawTypeKind), null); + + public TypeWithSerializedName GetTypeFromSpecification(MetadataReader reader, TypeSpecificationHandle handle, byte rawTypeKind) + => new TypeWithSerializedName(_provider.GetTypeFromSpecification(reader, handle, rawTypeKind), null); + + public TypeWithSerializedName GetTypeFromSerializedName(string name) + => new TypeWithSerializedName(_provider.GetTypeFromSerializedName(name), name); + + public PrimitiveTypeCode GetUnderlyingEnumType(TypeWithSerializedName type) + => _provider.GetUnderlyingEnumType(type.Type); + + public bool IsSystemType(TypeWithSerializedName type) + => _provider.IsSystemType(type.Type); + } + + internal readonly struct ExternalTypeMapEntry + { + public ExternalTypeMapEntry( + TypeDesc type, + string serializedTypeName, + TypeDesc trimmingType, + string serializedTrimmingTypeName, + ModuleDesc declaringModule) + { + Type = type; + SerializedTypeName = serializedTypeName; + TrimmingType = trimmingType; + SerializedTrimmingTypeName = serializedTrimmingTypeName; + DeclaringModule = declaringModule; + } + + public TypeDesc Type { get; } + public string SerializedTypeName { get; } + public TypeDesc TrimmingType { get; } + public string SerializedTrimmingTypeName { get; } + public ModuleDesc DeclaringModule { get; } + } + + internal readonly struct ProxyTypeMapEntry + { + public ProxyTypeMapEntry(TypeDesc type, string serializedSourceTypeName, string serializedTypeName, ModuleDesc declaringModule) + { + Type = type; + SerializedSourceTypeName = serializedSourceTypeName; + SerializedTypeName = serializedTypeName; + DeclaringModule = declaringModule; + } + + public TypeDesc Type { get; } + public string SerializedSourceTypeName { get; } + public string SerializedTypeName { get; } + public ModuleDesc DeclaringModule { get; } + } + private static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) { var typeDef = attrType.GetTypeDefinition() as MetadataType; @@ -41,13 +129,13 @@ private static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) internal interface IExternalTypeMap { - IReadOnlyDictionary TypeMap { get; } + IReadOnlyDictionary TypeMap { get; } MethodDesc ThrowingMethodStub { get; } } internal interface IProxyTypeMap { - IReadOnlyDictionary TypeMap { get; } + IReadOnlyDictionary TypeMap { get; } MethodDesc ThrowingMethodStub { get; } } @@ -95,8 +183,8 @@ protected override int CompareToImpl(MethodDesc other, TypeSystemComparer compar public override TypeSystemContext Context => OwningType.Context; } - private readonly Dictionary _associatedTypeMap = []; - private readonly Dictionary _externalTypeMap = []; + private readonly Dictionary _associatedTypeMap = []; + private readonly Dictionary _externalTypeMap = []; private readonly List _targetModules = []; private ThrowingMethodStub _externalTypeMapExceptionStub; private ThrowingMethodStub _associatedTypeMapExceptionStub; @@ -116,16 +204,31 @@ public Map(TypeDesc typeMapGroup) /// public bool HasAssemblyTargetAttributes { get; set; } - public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) + public void AddAssociatedTypeMapEntry( + TypeDesc type, + string serializedTypeName, + TypeDesc associatedType, + string serializedAssociatedTypeName, + ModuleDesc declaringModule) { - if (!_associatedTypeMap.TryAdd(type, associatedType)) + if (!_associatedTypeMap.TryAdd( + type, + new ProxyTypeMapEntry(associatedType, serializedTypeName, serializedAssociatedTypeName, declaringModule))) { ThrowHelper.ThrowBadImageFormatException(); } } - public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) + public void AddExternalTypeMapEntry( + string typeName, + TypeDesc type, + string serializedTypeName, + TypeDesc trimmingTarget, + string serializedTrimmingTypeName, + ModuleDesc declaringModule) { - if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) + if (!_externalTypeMap.TryAdd( + typeName, + new ExternalTypeMapEntry(type, serializedTypeName, trimmingTarget, serializedTrimmingTypeName, declaringModule))) { ThrowHelper.ThrowBadImageFormatException(); } @@ -165,9 +268,14 @@ public void MergePendingMap(ModuleDesc stubModule, Map pendingMap) { try { - foreach (KeyValuePair kvp in pendingMap._associatedTypeMap) + foreach (KeyValuePair kvp in pendingMap._associatedTypeMap) { - AddAssociatedTypeMapEntry(kvp.Key, kvp.Value); + AddAssociatedTypeMapEntry( + kvp.Key, + kvp.Value.SerializedSourceTypeName, + kvp.Value.Type, + kvp.Value.SerializedTypeName, + kvp.Value.DeclaringModule); } } catch (TypeSystemException ex) @@ -195,9 +303,15 @@ public void MergePendingMap(ModuleDesc stubModule, Map pendingMap) { try { - foreach (KeyValuePair kvp in pendingMap._externalTypeMap) + foreach (KeyValuePair kvp in pendingMap._externalTypeMap) { - AddExternalTypeMapEntry(kvp.Key, kvp.Value.type, kvp.Value.trimmingTarget); + AddExternalTypeMapEntry( + kvp.Key, + kvp.Value.Type, + kvp.Value.SerializedTypeName, + kvp.Value.TrimmingType, + kvp.Value.SerializedTrimmingTypeName, + kvp.Value.DeclaringModule); } } catch (TypeSystemException ex) @@ -227,11 +341,11 @@ public void AddTargetModule(ModuleDesc targetModule) /// public IReadOnlyList TargetModules => _targetModules; - IReadOnlyDictionary IExternalTypeMap.TypeMap => _externalTypeMap; + IReadOnlyDictionary IExternalTypeMap.TypeMap => _externalTypeMap; MethodDesc IExternalTypeMap.ThrowingMethodStub => _externalTypeMapExceptionStub; - IReadOnlyDictionary IProxyTypeMap.TypeMap => _associatedTypeMap; + IReadOnlyDictionary IProxyTypeMap.TypeMap => _associatedTypeMap; MethodDesc IProxyTypeMap.ThrowingMethodStub => _associatedTypeMapExceptionStub; } @@ -328,7 +442,7 @@ public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly, ModuleDe continue; } - CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); + CustomAttributeValue attrValue = attr.DecodeValue(new TypeMapCustomAttributeTypeProvider(currentAssembly)); TypeDesc typeMapGroup = type.Instantiation[0]; @@ -420,7 +534,7 @@ public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly, ModuleDe scannedAssemblies.Add((currentAssembly, currentTypeMapGroup)); } - void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue, Map typeMapState) + void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue, Map typeMapState) { typeMapState.HasAssemblyTargetAttributes = true; @@ -442,19 +556,40 @@ void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrVa } } - void ProcessTypeMapAttribute(CustomAttributeValue attrValue, Map typeMapState) + void ProcessTypeMapAttribute(CustomAttributeValue attrValue, Map typeMapState) { switch (attrValue.FixedArguments) { - case [{ Value: string typeName }, { Value: TypeDesc targetType }]: + case + [ + { Value: string typeName }, + { Value: TypeWithSerializedName { Type: TypeDesc targetType, SerializedName: string serializedTargetTypeName } } + ]: { - typeMapState.AddExternalTypeMapEntry(typeName, targetType, null); + typeMapState.AddExternalTypeMapEntry( + typeName, + targetType, + serializedTargetTypeName, + null, + null, + currentAssembly); break; } - case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: + case + [ + { Value: string typeName }, + { Value: TypeWithSerializedName { Type: TypeDesc targetType, SerializedName: string serializedTargetTypeName } }, + { Value: TypeWithSerializedName { Type: TypeDesc trimTargetType, SerializedName: string serializedTrimTargetTypeName } } + ]: { - typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); + typeMapState.AddExternalTypeMapEntry( + typeName, + targetType, + serializedTargetTypeName, + trimTargetType, + serializedTrimTargetTypeName, + currentAssembly); break; } @@ -464,17 +599,26 @@ void ProcessTypeMapAttribute(CustomAttributeValue attrValue, Map typeM } } - void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, Map typeMapState) + void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, Map typeMapState) { // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) // and process it. - if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) + if (attrValue.FixedArguments is not + [ + { Value: TypeWithSerializedName { Type: TypeDesc type, SerializedName: string serializedTypeName } }, + { Value: TypeWithSerializedName { Type: TypeDesc associatedType, SerializedName: string serializedAssociatedTypeName } } + ]) { ThrowHelper.ThrowBadImageFormatException(); return; } - typeMapState.AddAssociatedTypeMapEntry(type, associatedType); + typeMapState.AddAssociatedTypeMapEntry( + type, + serializedTypeName, + associatedType, + serializedAssociatedTypeName, + currentAssembly); } } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 2599ae355ebf5d..733e4e3f4c6996 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 27; + public const ushort CurrentMajorVersion = 28; public const ushort CurrentMinorVersion = 0; } #if READYTORUN diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs index 3f2e82302b3bdd..70ed4b62fb8598 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -13,9 +13,9 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class ExternalTypeMapNode : SortableDependencyNode, IExternalTypeMapNode { - private readonly IEnumerable> _mapEntries; + private readonly IEnumerable> _mapEntries; - public ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) + public ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) { _mapEntries = mapEntries; TypeMapGroup = typeMapGroup; @@ -37,7 +37,8 @@ public override IEnumerable GetConditionalStaticDep foreach (var entry in _mapEntries) { - var (targetType, trimmingTargetType) = entry.Value; + TypeDesc targetType = entry.Value.Type; + TypeDesc trimmingTargetType = entry.Value.TrimmingType; if (trimmingTargetType is not null) { IEETypeNode effectiveTrimTargetType = GetEffectiveTrimTargetType(context, trimmingTargetType); @@ -58,7 +59,8 @@ public override IEnumerable GetStaticDependencies(NodeFacto { foreach (var entry in _mapEntries) { - var (targetType, trimmingTargetType) = entry.Value; + TypeDesc targetType = entry.Value.Type; + TypeDesc trimmingTargetType = entry.Value.TrimmingType; if (trimmingTargetType is null) { yield return new DependencyListEntry( @@ -83,7 +85,8 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer { foreach (var entry in _mapEntries) { - var (targetType, trimmingTargetType) = entry.Value; + TypeDesc targetType = entry.Value.Type; + TypeDesc trimmingTargetType = entry.Value.TrimmingType; if (trimmingTargetType is null || GetEffectiveTrimTargetType(factory, trimmingTargetType).Marked) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs index e82bb502c2cbe6..46059fb6484fe4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -15,9 +15,9 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class ProxyTypeMapNode : SortableDependencyNode, IProxyTypeMapNode { - private readonly IEnumerable> _mapEntries; + private readonly IEnumerable> _mapEntries; - public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) + public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) { _mapEntries = mapEntries; TypeMapGroup = typeMapGroup; @@ -25,7 +25,7 @@ public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> MapEntries => _mapEntries; + public IEnumerable> MapEntries => _mapEntries; public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -40,10 +40,10 @@ public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable GetConditionalStaticDependencies(NodeFactory context) { - foreach (var (key, value) in _mapEntries) + foreach (var (key, entry) in _mapEntries) { yield return new CombinedDependencyListEntry( - context.MetadataTypeSymbol(value), + context.MetadataTypeSymbol(entry.Type), context.MaximallyConstructableType(key), "Proxy type map entry"); @@ -66,12 +66,12 @@ public override IEnumerable GetConditionalStaticDep private IEnumerable<(IEETypeNode key, IEETypeNode value)> GetMarkedEntries(NodeFactory factory) { - foreach (var (key, value) in MapEntries) + foreach (var (key, entry) in MapEntries) { IEETypeNode keyNode = factory.MaximallyConstructableType(key); if (keyNode.Marked) { - IEETypeNode valueNode = factory.MetadataTypeSymbol(value); + IEETypeNode valueNode = factory.MetadataTypeSymbol(entry.Type); Debug.Assert(valueNode.Marked); yield return (keyNode, valueNode); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs index d02586c9093220..f3c4e955d8f2e6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs @@ -11,6 +11,42 @@ namespace ILCompiler.ReadyToRun { + internal static class ReadyToRunTypeMapReference + { + private const uint Fixup = 0; + private const uint SerializedName = 1; + + public static bool HasFixup(NodeFactory factory, TypeDesc type) + => factory.CompilationModuleGroup.VersionsWithTypeReference(type); + + public static Vertex Encode( + NodeFactory factory, + NativeWriter writer, + INativeFormatTypeReferenceProvider references, + ModuleDesc triggeringModule, + TypeDesc type, + string serializedTypeName, + ModuleDesc declaringModule) + { + if (HasFixup(factory, type)) + { + return writer.GetTuple( + writer.GetUnsignedConstant(Fixup), + references.EncodeReferenceToType(writer, type, triggeringModule)); + } + + if (declaringModule.Assembly != triggeringModule.Assembly) + { + throw new InternalCompilerErrorException( + $"Serialized TypeMap type name '{serializedTypeName}' was declared in '{declaringModule}' but would be resolved relative to '{triggeringModule}'."); + } + + return writer.GetTuple( + writer.GetUnsignedConstant(SerializedName), + writer.GetStringConstant(serializedTypeName)); + } + } + internal class ReadyToRunExternalTypeMapNode(ModuleDesc triggeringModule, TypeDesc group, TypeMapMetadata.IExternalTypeMap map, ImportReferenceProvider importProvider) : SortableDependencyNode, IExternalTypeMapNode { public TypeDesc TypeMapGroup => group; @@ -53,10 +89,17 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se Section typeMapEntriesSection = writer.NewSection(); - foreach ((string key, (TypeDesc type, _)) in map.TypeMap) + foreach ((string key, TypeMapMetadata.ExternalTypeMapEntry mapEntry) in map.TypeMap) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type, TriggeringModule); + Vertex valueVertex = ReadyToRunTypeMapReference.Encode( + factory, + writer, + externalReferences, + TriggeringModule, + mapEntry.Type, + mapEntry.SerializedTypeName, + mapEntry.DeclaringModule); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)VersionResilientHashCode.NameHashCode(Encoding.UTF8.GetBytes(key)), typeMapEntriesSection.Place(entry)); } @@ -78,7 +121,10 @@ public override IEnumerable GetStaticDependencies(NodeFacto foreach (var entry in map.TypeMap) { - yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value.type, TriggeringModule), $"External type map entry target for key '{entry.Key}'"); + if (ReadyToRunTypeMapReference.HasFixup(context, entry.Value.Type)) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value.Type, TriggeringModule), $"External type map entry target for key '{entry.Key}'"); + } } } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs index 6f5e80a00f2e4b..02dd54dee8437f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs @@ -53,10 +53,24 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se Section typeMapEntriesSection = writer.NewSection(); - foreach ((TypeDesc type, TypeDesc targetType) in map.TypeMap) + foreach ((TypeDesc type, TypeMapMetadata.ProxyTypeMapEntry mapEntry) in map.TypeMap) { - Vertex keyVertex = ProxyReferences.EncodeReferenceToType(writer, type, TriggeringModule); - Vertex valueVertex = ProxyReferences.EncodeReferenceToType(writer, targetType, TriggeringModule); + Vertex keyVertex = ReadyToRunTypeMapReference.Encode( + factory, + writer, + ProxyReferences, + TriggeringModule, + type, + mapEntry.SerializedSourceTypeName, + mapEntry.DeclaringModule); + Vertex valueVertex = ReadyToRunTypeMapReference.Encode( + factory, + writer, + ProxyReferences, + TriggeringModule, + mapEntry.Type, + mapEntry.SerializedTypeName, + mapEntry.DeclaringModule); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)type.GetHashCode(), typeMapEntriesSection.Place(entry)); } @@ -78,8 +92,14 @@ public override IEnumerable GetStaticDependencies(NodeFacto foreach (var entry in map.TypeMap) { - yield return new DependencyListEntry(importProvider.GetImportToType(entry.Key, TriggeringModule), $"Key type of Proxy type map entry"); - yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value, TriggeringModule), $"Proxy type map entry target for key '{entry.Key}'"); + if (ReadyToRunTypeMapReference.HasFixup(context, entry.Key)) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Key, TriggeringModule), $"Key type of Proxy type map entry"); + } + if (ReadyToRunTypeMapReference.HasFixup(context, entry.Value.Type)) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value.Type, TriggeringModule), $"Proxy type map entry target for key '{entry.Key}'"); + } } } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs index e81aaae9ffe80e..16118bccdf7637 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs @@ -137,14 +137,6 @@ static string GetNameOfAssemblyRefWhichResolvesToType(ModuleDesc module, Metadat return assemblyName; } - // Some producers encode type map custom attributes without emitting matching TypeRef rows - // for the referenced types. In that case, fall back to the target type's defining assembly. - if (module is EcmaModule ecmaModule && type.Module is EcmaModule targetTypeModule) - { - string targetAssemblyName = targetTypeModule.Assembly.GetName().Name; - return targetAssemblyName; - } - throw new KeyNotFoundException($"Unable to resolve an assembly reference from module '{module}' to type '{type}'."); } } diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 09dcff1745694c..6856472aaa016b 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1851,6 +1851,7 @@ extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeMapEnt MethodTable* groupTypeMT = groupTypeTH.AsMethodTable(); resultTypeHnd = pR2RInfo->FindPrecachedExternalTypeMapEntry( + pModule, groupTypeMT, key); } @@ -1884,6 +1885,7 @@ extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( MethodTable* groupTypeMT = groupTypeTH.AsMethodTable(); resultTypeHnd = pR2RInfo->FindPrecachedProxyTypeMapEntry( + pModule, groupTypeMT, pType.AsTypeHandle()); } diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 0ddc7d72e47c5b..6a2bdc82f561ed 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -19,6 +19,7 @@ #include "dn-stdio.h" #include "ilstubcache.h" #include "sigbuilder.h" +#include "typeparse.h" #include "perfmap.h" @@ -1711,6 +1712,12 @@ void ReadyToRunInfo::DisableCustomAttributeFilter() namespace { + enum class TypeMapTypeReferenceKind : uint32_t + { + Fixup = 0, + SerializedName = 1, + }; + TypeHandle GetTypeHandleForNativeFormatFixupReference(PTR_ReadyToRunInfo pR2RInfo, PTR_Module pModule, uint32_t importSection, uint32_t fixupIndex) { STANDARD_VM_CONTRACT; @@ -1741,6 +1748,47 @@ namespace return *(TypeHandle*)fixupAddress; } + TypeHandle GetTypeHandleForTypeMapReference( + PTR_ReadyToRunInfo pR2RInfo, + PTR_Module pModule, + PTR_Module pRequestingModule, + NativeParser& entryParser) + { + STANDARD_VM_CONTRACT; + + if (!pR2RInfo->IsImageVersionAtLeast(READYTORUN_TYPEMAP_TAGGED_TYPES_MAJOR_VERSION, 0)) + { + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + return GetTypeHandleForNativeFormatFixupReference(pR2RInfo, pModule, importSection, fixupIndex); + } + + TypeMapTypeReferenceKind kind = static_cast(entryParser.GetUnsigned()); + switch (kind) + { + case TypeMapTypeReferenceKind::Fixup: + { + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + return GetTypeHandleForNativeFormatFixupReference(pR2RInfo, pModule, importSection, fixupIndex); + } + + case TypeMapTypeReferenceKind::SerializedName: + { + PTR_CBYTE serializedName; + uint32_t serializedNameLength; + entryParser.GetString(&serializedName, &serializedNameLength); + + StackSString typeName; + typeName.SetUTF8(reinterpret_cast(serializedName), serializedNameLength); + return TypeName::GetTypeReferencedByCustomAttribute(typeName.GetUnicode(), pRequestingModule->GetAssembly()); + } + + default: + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } + } + Module* GetModuleForNativeFormatFixupReference(PTR_ReadyToRunInfo pR2RInfo, PTR_Module pModule, uint32_t importSection, uint32_t fixupIndex) { STANDARD_VM_CONTRACT; @@ -1800,10 +1848,12 @@ bool ReadyToRunInfo::HasPrecachedExternalTypeMap(MethodTable* pGroupTypeMT) return false; } -TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(MethodTable* pGroupType, LPCUTF8 pKey) +TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(Module* pRequestingModule, MethodTable* pGroupType, LPCUTF8 pKey) { STANDARD_VM_CONTRACT; + _ASSERTE(pRequestingModule != nullptr); + _ASSERTE(pRequestingModule == m_pModule); _ASSERTE(pGroupType != nullptr); if (m_externalTypeMaps.IsNull()) { @@ -1840,9 +1890,7 @@ TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(MethodTable* pGroup if (typeMapEntryParser.StringEquals(pKey, keyLen)) { typeMapEntryParser.SkipString(); - uint32_t resultImportSection = typeMapEntryParser.GetUnsigned(); - uint32_t resultFixupIndex = typeMapEntryParser.GetUnsigned(); - return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, resultImportSection, resultFixupIndex); + return GetTypeHandleForTypeMapReference(this, m_pModule, pRequestingModule, typeMapEntryParser); } } @@ -1932,10 +1980,12 @@ bool ReadyToRunInfo::HasPrecachedProxyTypeMap(MethodTable* pGroupType) return false; } -TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key) +TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(Module* pRequestingModule, MethodTable* pGroupType, TypeHandle key) { STANDARD_VM_CONTRACT; + _ASSERTE(pRequestingModule != nullptr); + _ASSERTE(pRequestingModule == m_pModule); _ASSERTE(pGroupType != nullptr); if (m_proxyTypeMaps.IsNull()) { @@ -1968,17 +2018,13 @@ TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupTyp NativeParser typeMapEntryParser; while (typeMapLookup.GetNext(typeMapEntryParser)) { - uint32_t keyImportSection = typeMapEntryParser.GetUnsigned(); - uint32_t keyFixupIndex = typeMapEntryParser.GetUnsigned(); - TypeHandle keyTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, keyImportSection, keyFixupIndex); + TypeHandle keyTypeHandle = GetTypeHandleForTypeMapReference(this, m_pModule, pRequestingModule, typeMapEntryParser); if (keyTypeHandle != key) { continue; } - uint32_t resultImportSection = typeMapEntryParser.GetUnsigned(); - uint32_t resultFixupIndex = typeMapEntryParser.GetUnsigned(); - return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, resultImportSection, resultFixupIndex); + return GetTypeHandleForTypeMapReference(this, m_pModule, pRequestingModule, typeMapEntryParser); } // No matching entry found in the table. diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index c63f5cc1a3dedc..a6b037db08aa7b 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -418,12 +418,12 @@ class ReadyToRunInfo void DisableCustomAttributeFilter(); bool HasPrecachedExternalTypeMap(MethodTable* pGroupType); - TypeHandle FindPrecachedExternalTypeMapEntry(MethodTable* pGroupType, LPCUTF8 pKey); + TypeHandle FindPrecachedExternalTypeMapEntry(Module* pRequestingModule, MethodTable* pGroupType, LPCUTF8 pKey); bool CheckForUniqueExternalTypeMapKeys(MethodTable* pGroupType, ExternalTypeNameHash *pHash); bool HasPrecachedProxyTypeMap(MethodTable* pGroupType); - TypeHandle FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key); + TypeHandle FindPrecachedProxyTypeMapEntry(Module* pRequestingModule, MethodTable* pGroupType, TypeHandle key); bool HasTypeMapAssemblyTargets(MethodTable* pGroupType, COUNT_T* pCount); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index 8ac8130065b09c..744cacf3ac6f8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -406,12 +407,27 @@ public unsafe Type GetOrLoadType() private sealed class LazyExternalTypeDictionary : LazyTypeLoadDictionary { private readonly Dictionary _lazyData = []; + private readonly ConcurrentDictionary<(RuntimeModule Module, string Key), RuntimeType> _preCachedTypes = []; protected override bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, string key, [NotNullWhen(true)] out Type? type) { + if (_preCachedTypes.TryGetValue((module, key), out RuntimeType? cachedType)) + { + type = cachedType; + return true; + } + IntPtr handle = FindPrecachedExternalTypeMapEntry(new QCallModule(ref module), new QCallTypeHandle(ref _groupType), key); - type = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); - return type != null; + RuntimeType? resolvedType = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); + if (resolvedType is null) + { + type = null; + return false; + } + + _preCachedTypes.TryAdd((module, key), resolvedType); + type = resolvedType; + return true; } protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Type? type) @@ -480,13 +496,28 @@ public void Add(SourceProxyPair newEntryMaybe) } private readonly Dictionary _lazyData = new(); + private readonly ConcurrentDictionary<(RuntimeModule Module, RuntimeType Key), RuntimeType> _preCachedTypes = []; protected override bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, Type key, [NotNullWhen(true)] out Type? type) { RuntimeType rtKey = (RuntimeType)key; + if (_preCachedTypes.TryGetValue((module, rtKey), out RuntimeType? cachedType)) + { + type = cachedType; + return true; + } + IntPtr handle = FindPrecachedProxyTypeMapEntry(new QCallModule(ref module), new QCallTypeHandle(ref _groupType), new QCallTypeHandle(ref rtKey)); - type = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); - return type != null; + RuntimeType? resolvedType = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); + if (resolvedType is null) + { + type = null; + return false; + } + + _preCachedTypes.TryAdd((module, rtKey), resolvedType); + type = resolvedType; + return true; } protected override bool TryGetOrLoadType(Type key, [NotNullWhen(true)] out Type? type) diff --git a/src/tests/Interop/TypeMap/TypeMapApp.cs b/src/tests/Interop/TypeMap/TypeMapApp.cs index 6d92f3fac52e9c..a294812aa95502 100644 --- a/src/tests/Interop/TypeMap/TypeMapApp.cs +++ b/src/tests/Interop/TypeMap/TypeMapApp.cs @@ -323,11 +323,17 @@ public static void Validate_BlobOnlyAttributeTypeNames() Assert.Equal(typeof(C1), externalMap["blob_only_c1"]); Assert.Equal(typeof(S1), externalMap["blob_only_s1"]); Assert.Equal(typeof(Lib5Type1), externalMap["lib5_type1"]); + Assert.True(externalMap.TryGetValue("lib5_type1", out Type? cachedExternalType)); + Assert.Equal(typeof(Lib5Type1), cachedExternalType); IReadOnlyDictionary proxyMap = TypeMapping.GetOrCreateProxyTypeMapping(); Assert.Equal(typeof(S1), proxyMap[typeof(C1)]); Assert.Equal(typeof(C1), proxyMap[typeof(S1)]); Assert.Equal(typeof(Lib5Proxy1), proxyMap[new Lib5Type1().GetType()]); + Assert.Equal(typeof(C1), proxyMap[new DupType_MapObject().GetType()]); + Assert.Equal(typeof(S1), proxyMap[new DupType_MapString().GetType()]); + Assert.True(proxyMap.TryGetValue(typeof(Lib5Type1), out Type? cachedProxyType)); + Assert.Equal(typeof(Lib5Proxy1), cachedProxyType); } [Fact] diff --git a/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il b/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il index 3a75d49cff9f85..6b5a374134e013 100644 --- a/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il +++ b/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il @@ -34,5 +34,15 @@ = {type(class 'Lib5Type1, TypeMapLib5') type(class 'Lib5Proxy1, TypeMapLib5')} + // These source types have the same full name but come from different assemblies, so their + // proxy map entries share a hash bucket and require exact lazy type resolution. + .custom instance void class [System.Runtime.InteropServices]System.Runtime.InteropServices.TypeMapAssociationAttribute`1::.ctor(class [System.Runtime]System.Type, class [System.Runtime]System.Type) + = {type(class 'Lib.AliasedName, TypeMapLib2') + type(class 'C1, TypeMapLib1')} + + .custom instance void class [System.Runtime.InteropServices]System.Runtime.InteropServices.TypeMapAssociationAttribute`1::.ctor(class [System.Runtime]System.Type, class [System.Runtime]System.Type) + = {type(class 'Lib.AliasedName, TypeMapApp') + type(class 'S1, TypeMapLib1')} + .ver 0:0:0:0 }