From 47de42f423573505c31f7733da4680e3558e6a8c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 29 Jul 2026 15:13:18 -0700 Subject: [PATCH 1/6] Ensure the correct tokens are available for typerefs that may only be in assembly attributes --- .../INativeFormatTypeReferenceProvider.cs | 2 +- .../DependencyAnalysis/NodeFactory.cs | 2 +- .../ImportReferenceProvider.cs | 19 ++++++++++++++----- .../Compiler/ReadyToRunCodegenCompilation.cs | 4 ++-- .../Compiler/ReadyToRunExternalTypeMapNode.cs | 8 ++++---- .../Compiler/ReadyToRunProxyTypeMapNode.cs | 12 ++++++------ .../Compiler/TypeMapAssemblyTargetsNode.cs | 4 ++-- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs index da21e8771618b0..b8da6d5cba6431 100644 --- a/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs +++ b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs @@ -8,7 +8,7 @@ namespace ILCompiler.DependencyAnalysis { public interface INativeFormatTypeReferenceProvider { - internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type); + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module = null); internal Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 636bf28595a23f..72f3ad6edc1bd4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -1703,7 +1703,7 @@ private sealed class ExternalReferencesTableIndex(ExternalReferencesTableNode ta { public Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => writer.GetUnsignedConstant(table.GetIndex(factory.MethodEntrypoint(method))); - public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) + public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module = null) => writer.GetUnsignedConstant(table.GetIndex(factory.NecessaryTypeSymbol(type))); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs index 1fdb5029a286d5..64563b1d553b8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -4,6 +4,7 @@ using System; using ILCompiler.DependencyAnalysis.ReadyToRun; +using ILCompiler.ReadyToRun; using Internal.TypeSystem; using Internal.NativeFormat; using Internal.ReadyToRunConstants; @@ -15,9 +16,15 @@ namespace ILCompiler.DependencyAnalysis public sealed class ImportReferenceProvider : INativeFormatTypeReferenceProvider { private ReadyToRunSymbolNodeFactory _symbolNodeFactory; + private ExternalReferenceTokenManager _externalReferenceTokenManager; - public Import GetImportToType(TypeDesc type) + public Import GetImportToType(TypeDesc type, ModuleDesc module = null) { + if (module is not null) + { + _externalReferenceTokenManager.EnsureDefTokensAreAvailable(type, module, referencesAreForAsyncMethod: false); + } + return _symbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type); } @@ -27,9 +34,10 @@ public Import GetImportToModule(ModuleDesc module) return _symbolNodeFactory.ModuleLookup((IEcmaModule)module); } - public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory) + internal void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory, ExternalReferenceTokenManager externalReferenceTokenManager) { _symbolNodeFactory = symbolNodeFactory; + _externalReferenceTokenManager = externalReferenceTokenManager; } internal Vertex EncodeReferenceToModule(NativeWriter writer, ModuleDesc module) @@ -38,13 +46,14 @@ internal Vertex EncodeReferenceToModule(NativeWriter writer, ModuleDesc module) return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); } - internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module = null) { - Import typeImport = GetImportToType(type); + Debug.Assert(module is not null); + Import typeImport = GetImportToType(type, module); return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); } Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => throw new NotImplementedException(); - Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type) => EncodeReferenceToType(writer, type); + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module) => EncodeReferenceToType(writer, type, module); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 18d4f644407ddb..df57bf96eb6939 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -370,12 +370,13 @@ internal ReadyToRunCodegenCompilation( _customPESectionAlignment = customPESectionAlignment; _format = format; SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout); + _tokenManager = new ExternalReferenceTokenManager(_nodeFactory.ManifestMetadataTable._mutableModule, _nodeFactory.Resolver); if (nodeFactory.InstrumentationDataTable != null) nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory); if (nodeFactory.CrossModuleInlningInfo != null) nodeFactory.CrossModuleInlningInfo.Initialize(SymbolNodeFactory); if (nodeFactory.ImportReferenceProvider != null) - nodeFactory.ImportReferenceProvider.Initialize(SymbolNodeFactory); + nodeFactory.ImportReferenceProvider.Initialize(SymbolNodeFactory, _tokenManager); _inputFiles = inputFiles; _compositeRootPath = compositeRootPath; _printReproInstructions = printReproInstructions; @@ -393,7 +394,6 @@ internal ReadyToRunCodegenCompilation( _profileData = profileData; _fileLayoutOptimizer = new FileLayoutOptimizer(logger, methodLayoutAlgorithm, fileLayoutAlgorithm, profileData, _nodeFactory); - _tokenManager = new ExternalReferenceTokenManager(_nodeFactory.ManifestMetadataTable._mutableModule, _nodeFactory.Resolver); } private readonly static string s_folderUpPrefix = ".." + Path.DirectorySeparatorChar; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs index 63a710de768919..d02586c9093220 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs @@ -39,7 +39,7 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, TriggeringModule); if (map.ThrowingMethodStub is not null) { // We don't write out the throwing method stub for R2R @@ -56,7 +56,7 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se foreach ((string key, (TypeDesc type, _)) in map.TypeMap) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type, TriggeringModule); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)VersionResilientHashCode.NameHashCode(Encoding.UTF8.GetBytes(key)), typeMapEntriesSection.Place(entry)); } @@ -69,7 +69,7 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; public override IEnumerable GetStaticDependencies(NodeFactory context) { - yield return new DependencyListEntry(importProvider.GetImportToType(TypeMapGroup), $"Type map '{TypeMapGroup}' key type"); + yield return new DependencyListEntry(importProvider.GetImportToType(TypeMapGroup, TriggeringModule), $"Type map '{TypeMapGroup}' key type"); if (map.ThrowingMethodStub is not null) { @@ -78,7 +78,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto foreach (var entry in map.TypeMap) { - yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value.type), $"External type map entry target for key '{entry.Key}'"); + 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 5f70c66b4b32ef..6f5e80a00f2e4b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs @@ -37,7 +37,7 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider ProxyReferences) { - Vertex typeMapGroupVertex = ProxyReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = ProxyReferences.EncodeReferenceToType(writer, TypeMapGroup, TriggeringModule); if (map.ThrowingMethodStub is not null) { // We don't write out the throwing method stub for R2R @@ -55,8 +55,8 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se foreach ((TypeDesc type, TypeDesc targetType) in map.TypeMap) { - Vertex keyVertex = ProxyReferences.EncodeReferenceToType(writer, type); - Vertex valueVertex = ProxyReferences.EncodeReferenceToType(writer, targetType); + Vertex keyVertex = ProxyReferences.EncodeReferenceToType(writer, type, TriggeringModule); + Vertex valueVertex = ProxyReferences.EncodeReferenceToType(writer, targetType, TriggeringModule); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)type.GetHashCode(), typeMapEntriesSection.Place(entry)); } @@ -69,7 +69,7 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; public override IEnumerable GetStaticDependencies(NodeFactory context) { - yield return new DependencyListEntry(importProvider.GetImportToType(TypeMapGroup), $"Type map '{TypeMapGroup}' key type"); + yield return new DependencyListEntry(importProvider.GetImportToType(TypeMapGroup, TriggeringModule), $"Type map '{TypeMapGroup}' key type"); if (map.ThrowingMethodStub is not null) { @@ -78,8 +78,8 @@ public override IEnumerable GetStaticDependencies(NodeFacto foreach (var entry in map.TypeMap) { - yield return new DependencyListEntry(importProvider.GetImportToType(entry.Key), $"Key type of Proxy type map entry"); - yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value), $"Proxy type map entry target for key '{entry.Key}'"); + 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}'"); } } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs index ac10f71ec41ee9..d723fc8857cbe0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs @@ -48,7 +48,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact continue; var groupType = map.Key; - dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToType(groupType), "Type Map Assembly Target")); + dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToType(groupType, _assemblyTypeMaps.AssociatedModule), "Type Map Assembly Target")); foreach (var targetModule in map.Value.TargetModules) { dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToModule(targetModule), "Type Map Assembly Target")); @@ -79,7 +79,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) continue; var groupType = map.Key; - Vertex groupTypeVertex = _importReferenceProvider.EncodeReferenceToType(writer, groupType); + Vertex groupTypeVertex = _importReferenceProvider.EncodeReferenceToType(writer, groupType, _assemblyTypeMaps.AssociatedModule); VertexSequence modules = new(); foreach (var targetModule in map.Value.TargetModules) { From 67757470d3df9dd26f320f4345b67b87c9571581 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 29 Jul 2026 16:07:26 -0700 Subject: [PATCH 2/6] Add TypeMap blob-only IL regression test --- src/tests/Interop/TypeMap/GroupTypes.cs | 2 ++ src/tests/Interop/TypeMap/TypeMapApp.cs | 15 ++++++++++ src/tests/Interop/TypeMap/TypeMapApp.csproj | 1 + .../Interop/TypeMap/TypeMapBlobOnlyLib.il | 30 +++++++++++++++++++ .../Interop/TypeMap/TypeMapBlobOnlyLib.ilproj | 10 +++++++ 5 files changed, 58 insertions(+) create mode 100644 src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il create mode 100644 src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.ilproj diff --git a/src/tests/Interop/TypeMap/GroupTypes.cs b/src/tests/Interop/TypeMap/GroupTypes.cs index e178526f22851c..436bc218f03f2b 100644 --- a/src/tests/Interop/TypeMap/GroupTypes.cs +++ b/src/tests/Interop/TypeMap/GroupTypes.cs @@ -26,3 +26,5 @@ public class MultipleTypeMapAssemblies { } public class DuplicateTypeMapEntriesAcrossAssemblies { } public class UnknownAssemblyReference { } + +public class BlobOnlyAttributeTypeNames { } diff --git a/src/tests/Interop/TypeMap/TypeMapApp.cs b/src/tests/Interop/TypeMap/TypeMapApp.cs index 9c07412d5e3282..c243f3ce928395 100644 --- a/src/tests/Interop/TypeMap/TypeMapApp.cs +++ b/src/tests/Interop/TypeMap/TypeMapApp.cs @@ -19,6 +19,7 @@ [assembly: TypeMapAssemblyTarget("TypeMapLib3")] [assembly: TypeMapAssemblyTarget("DoesNotExist")] +[assembly: TypeMapAssemblyTarget("TypeMapBlobOnlyLib")] [assembly: TypeMap("1", typeof(C1))] [assembly: TypeMap("2", typeof(S1))] @@ -313,6 +314,20 @@ public static void Validate_MissingAssemblyTarget_DoesNotAffectGroupsWithoutTarg } } + [Fact] + public static void Validate_BlobOnlyAttributeTypeNames() + { + Console.WriteLine(nameof(Validate_BlobOnlyAttributeTypeNames)); + + IReadOnlyDictionary externalMap = TypeMapping.GetOrCreateExternalTypeMapping(); + Assert.Equal(typeof(C1), externalMap["blob_only_c1"]); + Assert.Equal(typeof(S1), externalMap["blob_only_s1"]); + + IReadOnlyDictionary proxyMap = TypeMapping.GetOrCreateProxyTypeMapping(); + Assert.Equal(typeof(S1), proxyMap[typeof(C1)]); + Assert.Equal(typeof(C1), proxyMap[typeof(S1)]); + } + [Fact] public static void Validate_EmptyOrInvalidMappings() { diff --git a/src/tests/Interop/TypeMap/TypeMapApp.csproj b/src/tests/Interop/TypeMap/TypeMapApp.csproj index cefd065273d788..c4abcd1b407a06 100644 --- a/src/tests/Interop/TypeMap/TypeMapApp.csproj +++ b/src/tests/Interop/TypeMap/TypeMapApp.csproj @@ -18,6 +18,7 @@ + diff --git a/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il b/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il new file mode 100644 index 00000000000000..78e9cf7b347635 --- /dev/null +++ b/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.il @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { } +.assembly extern System.Runtime.InteropServices { } +.assembly extern TypeMapLib1 { } + +.assembly TypeMapBlobOnlyLib +{ + // Keys and mapped types are encoded directly in the custom attribute blob as SerString values. + // This intentionally avoids any IL usage of C1/S1 so there is no requirement for TypeRef rows + // for the mapped types outside of custom attribute blobs. + .custom instance void class [System.Runtime.InteropServices]System.Runtime.InteropServices.TypeMapAttribute`1::.ctor(string, class [System.Runtime]System.Type) + = {string('blob_only_c1') + type(class 'C1, TypeMapLib1')} + + .custom instance void class [System.Runtime.InteropServices]System.Runtime.InteropServices.TypeMapAttribute`1::.ctor(string, class [System.Runtime]System.Type) + = {string('blob_only_s1') + type(class 'S1, 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 'C1, TypeMapLib1') + type(class 'S1, 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 'S1, TypeMapLib1') + type(class 'C1, TypeMapLib1')} + + .ver 0:0:0:0 +} diff --git a/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.ilproj b/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.ilproj new file mode 100644 index 00000000000000..16590112993f78 --- /dev/null +++ b/src/tests/Interop/TypeMap/TypeMapBlobOnlyLib.ilproj @@ -0,0 +1,10 @@ + + + library + true + true + + + + + From 020d683680e3797b339e61da3d74bafa0abd04ef Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Jul 2026 12:42:58 -0700 Subject: [PATCH 3/6] Handle blob-only typemap types without AssemblyRef rows --- .../TypeSystem/Mutable/MutableModule.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) 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 116d6d71bc0bff..ee304e151b1af5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs @@ -132,7 +132,32 @@ static string GetNameOfAssemblyRefWhichResolvesToType(ModuleDesc module, Metadat s_assemblyNameFromTypeLookups.AddOrUpdate(module, lookupTable); } - return lookupTable[type]; + if (lookupTable.TryGetValue(type, out string assemblyName)) + { + 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; + MetadataReader reader = ecmaModule.MetadataReader; + foreach (AssemblyReferenceHandle assemblyRefHandle in reader.AssemblyReferences) + { + string referencedAssemblyName = reader.GetString(reader.GetAssemblyReference(assemblyRefHandle).Name); + if (string.Equals(referencedAssemblyName, targetAssemblyName, StringComparison.Ordinal)) + { + return targetAssemblyName; + } + } + + // The assembly name in a custom attribute type argument is valid even when there is no + // corresponding AssemblyRef row in the source module metadata. + return targetAssemblyName; + } + + throw new KeyNotFoundException($"Unable to resolve an assembly reference from module '{module}' to type '{type}'."); } } From bd78634903c7f7d32ed761baeef5389ce9ea5a62 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Jul 2026 13:35:52 -0700 Subject: [PATCH 4/6] Avoid extra work when looking for assembly references Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../TypeSystem/Mutable/MutableModule.cs | 10 ---------- 1 file changed, 10 deletions(-) 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 ee304e151b1af5..3d85ea819ac4b0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs @@ -142,20 +142,10 @@ static string GetNameOfAssemblyRefWhichResolvesToType(ModuleDesc module, Metadat if (module is EcmaModule ecmaModule && type.Module is EcmaModule targetTypeModule) { string targetAssemblyName = targetTypeModule.Assembly.GetName().Name; - MetadataReader reader = ecmaModule.MetadataReader; - foreach (AssemblyReferenceHandle assemblyRefHandle in reader.AssemblyReferences) - { - string referencedAssemblyName = reader.GetString(reader.GetAssemblyReference(assemblyRefHandle).Name); - if (string.Equals(referencedAssemblyName, targetAssemblyName, StringComparison.Ordinal)) - { - return targetAssemblyName; - } - } // The assembly name in a custom attribute type argument is valid even when there is no // corresponding AssemblyRef row in the source module metadata. return targetAssemblyName; - } throw new KeyNotFoundException($"Unable to resolve an assembly reference from module '{module}' to type '{type}'."); } From fab95192f1cc38b8decde2ba2a20fd0b18fef511 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Jul 2026 13:53:42 -0700 Subject: [PATCH 5/6] Make module argument to EncodeReferenceToType explicit at all call sites --- .../Compiler/INativeFormatTypeReferenceProvider.cs | 2 +- .../DependencyAnalysis/AnalyzedExternalTypeMapNode.cs | 4 ++-- .../DependencyAnalysis/AnalyzedProxyTypeMapNode.cs | 6 +++--- .../Compiler/DependencyAnalysis/ExternalTypeMapNode.cs | 4 ++-- .../DependencyAnalysis/InvalidExternalTypeMapNode.cs | 2 +- .../DependencyAnalysis/InvalidProxyTypeMapNode.cs | 2 +- .../Compiler/DependencyAnalysis/NodeFactory.cs | 2 +- .../Compiler/DependencyAnalysis/ProxyTypeMapNode.cs | 6 +++--- .../DependencyAnalysis/ImportReferenceProvider.cs | 2 +- .../TypeSystem/Mutable/MutableModule.cs | 10 ++++++++++ 10 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs index b8da6d5cba6431..8a75a4916d03b7 100644 --- a/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs +++ b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs @@ -8,7 +8,7 @@ namespace ILCompiler.DependencyAnalysis { public interface INativeFormatTypeReferenceProvider { - internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module = null); + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module); internal Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs index 0d8535488348f3..531d6a9713d893 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs @@ -21,13 +21,13 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se foreach ((string key, TypeDesc type) in entries) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type, null); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, null); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs index 416497cbf46bd2..77d4c629d0b032 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs @@ -20,14 +20,14 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se foreach ((TypeDesc key, TypeDesc type) in entries) { - Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, key); - Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); + Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, key, null); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type, null); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)key.GetHashCode(), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, null); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } 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 0a245de55355b7..3f2e82302b3bdd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -105,13 +105,13 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se foreach ((string key, IEETypeNode valueNode) in GetMarkedEntries(factory)) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type, null); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, null); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs index 657382714ac04c..fa527fdfae76c5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -41,7 +41,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, null); Vertex throwingMethodStubVertex = externalReferences.EncodeReferenceToMethod(writer, ThrowingMethodStub); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); return section.Place(tuple); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs index db0fb70e2a8cbe..f0035a2beb3e08 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -40,7 +40,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, null); Vertex throwingMethodStubVertex = externalReferences.EncodeReferenceToMethod(writer, ThrowingMethodStub); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); return section.Place(tuple); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 72f3ad6edc1bd4..e60f87afae3752 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -1703,7 +1703,7 @@ private sealed class ExternalReferencesTableIndex(ExternalReferencesTableNode ta { public Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => writer.GetUnsignedConstant(table.GetIndex(factory.MethodEntrypoint(method))); - public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module = null) + public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module) => writer.GetUnsignedConstant(table.GetIndex(factory.NecessaryTypeSymbol(type))); } 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 6cc884c0997705..e82bb502c2cbe6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -84,14 +84,14 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in GetMarkedEntries(factory)) { - Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, keyNode.Type); - Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type); + Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, keyNode.Type, null); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type, null); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup, null); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs index 64563b1d553b8e..ed72df9012b8d0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -46,7 +46,7 @@ internal Vertex EncodeReferenceToModule(NativeWriter writer, ModuleDesc module) return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); } - internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module = null) + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type, ModuleDesc module) { Debug.Assert(module is not null); Import typeImport = GetImportToType(type, module); 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 3d85ea819ac4b0..ee304e151b1af5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs @@ -142,10 +142,20 @@ static string GetNameOfAssemblyRefWhichResolvesToType(ModuleDesc module, Metadat if (module is EcmaModule ecmaModule && type.Module is EcmaModule targetTypeModule) { string targetAssemblyName = targetTypeModule.Assembly.GetName().Name; + MetadataReader reader = ecmaModule.MetadataReader; + foreach (AssemblyReferenceHandle assemblyRefHandle in reader.AssemblyReferences) + { + string referencedAssemblyName = reader.GetString(reader.GetAssemblyReference(assemblyRefHandle).Name); + if (string.Equals(referencedAssemblyName, targetAssemblyName, StringComparison.Ordinal)) + { + return targetAssemblyName; + } + } // The assembly name in a custom attribute type argument is valid even when there is no // corresponding AssemblyRef row in the source module metadata. return targetAssemblyName; + } throw new KeyNotFoundException($"Unable to resolve an assembly reference from module '{module}' to type '{type}'."); } From c2404517b72b9b5a6a9a7d223952a2b25a072164 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Jul 2026 17:55:59 -0700 Subject: [PATCH 6/6] Update ImportReferenceProvider.cs Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> --- .../Compiler/DependencyAnalysis/ImportReferenceProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs index ed72df9012b8d0..6e7e2f04feaf85 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -18,11 +18,11 @@ public sealed class ImportReferenceProvider : INativeFormatTypeReferenceProvider private ReadyToRunSymbolNodeFactory _symbolNodeFactory; private ExternalReferenceTokenManager _externalReferenceTokenManager; - public Import GetImportToType(TypeDesc type, ModuleDesc module = null) + public Import GetImportToType(TypeDesc type, ModuleDesc moduleRequiringImport = null) { - if (module is not null) + if (moduleRequiringImport is not null) { - _externalReferenceTokenManager.EnsureDefTokensAreAvailable(type, module, referencesAreForAsyncMethod: false); + _externalReferenceTokenManager.EnsureDefTokensAreAvailable(type, moduleRequiringImport, referencesAreForAsyncMethod: false); } return _symbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type);