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

Nullable Resolution Scopes #466

Merged
merged 6 commits into from
Jul 14, 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
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private uint AddResolutionScope(IResolutionScope? scope, bool allowDuplicates, b
TableIndex.AssemblyRef => AddAssemblyReference(scope as AssemblyReference, allowDuplicates, preserveRid),
TableIndex.TypeRef => AddTypeReference(scope as TypeReference, allowDuplicates, preserveRid),
TableIndex.ModuleRef => AddModuleReference(scope as ModuleReference, allowDuplicates, preserveRid),
TableIndex.Module => 0,
TableIndex.Module => new MetadataToken(TableIndex.Module, 1),
_ => throw new ArgumentOutOfRangeException(nameof(scope))
};

Expand Down
21 changes: 12 additions & 9 deletions src/AsmResolver.DotNet/DefaultMetadataResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,11 @@ public TypeResolution(IAssemblyResolver resolver)

public TypeDefinition? ResolveTypeReference(TypeReference? reference)
{
var scope = reference?.Scope;
if (reference?.Name is null || scope is null || _scopeStack.Contains(scope))
if (reference is null)
return null;

var scope = reference.Scope ?? reference.Module;
if (reference.Name is null || scope is null || _scopeStack.Contains(scope))
return null;
_scopeStack.Push(scope);

Expand Down Expand Up @@ -241,20 +244,20 @@ public TypeResolution(IAssemblyResolver resolver)

private TypeDefinition? FindTypeInModule(ModuleDefinition module, Utf8String? ns, Utf8String name)
{
for (int i = 0; i < module.ExportedTypes.Count; i++)
{
var exportedType = module.ExportedTypes[i];
if (exportedType.IsTypeOfUtf8(ns, name))
return ResolveExportedType(exportedType);
}

for (int i = 0; i < module.TopLevelTypes.Count; i++)
{
var type = module.TopLevelTypes[i];
if (type.IsTypeOfUtf8(ns, name))
return type;
}

for (int i = 0; i < module.ExportedTypes.Count; i++)
{
var exportedType = module.ExportedTypes[i];
if (exportedType.IsTypeOfUtf8(ns, name))
return ResolveExportedType(exportedType);
}

return null;
}

Expand Down
33 changes: 17 additions & 16 deletions src/AsmResolver.DotNet/ReferenceImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,12 @@ public ModuleDefinition TargetModule
get;
}

private static void AssertTypeIsValid(ITypeDefOrRef? type)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
if (type.Scope is null)
throw new ArgumentException("Cannot import types that are not added to a module.");
}

