diff --git a/src/AsmResolver.DotNet/ModuleDefinition.cs b/src/AsmResolver.DotNet/ModuleDefinition.cs index 13a5c9669..ffb018860 100644 --- a/src/AsmResolver.DotNet/ModuleDefinition.cs +++ b/src/AsmResolver.DotNet/ModuleDefinition.cs @@ -304,7 +304,7 @@ public ModuleDefinition(Utf8String? name) AssemblyReferences.Add((AssemblyReference)CorLibTypeFactory.CorLibScope); MetadataResolver = new DefaultMetadataResolver(new DotNetFrameworkAssemblyResolver()); - TopLevelTypes.Add(new TypeDefinition(null, "", 0)); + TopLevelTypes.Add(new TypeDefinition(null, TypeDefinition.ModuleTypeName, 0)); } /// @@ -325,7 +325,7 @@ public ModuleDefinition(string? name, AssemblyReference corLib) OriginalTargetRuntime = DetectTargetRuntime(); MetadataResolver = new DefaultMetadataResolver(CreateAssemblyResolver(UncachedFileService.Instance)); - TopLevelTypes.Add(new TypeDefinition(null, "", 0)); + TopLevelTypes.Add(new TypeDefinition(null, TypeDefinition.ModuleTypeName, 0)); } /// @@ -915,7 +915,19 @@ public IEnumerable GetAllTypes() /// Obtains the global scope type of the .NET module. /// /// The module type. - public TypeDefinition? GetModuleType() => TopLevelTypes.Count > 0 ? TopLevelTypes[0] : null; + public TypeDefinition? GetModuleType() + { + if (TopLevelTypes.Count == 0) + return null; + + var firstType = TopLevelTypes[0]; + + // Only .NET Framework allows the module type to be renamed to something else. + if (!OriginalTargetRuntime.IsNetFramework && !firstType.IsTypeOfUtf8(null, TypeDefinition.ModuleTypeName)) + return null; + + return firstType; + } /// /// Obtains or creates the global scope type of the .NET module. @@ -923,13 +935,15 @@ public IEnumerable GetAllTypes() /// The module type. public TypeDefinition GetOrCreateModuleType() { - if (TopLevelTypes.Count == 0 || TopLevelTypes[0].Name != "") + var moduleType = GetModuleType(); + + if (moduleType is null) { - var moduleType = new TypeDefinition(null, "", 0); + moduleType = new TypeDefinition(null, TypeDefinition.ModuleTypeName, 0); TopLevelTypes.Insert(0, moduleType); } - return TopLevelTypes[0]; + return moduleType; } /// diff --git a/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs b/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs index 9b7866ab4..c766f2427 100644 --- a/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs +++ b/src/AsmResolver.DotNet/Serialized/SerializedModuleDefinition.cs @@ -336,12 +336,11 @@ private CorLibTypeFactory CreateCorLibTypeFactory() // TODO: perhaps check public key tokens. IResolutionScope? mostRecentCorLib = null; - var mostRecentVersion = new Version(); foreach (var reference in AssemblyReferences) { if (reference.Name is not null && KnownCorLibs.KnownCorLibNames.Contains(reference.Name)) { - if (mostRecentVersion < reference.Version) + if (mostRecentCorLib is null || reference.Version > mostRecentCorLib.GetAssembly()!.Version) mostRecentCorLib = reference; } } diff --git a/src/AsmResolver.DotNet/TypeDefinition.cs b/src/AsmResolver.DotNet/TypeDefinition.cs index ef51d751e..21821ad1c 100644 --- a/src/AsmResolver.DotNet/TypeDefinition.cs +++ b/src/AsmResolver.DotNet/TypeDefinition.cs @@ -25,6 +25,8 @@ public class TypeDefinition : IOwnedCollectionElement, IOwnedCollectionElement { + internal static readonly Utf8String ModuleTypeName = ""; + private readonly LazyVariable _namespace; private readonly LazyVariable _name; private readonly LazyVariable _baseType; @@ -492,10 +494,11 @@ public bool IsDelegate } /// - /// true if this is the global (aka. <Module>) type, otherwise false. + /// true if this is the global (i.e., <Module>) type, otherwise false. /// /// - /// If the global (aka. <Module>) type was not added or does not exist yet in the will return false. + /// If the global (i.e., <Module>) type was not added or does not exist yet in the , + /// this will return false. /// public bool IsModuleType { diff --git a/test/AsmResolver.DotNet.Tests/ModuleDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/ModuleDefinitionTest.cs index 7e2e1ba10..e527ce48c 100644 --- a/test/AsmResolver.DotNet.Tests/ModuleDefinitionTest.cs +++ b/test/AsmResolver.DotNet.Tests/ModuleDefinitionTest.cs @@ -412,5 +412,63 @@ public void RewriteSystemPrivateXml() using var stream = new MemoryStream(); module.Write(stream); } + + [Fact] + public void GetModuleTypeNetFramework() + { + var module = ModuleDefinition.FromBytes(Properties.Resources.ModuleCctorNetFramework); + + // Module type should exist. + var type = module.GetModuleType(); + Assert.NotNull(type); + Assert.Equal("CustomModuleType", type.Name); + + // Creating module type should give us the existing type. + Assert.Same(type, module.GetOrCreateModuleType()); + } + + [Fact] + public void GetModuleTypeNet6() + { + var module = ModuleDefinition.FromBytes(Properties.Resources.ModuleCctorNet6); + + // Module type should exist. + var type = module.GetModuleType(); + Assert.NotNull(type); + Assert.Equal("", type.Name); + + // Creating module type should give us the existing type. + Assert.Same(type, module.GetOrCreateModuleType()); + } + + [Fact] + public void GetModuleTypeAbsentNet6() + { + var module = ModuleDefinition.FromBytes(Properties.Resources.ModuleCctorAbsentNet6); + + // Module type should not exist. + var type = module.GetModuleType(); + Assert.Null(type); + + // Creating should add it to the module. + type = module.GetOrCreateModuleType(); + Assert.NotNull(type); + Assert.Same(type, module.GetModuleType()); + } + + [Fact] + public void GetModuleTypeLookalikeNet6() + { + var module = ModuleDefinition.FromBytes(Properties.Resources.ModuleCctorLookalikeNet6); + + // Module type should not exist. + var type = module.GetModuleType(); + Assert.Null(type); + + // Creating should add it to the module. + type = module.GetOrCreateModuleType(); + Assert.NotNull(type); + Assert.Same(type, module.GetModuleType()); + } } } diff --git a/test/AsmResolver.DotNet.Tests/Properties/Resources.Designer.cs b/test/AsmResolver.DotNet.Tests/Properties/Resources.Designer.cs index fffb016d9..427f830ea 100644 --- a/test/AsmResolver.DotNet.Tests/Properties/Resources.Designer.cs +++ b/test/AsmResolver.DotNet.Tests/Properties/Resources.Designer.cs @@ -331,5 +331,33 @@ public static byte[] MyLibrary_X64 { return ((byte[])(obj)); } } + + public static byte[] ModuleCctorAbsentNet6 { + get { + object obj = ResourceManager.GetObject("ModuleCctorAbsentNet6", resourceCulture); + return ((byte[])(obj)); + } + } + + public static byte[] ModuleCctorLookalikeNet6 { + get { + object obj = ResourceManager.GetObject("ModuleCctorLookalikeNet6", resourceCulture); + return ((byte[])(obj)); + } + } + + public static byte[] ModuleCctorNet6 { + get { + object obj = ResourceManager.GetObject("ModuleCctorNet6", resourceCulture); + return ((byte[])(obj)); + } + } + + public static byte[] ModuleCctorNetFramework { + get { + object obj = ResourceManager.GetObject("ModuleCctorNetFramework", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/test/AsmResolver.DotNet.Tests/Properties/Resources.resx b/test/AsmResolver.DotNet.Tests/Properties/Resources.resx index 37af630d8..f13ac4af0 100644 --- a/test/AsmResolver.DotNet.Tests/Properties/Resources.resx +++ b/test/AsmResolver.DotNet.Tests/Properties/Resources.resx @@ -141,4 +141,16 @@ ..\Resources\MyLibrary.x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\ModuleCctorAbsentNet6.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\ModuleCctorLookalikeNet6.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\ModuleCctorNet6.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\ModuleCctorNetFramework.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + diff --git a/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorAbsentNet6.dll b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorAbsentNet6.dll new file mode 100644 index 000000000..7dbed5038 Binary files /dev/null and b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorAbsentNet6.dll differ diff --git a/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorLookalikeNet6.dll b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorLookalikeNet6.dll new file mode 100644 index 000000000..559b6cf32 Binary files /dev/null and b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorLookalikeNet6.dll differ diff --git a/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorNet6.dll b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorNet6.dll new file mode 100644 index 000000000..1caa361a2 Binary files /dev/null and b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorNet6.dll differ diff --git a/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorNetFramework.exe b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorNetFramework.exe new file mode 100644 index 000000000..9f67d260d Binary files /dev/null and b/test/AsmResolver.DotNet.Tests/Resources/ModuleCctorNetFramework.exe differ