Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement .NET Framework / .NET Core differences for GetModuleType #396

Merged
merged 5 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.