/// <summary>
/// Imports a resolution scope.
/// </summary>
/// <param name="scope">The resolution scope to import.</param>
/// <returns>The imported resolution scope.</returns>
public IResolutionScope ImportScope(IResolutionScope? scope)
public IResolutionScope ImportScope(IResolutionScope scope)
{
if (scope is null)
throw new ArgumentNullException(nameof(scope));
Expand Down Expand Up @@ -181,14 +173,16 @@ public ITypeDefOrRef ImportType(ITypeDefOrRef type)
/// <returns>The imported type.</returns>
protected virtual ITypeDefOrRef ImportType(TypeDefinition type)
{
AssertTypeIsValid(type);

if (type is null)
throw new ArgumentNullException(nameof(type));
if (type.IsImportedInModule(TargetModule))
return type;
if (((ITypeDescriptor) type).Scope is not { } scope)
throw new ArgumentException("Cannot import a type that has not been added to a module.");

return new TypeReference(
TargetModule,
ImportScope(((ITypeDescriptor) type).Scope),
ImportScope(scope),
type.Namespace,
type.Name);
}
Expand All @@ -200,12 +194,18 @@ protected virtual ITypeDefOrRef ImportType(TypeDefinition type)
/// <returns>The imported type.</returns>
protected virtual ITypeDefOrRef ImportType(TypeReference type)
{
AssertTypeIsValid(type);

if (type is null)
throw new ArgumentNullException(nameof(type));
if (type.IsImportedInModule(TargetModule))
return type;

return new TypeReference(TargetModule, ImportScope(type.Scope!), type.Namespace, type.Name);
return new TypeReference(
TargetModule,
type.Scope is not null
? ImportScope(type.Scope)
: null,
type.Namespace,
type.Name);
}

/// <summary>
Expand All @@ -215,7 +215,8 @@ protected virtual ITypeDefOrRef ImportType(TypeReference type)
/// <returns>The imported type.</returns>
protected virtual ITypeDefOrRef ImportType(TypeSpecification type)
{
AssertTypeIsValid(type);
if (type is null)
throw new ArgumentNullException(nameof(type));
if (type.Signature is null)
throw new ArgumentNullException(nameof(type));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public SerializedTypeReference(ModuleReaderContext context, MetadataToken token,
protected override IResolutionScope? GetScope()
{
if (_row.ResolutionScope == 0)
return _context.ParentModule;
return null;

var tablesStream = _context.TablesStream;
var decoder = tablesStream.GetIndexEncoder(CodedIndex.ResolutionScope);
Expand Down
68 changes: 53 additions & 15 deletions test/AsmResolver.DotNet.Tests/MetadataResolverTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.TestCases.NestedClasses;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
using Xunit;

namespace AsmResolver.DotNet.Tests
Expand Down Expand Up @@ -52,6 +53,7 @@ public void ResolveSystemObjectNetCore()
var reference = new TypeReference(module.CorLibTypeFactory.CorLibScope, "System", "Object");
var definition = _coreResolver.ResolveType(reference);

Assert.NotNull(definition);
Assert.True(definition.IsTypeOf(reference.Namespace, reference.Name));
}

Expand All @@ -69,8 +71,8 @@ public void ResolveType()
{
var module = ModuleDefinition.FromFile(typeof(TopLevelClass1).Assembly.Location);

var topLevelClass1 = new TypeReference(new AssemblyReference(module.Assembly),
typeof(TopLevelClass1).Namespace, typeof(TopLevelClass1).Name);
var topLevelClass1 = new TypeReference(new AssemblyReference(module.Assembly!),
typeof(TopLevelClass1).Namespace, nameof(TopLevelClass1));

var definition = _coreResolver.ResolveType(topLevelClass1);
Assert.Equal((ITypeDefOrRef) topLevelClass1, definition, _comparer);
Expand Down Expand Up @@ -104,7 +106,7 @@ public void ResolveTypeReferenceThenChangeDefAndResolveAgain()

ITypeDefOrRef expected = new TypeReference(module.CorLibTypeFactory.CorLibScope, "System", "Object");
var reference = new TypeReference(module.CorLibTypeFactory.CorLibScope, "System", "Object");
var definition = _fwResolver.ResolveType(reference);
var definition = _fwResolver.ResolveType(reference)!;
Assert.Equal(expected, definition, _comparer);
definition.Name = "String";
Assert.NotEqual(expected, _fwResolver.ResolveType(reference), _comparer);
Expand All @@ -115,9 +117,9 @@ public void ResolveNestedType()
{
var module = ModuleDefinition.FromFile(typeof(TopLevelClass1).Assembly.Location);

var topLevelClass1 = new TypeReference(new AssemblyReference(module.Assembly),
typeof(TopLevelClass1).Namespace, typeof(TopLevelClass1).Name);
var nested1 = new TypeReference(topLevelClass1,null, typeof(TopLevelClass1.Nested1).Name);
var topLevelClass1 = new TypeReference(new AssemblyReference(module.Assembly!),
typeof(TopLevelClass1).Namespace, nameof(TopLevelClass1));
var nested1 = new TypeReference(topLevelClass1,null, nameof(TopLevelClass1.Nested1));

var definition = _coreResolver.ResolveType(nested1);

Expand All @@ -129,16 +131,52 @@ public void ResolveNestedNestedType()
{
var module = ModuleDefinition.FromFile(typeof(TopLevelClass1).Assembly.Location);

var topLevelClass1 = new TypeReference(new AssemblyReference(module.Assembly),
typeof(TopLevelClass1).Namespace, typeof(TopLevelClass1).Name);
var nested1 = new TypeReference(topLevelClass1,null, typeof(TopLevelClass1.Nested1).Name);
var nested1nested1 = new TypeReference(nested1,null, typeof(TopLevelClass1.Nested1.Nested1Nested1).Name);
var topLevelClass1 = new TypeReference(new AssemblyReference(module.Assembly!),
typeof(TopLevelClass1).Namespace, nameof(TopLevelClass1));
var nested1 = new TypeReference(topLevelClass1,null, nameof(TopLevelClass1.Nested1));
var nested1nested1 = new TypeReference(nested1,null, nameof(TopLevelClass1.Nested1.Nested1Nested1));

var definition = _fwResolver.ResolveType(nested1nested1);

Assert.Equal((ITypeDefOrRef) nested1nested1, definition, _comparer);
}

[Fact]
public void ResolveTypeWithModuleScope()
{
var module = ModuleDefinition.FromBytes(Properties.Resources.TypeRefModuleScope);
var reference = module.LookupMember<TypeReference>(new MetadataToken(TableIndex.TypeRef, 2));

var definition = reference.Resolve();

Assert.NotNull(definition);
Assert.Same(module, definition.Module);
}

[Fact]
public void ResolveTypeWithNullScopeCurrentModule()
{
var module = ModuleDefinition.FromBytes(Properties.Resources.TypeRefNullScope_CurrentModule);
var reference = module.LookupMember<TypeReference>(new MetadataToken(TableIndex.TypeRef, 2));

var definition = reference.Resolve();

Assert.NotNull(definition);
Assert.Same(module, definition.Module);
}

[Fact]
public void ResolveTypeWithNullScopeExportedType()
{
var module = ModuleDefinition.FromBytes(Properties.Resources.TypeRefNullScope_ExportedType);
var reference = module.LookupMember<TypeReference>(new MetadataToken(TableIndex.TypeRef, 1));

var definition = reference.Resolve();

Assert.NotNull(definition);
Assert.Equal("mscorlib", definition.Module!.Assembly!.Name);
}

[Fact]
public void ResolveConsoleWriteLineMethod()
{
Expand Down Expand Up @@ -187,13 +225,13 @@ public void ResolveExportedMemberReference()
var resolver = (AssemblyResolverBase) module.MetadataResolver.AssemblyResolver;
resolver.AddToCache(assembly1, assembly1);
resolver.AddToCache(assembly2, assembly2);
resolver = (AssemblyResolverBase) assembly1.ManifestModule.MetadataResolver.AssemblyResolver;
resolver = (AssemblyResolverBase) assembly1.ManifestModule!.MetadataResolver.AssemblyResolver;
resolver.AddToCache(assembly1, assembly1);
resolver.AddToCache(assembly2, assembly2);

// Resolve
var instructions = module.ManagedEntryPointMethod.CilMethodBody.Instructions;
Assert.NotNull(((IMethodDescriptor) instructions[0].Operand).Resolve());
var instructions = module.ManagedEntryPointMethod!.CilMethodBody!.Instructions;
Assert.NotNull(((IMethodDescriptor) instructions[0].Operand!).Resolve());
}

[Fact]
Expand All @@ -208,10 +246,10 @@ public void MaliciousExportedTypeLoop()
var resolver = (AssemblyResolverBase) module.MetadataResolver.AssemblyResolver;
resolver.AddToCache(assembly1, assembly1);
resolver.AddToCache(assembly2, assembly2);
resolver = (AssemblyResolverBase) assembly1.ManifestModule.MetadataResolver.AssemblyResolver;
resolver = (AssemblyResolverBase) assembly1.ManifestModule!.MetadataResolver.AssemblyResolver;
resolver.AddToCache(assembly1, assembly1);
resolver.AddToCache(assembly2, assembly2);
resolver = (AssemblyResolverBase) assembly2.ManifestModule.MetadataResolver.AssemblyResolver;
resolver = (AssemblyResolverBase) assembly2.ManifestModule!.MetadataResolver.AssemblyResolver;
resolver.AddToCache(assembly1, assembly1);
resolver.AddToCache(assembly2, assembly2);

Expand Down
21 changes: 21 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.

9 changes: 9 additions & 0 deletions test/AsmResolver.DotNet.Tests/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,13 @@
<data name="ArgListTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ArgListTest.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="TypeRefModuleScope" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\TypeRefModuleScope.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="TypeRefNullScope_CurrentModule" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\TypeRefNullScope_CurrentModule.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="TypeRefNullScope_ExportedType" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\TypeRefNullScope_ExportedType.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.
34 changes: 34 additions & 0 deletions test/AsmResolver.DotNet.Tests/Signatures/TypeSignatureTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.DotNet.TestCases.Generics;
using AsmResolver.DotNet.TestCases.Types;
using AsmResolver.PE.DotNet.Metadata.Tables;
using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
using Xunit;

Expand Down Expand Up @@ -619,5 +620,38 @@ public void IgnorePinnedModifiers()
Assert.True(type1.IsCompatibleWith(type2));
Assert.True(type2.IsCompatibleWith(type1));
}

[Fact]
public void GetModuleOfTypeDefOrRef()
{
var module = ModuleDefinition.FromBytes(Properties.Resources.HelloWorld);
var signature = module.GetOrCreateModuleType().ToTypeSignature();
Assert.Same(module, signature.Module);
}

[Fact]
public void GetModuleOfTypeDefOrRefWithNullScope()
{
var module = ModuleDefinition.FromBytes(Properties.Resources.TypeRefNullScope_CurrentModule);
var signature = module
.LookupMember<TypeReference>(new MetadataToken(TableIndex.TypeRef, 2))
.ToTypeSignature();

Assert.Null(signature.Scope);
Assert.Same(module, signature.Module);
}

[Fact]
public void GetModuleOfSpecificationTypeWithNullScope()
{
var module = ModuleDefinition.FromBytes(Properties.Resources.TypeRefNullScope_CurrentModule);
var signature = module
.LookupMember<TypeReference>(new MetadataToken(TableIndex.TypeRef, 2))
.ToTypeSignature()
.MakeSzArrayType();

Assert.Null(signature.Scope);
Assert.Same(module, signature.Module);
}
}
}
Loading