Skip to content

Commit

Permalink
Merge pull request #466 from Washi1337/feature/null-resolution-scopes
Browse files Browse the repository at this point in the history
Nullable Resolution Scopes
  • Loading branch information
Washi1337 committed Jul 14, 2023
2 parents f4a83a4 + 21bfdf4 commit a0d9e72
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 46 deletions.
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

0 comments on commit a0d9e72

Please sign in to comment.