Fix generation of type forwarders (#67570)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Fri, 8 Apr 2022 05:25:34 +0000 (14:25 +0900)
committerGitHub <noreply@github.com>
Fri, 8 Apr 2022 05:25:34 +0000 (22:25 -0700)
These need to be interned to account for nested forwarders. We were erroneously generating multiple containing forwarders whenever there's a nested one.

src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.TypeForwarders.cs

index 104a222..d0e09c2 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Collections.Generic;
 
 using Internal.Metadata.NativeFormat.Writer;
 
@@ -18,6 +19,10 @@ namespace ILCompiler.Metadata
 {
     partial class Transform<TPolicy>
     {
+        private EntityMap<ForwarderKey, TypeForwarder> _forwarders = new EntityMap<ForwarderKey, TypeForwarder>(EqualityComparer<ForwarderKey>.Default);
+
+        private Action<ForwarderKey, TypeForwarder> _initForwarder;
+
         private void HandleTypeForwarders(Cts.Ecma.EcmaModule module)
         {
             foreach (var exportedTypeHandle in module.MetadataReader.ExportedTypes)
@@ -30,7 +35,7 @@ namespace ILCompiler.Metadata
                 Ecma.ExportedType exportedType = module.MetadataReader.GetExportedType(exportedTypeHandle);
                 if (exportedType.IsForwarder || exportedType.Implementation.Kind == Ecma.HandleKind.ExportedType)
                 {
-                    HandleTypeForwarder(module, exportedType);
+                    HandleTypeForwarder(module, exportedTypeHandle);
                 }
                 else
                 {
@@ -39,11 +44,18 @@ namespace ILCompiler.Metadata
             }
         }
 
-        private TypeForwarder HandleTypeForwarder(Cts.Ecma.EcmaModule module, Ecma.ExportedType exportedType)
+        private TypeForwarder HandleTypeForwarder(Cts.Ecma.EcmaModule module, Ecma.ExportedTypeHandle handle)
+        {
+            return _forwarders.GetOrCreate(new ForwarderKey(module, handle), _initForwarder ?? (_initForwarder = InitializeTypeForwarder));
+        }
+
+        private void InitializeTypeForwarder(ForwarderKey key, TypeForwarder record)
         {
+            Cts.Ecma.EcmaModule module = key.Module;
             Ecma.MetadataReader reader = module.MetadataReader;
-            string name = reader.GetString(exportedType.Name);
-            TypeForwarder result;
+            Ecma.ExportedType exportedType = reader.GetExportedType(key.ExportedType);
+            
+            record.Name = HandleString(reader.GetString(exportedType.Name));
 
             switch (exportedType.Implementation.Kind)
             {
@@ -67,35 +79,36 @@ namespace ILCompiler.Metadata
                         else
                             refName.SetPublicKeyToken(reader.GetBlobBytes(assemblyRef.PublicKeyOrToken));
 
-                        result = new TypeForwarder
-                        {
-                            Name = HandleString(name),
-                            Scope = HandleScopeReference(refName),
-                        };
+                        record.Scope = HandleScopeReference(refName);
 
-                        namespaceDefinition.TypeForwarders.Add(result);
+                        namespaceDefinition.TypeForwarders.Add(record);
                     }
                     break;
 
                 case Ecma.HandleKind.ExportedType:
                     {
-                        TypeForwarder scope = HandleTypeForwarder(module, reader.GetExportedType((Ecma.ExportedTypeHandle)exportedType.Implementation));
+                        TypeForwarder scope = HandleTypeForwarder(module, (Ecma.ExportedTypeHandle)exportedType.Implementation);
 
-                        result = new TypeForwarder
-                        {
-                            Name = HandleString(name),
-                            Scope = scope.Scope,
-                        };
+                        record.Scope = scope.Scope;
 
-                        scope.NestedTypes.Add(result);
+                        scope.NestedTypes.Add(record);
                     }
                     break;
 
                 default:
                     throw new BadImageFormatException();
             }
+        }
+
+        private readonly struct ForwarderKey : IEquatable<ForwarderKey>
+        {
+            public readonly Cts.Ecma.EcmaModule Module;
+            public readonly Ecma.ExportedTypeHandle ExportedType;
+            public ForwarderKey(Cts.Ecma.EcmaModule module, Ecma.ExportedTypeHandle exportedType)
+                => (Module, ExportedType) = (module, exportedType);
 
-            return result;
+            public bool Equals(ForwarderKey other) => Module == other.Module && ExportedType == other.ExportedType;
+            public override int GetHashCode() => HashCode.Combine(Module.GetHashCode(), ExportedType.GetHashCode());
         }
     }
 }