Skip to content

Commit

Permalink
Merge pull request #396 from Washi1337/issue/module-type-across-runtimes
Browse files Browse the repository at this point in the history
Implement .NET Framework / .NET Core differences for GetModuleType
  • Loading branch information
Washi1337 committed Jan 3, 2023
2 parents 04d5ade + 6362a9c commit 25b13b1
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 10 deletions.
26 changes: 20 additions & 6 deletions src/AsmResolver.DotNet/ModuleDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public ModuleDefinition(Utf8String? name)
AssemblyReferences.Add((AssemblyReference)CorLibTypeFactory.CorLibScope);
MetadataResolver = new DefaultMetadataResolver(new DotNetFrameworkAssemblyResolver());

TopLevelTypes.Add(new TypeDefinition(null, "<Module>", 0));
TopLevelTypes.Add(new TypeDefinition(null, TypeDefinition.ModuleTypeName, 0));
}

/// <summary>
Expand All @@ -325,7 +325,7 @@ public ModuleDefinition(string? name, AssemblyReference corLib)
OriginalTargetRuntime = DetectTargetRuntime();
MetadataResolver = new DefaultMetadataResolver(CreateAssemblyResolver(UncachedFileService.Instance));

TopLevelTypes.Add(new TypeDefinition(null, "<Module>", 0));
TopLevelTypes.Add(new TypeDefinition(null, TypeDefinition.ModuleTypeName, 0));
}

/// <summary>
Expand Down Expand Up @@ -915,21 +915,35 @@ public IEnumerable<TypeDefinition> GetAllTypes()
/// Obtains the global scope type of the .NET module.
/// </summary>
/// <returns>The module type.</returns>
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;
}

/// <summary>
/// Obtains or creates the global scope type of the .NET module.
/// </summary>
/// <returns>The module type.</returns>
public TypeDefinition GetOrCreateModuleType()
{
if (TopLevelTypes.Count == 0 || TopLevelTypes[0].Name != "<Module>")
var moduleType = GetModuleType();

if (moduleType is null)
{
var moduleType = new TypeDefinition(null, "<Module>", 0);
moduleType = new TypeDefinition(null, TypeDefinition.ModuleTypeName, 0);
TopLevelTypes.Insert(0, moduleType);
}

return TopLevelTypes[0];
return moduleType;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/AsmResolver.DotNet/TypeDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class TypeDefinition :
IOwnedCollectionElement<ModuleDefinition>,
IOwnedCollectionElement<TypeDefinition>
{
internal static readonly Utf8String ModuleTypeName = "<Module>";

private readonly LazyVariable<Utf8String?> _namespace;
private readonly LazyVariable<Utf8String?> _name;
private readonly LazyVariable<ITypeDefOrRef?> _baseType;
Expand Down Expand Up @@ -492,10 +494,11 @@ public bool IsDelegate
}

/// <summary>
/// <c>true</c> if this is the global (aka. &lt;Module&gt;) type, otherwise <c>false</c>.
/// <c>true</c> if this is the global (i.e., &lt;Module&gt;) type, otherwise <c>false</c>.
/// </summary>
/// <remarks>
/// If the global (aka. &lt;Module&gt;) type was not added or does not exist yet in the <see cref="Module"/> will return false.
/// If the global (i.e., &lt;Module&gt;) type was not added or does not exist yet in the <see cref="Module"/>,
/// this will return <c>false</c>.
/// </remarks>
public bool IsModuleType
{
Expand Down
58 changes: 58 additions & 0 deletions test/AsmResolver.DotNet.Tests/ModuleDefinitionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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("<Module>", 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());
}
}
}
28 changes: 28 additions & 0 deletions test/AsmResolver.DotNet.Tests/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions test/AsmResolver.DotNet.Tests/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,16 @@
<data name="MyLibrary_X64" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MyLibrary.x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ModuleCctorAbsentNet6" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ModuleCctorAbsentNet6.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ModuleCctorLookalikeNet6" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ModuleCctorLookalikeNet6.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ModuleCctorNet6" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ModuleCctorNet6.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ModuleCctorNetFramework" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ModuleCctorNetFramework.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 25b13b1

Please sign in to comment.