diff --git a/Directory.Build.props b/Directory.Build.props
index da8160f3a..13a85bb60 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,7 +7,7 @@
https://github.com/Washi1337/AsmResolver
git
10
- 5.0.0-beta.1
+ 5.0.0-beta.2
diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs b/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs
index e39ebc5f5..4fdc14587 100644
--- a/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs
+++ b/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs
@@ -25,8 +25,8 @@ public class DynamicMethodDefinition : MethodDefinition
public DynamicMethodDefinition(ModuleDefinition module, object dynamicMethodObj) :
base(new MetadataToken(TableIndex.Method, 0))
{
- dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
- var methodBase = FieldReader.ReadField(dynamicMethodObj, "m_method");
+ object resolver = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
+ var methodBase = FieldReader.ReadField(resolver, "m_method");
if (methodBase is null)
{
throw new ArgumentException(
@@ -37,9 +37,17 @@ public DynamicMethodDefinition(ModuleDefinition module, object dynamicMethodObj)
Name = methodBase.Name;
Attributes = (MethodAttributes)methodBase.Attributes;
Signature = module.DefaultImporter.ImportMethodSignature(ResolveSig(methodBase, module));
- CilMethodBody = CreateDynamicMethodBody(this, dynamicMethodObj);
+ CilMethodBody = CreateDynamicMethodBody(this, resolver);
}
+ ///
+ /// Determines whether dynamic method reading is fully supported in the current host's .NET environment.
+ ///
+ public static bool IsSupported => DynamicTypeSignatureResolver.IsSupported;
+
+ ///
+ public override ModuleDefinition Module { get; }
+
private MethodSignature ResolveSig(MethodBase methodBase, ModuleDefinition module)
{
var importer = module.DefaultImporter;
@@ -58,9 +66,6 @@ private MethodSignature ResolveSig(MethodBase methodBase, ModuleDefinition modul
returnType, parameterTypes);
}
- ///
- public override ModuleDefinition Module { get; }
-
///
/// Creates a CIL method body from a dynamic method.
///
@@ -73,42 +78,58 @@ private static CilMethodBody CreateDynamicMethodBody(MethodDefinition method, ob
throw new ArgumentException("Method body should reference a serialized module.");
var result = new CilMethodBody(method);
- dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
-
- // Attempt to get the code field.
- byte[]? code = FieldReader.ReadField(dynamicMethodObj, "m_code");
-
- // If it is still null, it might still be set using DynamicILInfo::SetCode.
- // Find the code stored in the DynamicILInfo if available.
- if (code is null
- && FieldReader.TryReadField(dynamicMethodObj, "m_method", out var methodBase)
- && methodBase is not null
- && FieldReader.TryReadField(methodBase, "m_DynamicILInfo", out object? dynamicILInfo)
- && dynamicILInfo is not null)
+ object resolver = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
+
+ // We prefer to extract the information from DynamicILInfo if it is there, as it has more accurate info
+ // if the DynamicMethod code is not flushed yet into the resolver (e.g., it hasn't been invoked yet).
+ object? dynamicILInfo = null;
+ if (FieldReader.TryReadField(resolver, "m_method", out var m) && m is not null)
+ FieldReader.TryReadField(m, "m_DynamicILInfo", out dynamicILInfo);
+
+ // Extract all required information to construct the body.
+ byte[]? code;
+ object scope;
+ List tokenList;
+ byte[]? localSig;
+ byte[]? ehHeader;
+ IList? ehInfos;
+
+ if (resolver.GetType().FullName != "System.Reflection.Emit.DynamicILInfo" && dynamicILInfo is not null)
{
code = FieldReader.ReadField(dynamicILInfo, "m_code");
- }
+ scope = FieldReader.ReadField(dynamicILInfo, "m_scope")!;
+ tokenList = FieldReader.ReadField>(scope, "m_tokens")!;
+ localSig = FieldReader.ReadField(dynamicILInfo, "m_localSignature");
+ ehHeader = FieldReader.ReadField(dynamicILInfo, "m_exceptions");
- if (code is null)
- throw new InvalidOperationException("Dynamic method does not have a CIL code stream.");
-
- // Get remaining fields.
- object scope = FieldReader.ReadField(dynamicMethodObj, "m_scope")!;
- var tokenList = FieldReader.ReadField>(scope, "m_tokens")!;
- byte[] localSig = FieldReader.ReadField(dynamicMethodObj, "m_localSignature")!;
- byte[] ehHeader = FieldReader.ReadField(dynamicMethodObj, "m_exceptionHeader")!;
- var ehInfos = FieldReader.ReadField>(dynamicMethodObj, "m_exceptions")!;
+ // DynamicILInfo does not have EH info. Try recover it from the resolver.
+ ehInfos = FieldReader.ReadField>(resolver, "m_exceptions");
+ }
+ else
+ {
+ code = FieldReader.ReadField(resolver, "m_code");
+ scope = FieldReader.ReadField(resolver, "m_scope")!;
+ tokenList = FieldReader.ReadField>(scope, "m_tokens")!;
+ localSig = FieldReader.ReadField(resolver, "m_localSignature");
+ ehHeader = FieldReader.ReadField(resolver, "m_exceptionHeader");
+ ehInfos = FieldReader.ReadField>(resolver, "m_exceptions");
+ }
- //Local Variables
- DynamicMethodHelper.ReadLocalVariables(result, method, localSig);
+ // Interpret local variables signatures.
+ if (localSig is not null)
+ DynamicMethodHelper.ReadLocalVariables(result, method, localSig);
// Read raw instructions.
- var reader = new BinaryStreamReader(code);
- var disassembler = new CilDisassembler(reader, new DynamicCilOperandResolver(module, result, tokenList));
- result.Instructions.AddRange(disassembler.ReadInstructions());
+ if (code is not null)
+ {
+ var reader = new BinaryStreamReader(code);
+ var operandResolver = new DynamicCilOperandResolver(module, result, tokenList);
+ var disassembler = new CilDisassembler(reader, operandResolver);
+ result.Instructions.AddRange(disassembler.ReadInstructions());
+ }
- //Exception Handlers
- DynamicMethodHelper.ReadReflectionExceptionHandlers(result, ehInfos, ehHeader, module.DefaultImporter);
+ // Interpret exception handler information or header.
+ DynamicMethodHelper.ReadReflectionExceptionHandlers(result, ehHeader, ehInfos, module.DefaultImporter);
return result;
}
diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs b/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs
index 9deb9ec5e..3563096b6 100644
--- a/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs
+++ b/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs
@@ -6,36 +6,22 @@
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Serialized;
using AsmResolver.DotNet.Signatures;
-using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.IO;
using AsmResolver.PE.DotNet.Cil;
-using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
namespace AsmResolver.DotNet.Dynamic
{
internal static class DynamicMethodHelper
{
- private static readonly MethodInfo GetTypeFromHandleUnsafeMethod;
-
- static DynamicMethodHelper()
- {
- // We need to use reflection for this to stay compatible with .netstandard 2.0.
- GetTypeFromHandleUnsafeMethod = typeof(Type)
- .GetMethod("GetTypeFromHandleUnsafe",
- (BindingFlags) (-1),
- null,
- new[] {typeof(IntPtr)},
- null)!;
- }
-
public static void ReadLocalVariables(CilMethodBody methodBody, MethodDefinition method, byte[] localSig)
{
if (method.Module is not SerializedModuleDefinition module)
throw new ArgumentException("Method body should reference a serialized module.");
var reader = new BinaryStreamReader(localSig);
- if (ReadLocalVariableSignature(new BlobReadContext(module.ReaderContext), ref reader)
- is not { } localsSignature)
+ var context = new BlobReadContext(module.ReaderContext, DynamicTypeSignatureResolver.Instance);
+ if (CallingConventionSignature.FromReader(context, ref reader)
+ is not LocalVariablesSignature localsSignature)
{
throw new ArgumentException("Invalid local variables signature.");
}
@@ -44,64 +30,25 @@ public static void ReadLocalVariables(CilMethodBody methodBody, MethodDefinition
methodBody.LocalVariables.Add(new CilLocalVariable(localsSignature.VariableTypes[i]));
}
- private static TypeSignature ReadTypeSignature(in BlobReadContext context, ref BinaryStreamReader reader)
- {
- return (ElementType) reader.PeekByte() == ElementType.Internal
- ? ReadInternalTypeSignature(context, ref reader)
- : TypeSignature.FromReader(in context, ref reader);
- }
-
- private static TypeSignature ReadInternalTypeSignature(in BlobReadContext context, ref BinaryStreamReader reader)
- {
- var address = IntPtr.Size switch
- {
- 4 => new IntPtr(reader.ReadInt32()),
- _ => new IntPtr(reader.ReadInt64())
- };
-
- // Let the runtime translate the address to a type and import it.
- var clrType = (Type?) GetTypeFromHandleUnsafeMethod.Invoke(null, new object[] { address });
-
- var type = clrType is not null
- ? new ReferenceImporter(context.ReaderContext.ParentModule).ImportType(clrType)
- : InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec);
-
- return new TypeDefOrRefSignature(type);
- }
-
- private static LocalVariablesSignature ReadLocalVariableSignature(
- in BlobReadContext context,
- ref BinaryStreamReader reader)
+ public static void ReadReflectionExceptionHandlers(CilMethodBody methodBody,
+ byte[]? ehHeader, IList? ehInfos, ReferenceImporter importer)
{
- var result = new LocalVariablesSignature();
- result.Attributes = (CallingConventionAttributes) reader.ReadByte();
-
- if (!reader.TryReadCompressedUInt32(out uint count))
+ if (ehHeader is {Length: > 4})
{
- context.ReaderContext.BadImage("Invalid number of local variables in local variable signature.");
- return result;
+ InterpretEHSection(methodBody, importer, ehHeader);
}
-
- for (int i = 0; i < count; i++)
- result.VariableTypes.Add(ReadTypeSignature(context, ref reader));
-
- return result;
- }
-
- public static void ReadReflectionExceptionHandlers(CilMethodBody methodBody,
- IList? ehInfos, byte[] ehHeader, ReferenceImporter importer)
- {
- //Sample needed!
- if (ehHeader is { Length: > 4 })
- throw new NotImplementedException("Exception handlers from ehHeader not supported yet.");
-
- if (ehInfos is { Count: > 0 })
+ else if (ehInfos is { Count: > 0 })
{
foreach (var ehInfo in ehInfos)
InterpretEHInfo(methodBody, importer, ehInfo);
}
}
+ private static void InterpretEHSection(CilMethodBody methodBody, ReferenceImporter importer, byte[] ehHeader)
+ {
+ throw new NotImplementedException("Raw exception data is not supported yet.");
+ }
+
private static void InterpretEHInfo(CilMethodBody methodBody, ReferenceImporter importer, object ehInfo)
{
for (int i = 0; i < FieldReader.ReadField(ehInfo, "m_currentCatch"); i++)
diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs b/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs
new file mode 100644
index 000000000..969ef3f0a
--- /dev/null
+++ b/src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using AsmResolver.DotNet.Signatures;
+using AsmResolver.DotNet.Signatures.Types;
+
+namespace AsmResolver.DotNet.Dynamic
+{
+ ///
+ /// Provides an implementation for the that resolves metadata tokens from
+ /// the underlying module's tables stream, and is able to transform addresses referencing method tables in the
+ /// current process to type signatures.
+ ///
+ public class DynamicTypeSignatureResolver : PhysicalTypeSignatureResolver
+ {
+ private static readonly MethodInfo? GetTypeFromHandleUnsafeMethod;
+
+ static DynamicTypeSignatureResolver()
+ {
+ // We need to use reflection for this to stay compatible with .netstandard 2.0.
+ GetTypeFromHandleUnsafeMethod = typeof(Type)
+ .GetMethod("GetTypeFromHandleUnsafe",
+ (BindingFlags) (-1),
+ null,
+ new[] {typeof(IntPtr)},
+ null);
+ }
+
+ ///
+ /// Gets the singleton instance of the class.
+ ///
+ public new static DynamicTypeSignatureResolver Instance
+ {
+ get;
+ } = new();
+
+ ///
+ /// Gets a value indicating whether dynamic resolution of method tables is supported.
+ ///
+ [MemberNotNullWhen(true, nameof(GetTypeFromHandleUnsafeMethod))]
+ public static bool IsSupported => GetTypeFromHandleUnsafeMethod is not null;
+
+ ///
+ public override TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address)
+ {
+ if (!IsSupported)
+ throw new PlatformNotSupportedException("The current platform does not support the translation of raw type handles to System.Type instances.");
+
+ // Let the runtime translate the address to a type and import it.
+ var clrType = (Type?) GetTypeFromHandleUnsafeMethod.Invoke(null, new object[] { address });
+
+ var type = clrType is not null
+ ? new ReferenceImporter(context.ReaderContext.ParentModule).ImportType(clrType)
+ : InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec);
+
+ return new TypeDefOrRefSignature(type);
+ }
+ }
+}
diff --git a/src/AsmResolver.DotNet/Code/Cil/OriginalMetadataTokenProvider.cs b/src/AsmResolver.DotNet/Code/Cil/OriginalMetadataTokenProvider.cs
index db8440077..6d4ac27b1 100644
--- a/src/AsmResolver.DotNet/Code/Cil/OriginalMetadataTokenProvider.cs
+++ b/src/AsmResolver.DotNet/Code/Cil/OriginalMetadataTokenProvider.cs
@@ -26,7 +26,7 @@ public OriginalMetadataTokenProvider(ModuleDefinition? module)
private MetadataToken GetToken(IMetadataMember member)
{
- if (_module is not null && member is IModuleProvider provider && provider.Module == _module)
+ if (_module is not null && member is IModuleProvider provider && provider.Module != _module)
throw new MemberNotImportedException(member);
return member.MetadataToken;
diff --git a/src/AsmResolver.DotNet/ReferenceImporter.cs b/src/AsmResolver.DotNet/ReferenceImporter.cs
index eb27ec74e..1f0821c10 100644
--- a/src/AsmResolver.DotNet/ReferenceImporter.cs
+++ b/src/AsmResolver.DotNet/ReferenceImporter.cs
@@ -506,14 +506,26 @@ public virtual IMethodDescriptor ImportMethod(MethodBase method)
if (method is null)
throw new ArgumentNullException(nameof(method));
+ // We need to create a method spec if this method is a generic instantiation.
if (method.IsGenericMethod && !method.IsGenericMethodDefinition)
return ImportGenericMethod((MethodInfo) method);
+ // Test whether we have a declaring type.
+ var originalDeclaringType = method.DeclaringType;
+ if (originalDeclaringType is null)
+ throw new ArgumentException("Method's declaring type is null.");
+
+ // System.Reflection substitutes all type parameters in the MethodInfo instance with their concrete
+ // arguments if the declaring type is a generic instantiation. However in metadata, we need the original
+ // parameter references. Thus, resolve the original method info first if required.
+ if (originalDeclaringType.IsGenericType && !originalDeclaringType.IsGenericTypeDefinition)
+ method = method.Module.ResolveMethod(method.MetadataToken)!;
+
var returnType = method is MethodInfo info
? ImportTypeSignature(info.ReturnType)
: TargetModule.CorLibTypeFactory.Void;
- var parameters = method.DeclaringType is { IsConstructedGenericType: true }
+ var parameters = originalDeclaringType is { IsConstructedGenericType: true }
? method.Module.ResolveMethod(method.MetadataToken)!.GetParameters()
: method.GetParameters();
@@ -523,12 +535,16 @@ public virtual IMethodDescriptor ImportMethod(MethodBase method)
var result = new MethodSignature(
method.IsStatic ? 0 : CallingConventionAttributes.HasThis,
- returnType, parameterTypes);
+ returnType,
+ parameterTypes);
- if (method.DeclaringType == null)
- throw new ArgumentException("Method's declaring type is null.");
+ if (method.IsGenericMethodDefinition)
+ {
+ result.IsGeneric = true;
+ result.GenericParameterCount = method.GetGenericArguments().Length;
+ }
- return new MemberReference(ImportType(method.DeclaringType), method.Name, result);
+ return new MemberReference(ImportType(originalDeclaringType), method.Name, result);
}
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls AsmResolver.DotNet.ReferenceImporter.ImportMethod(System.Reflection.MethodBase)")]
diff --git a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs b/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs
index 4e1757ed2..1af93d92d 100644
--- a/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs
+++ b/src/AsmResolver.DotNet/Signatures/BlobReadContext.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using AsmResolver.DotNet.Serialized;
+using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.PE.DotNet.Metadata.Tables;
namespace AsmResolver.DotNet.Signatures
@@ -16,7 +17,7 @@ public readonly struct BlobReadContext
///
/// The original read context.
public BlobReadContext(ModuleReaderContext readerContext)
- : this(readerContext, Enumerable.Empty())
+ : this(readerContext, PhysicalTypeSignatureResolver.Instance, Enumerable.Empty())
{
}
@@ -24,10 +25,22 @@ public BlobReadContext(ModuleReaderContext readerContext)
/// Creates a new instance of the structure.
///
/// The original read context.
+ /// The object responsible for resolving raw type metadata tokens and addresses.
+ public BlobReadContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver)
+ : this(readerContext, resolver, Enumerable.Empty())
+ {
+ }
+
+ ///
+ /// Creates a new instance of the structure.
+ ///
+ /// The original read context.
+ /// The object responsible for resolving raw type metadata tokens and addresses.
/// A collection of traversed metadata tokens.
- public BlobReadContext(ModuleReaderContext readerContext, IEnumerable traversedTokens)
+ public BlobReadContext(ModuleReaderContext readerContext, ITypeSignatureResolver resolver, IEnumerable traversedTokens)
{
ReaderContext = readerContext;
+ TypeSignatureResolver = resolver;
TraversedTokens = new HashSet(traversedTokens);
}
@@ -39,6 +52,14 @@ public ModuleReaderContext ReaderContext
get;
}
+ ///
+ /// Gets the object responsible for resolving raw type metadata tokens and addresses.
+ ///
+ public ITypeSignatureResolver TypeSignatureResolver
+ {
+ get;
+ }
+
///
/// Gets a collection of metadata tokens that were traversed during the parsing of metadata.
///
@@ -47,4 +68,4 @@ public ISet TraversedTokens
get;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs
new file mode 100644
index 000000000..9b7faec67
--- /dev/null
+++ b/src/AsmResolver.DotNet/Signatures/Types/ITypeSignatureResolver.cs
@@ -0,0 +1,27 @@
+using AsmResolver.PE.DotNet.Metadata.Tables;
+
+namespace AsmResolver.DotNet.Signatures.Types
+{
+ ///
+ /// Provides members for resolving raw metadata tokens and addresses to types.
+ ///
+ public interface ITypeSignatureResolver
+ {
+ ///
+ /// Resolves a metadata token to a type.
+ ///
+ /// The blob reading context the type is situated in.
+ /// The token to resolve.
+ /// The type.
+ ITypeDefOrRef ResolveToken(in BlobReadContext context, MetadataToken token);
+
+ ///
+ /// Resolves an address to a runtime method table to a type signature.
+ ///
+ /// The blob reading context the type is situated in.
+ /// The address to resolve.
+ /// The type.
+ TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address);
+ }
+
+}
diff --git a/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs b/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs
new file mode 100644
index 000000000..b85f6b6aa
--- /dev/null
+++ b/src/AsmResolver.DotNet/Signatures/Types/PhysicalTypeSignatureResolver.cs
@@ -0,0 +1,57 @@
+using System;
+using AsmResolver.PE.DotNet.Metadata.Tables;
+
+namespace AsmResolver.DotNet.Signatures.Types
+{
+ ///
+ /// Provides an implementation for the that resolves metadata tokens from
+ /// the underlying module's tables stream.
+ ///
+ public class PhysicalTypeSignatureResolver : ITypeSignatureResolver
+ {
+ ///
+ /// Gets the singleton instance of the class.
+ ///
+ public static PhysicalTypeSignatureResolver Instance
+ {
+ get;
+ } = new();
+
+ ///
+ public virtual ITypeDefOrRef ResolveToken(in BlobReadContext context, MetadataToken token)
+ {
+ switch (token.Table)
+ {
+ // Check for infinite recursion.
+ case TableIndex.TypeSpec when !context.TraversedTokens.Add(token):
+ context.ReaderContext.BadImage("Infinite metadata loop was detected.");
+ return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.MetadataLoop);
+
+ // Any other type is legal.
+ case TableIndex.TypeSpec:
+ case TableIndex.TypeDef:
+ case TableIndex.TypeRef:
+ if (context.ReaderContext.ParentModule.TryLookupMember(token, out var member)
+ && member is ITypeDefOrRef typeDefOrRef)
+ {
+ return typeDefOrRef;
+ }
+
+ context.ReaderContext.BadImage($"Metadata token in type signature refers to a non-existing TypeDefOrRef member {token}.");
+ return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.InvalidCodedIndex);
+
+ default:
+ context.ReaderContext.BadImage("Invalid coded index.");
+ return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.InvalidCodedIndex);
+ }
+ }
+
+ ///
+ public virtual TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address)
+ {
+ throw new NotSupportedException(
+ "Encountered an COR_ELEMENT_TYPE_INTERNAL type signature which is not supported by this "
+ + " type signature reader. Use the AsmResolver.DotNet.Dynamic extension package instead.");
+ }
+ }
+}
diff --git a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs
index 7eb81bfbd..78ce8e2b9 100644
--- a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs
+++ b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs
@@ -144,9 +144,11 @@ public static TypeSignature FromReader(in BlobReadContext context, ref BinaryStr
return new BoxedTypeSignature(FromReader(context, ref reader));
case ElementType.Internal:
- throw new NotSupportedException(
- "Encountered an COR_ELEMENT_TYPE_INTERNAL type signature which is not supported by this "
- + " type signature reader. Use the AsmResolver.DotNet.Dynamic extension package instead.");
+ return context.TypeSignatureResolver.ResolveRuntimeType(context, IntPtr.Size switch
+ {
+ 4 => new IntPtr(reader.ReadInt32()),
+ _ => new IntPtr(reader.ReadInt64())
+ });
default:
throw new ArgumentOutOfRangeException($"Invalid or unsupported element type {elementType}.");
@@ -177,38 +179,7 @@ protected static ITypeDefOrRef ReadTypeDefOrRef(in BlobReadContext context, ref
return InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec);
}
- ITypeDefOrRef result;
- switch (token.Table)
- {
- // Check for infinite recursion.
- case TableIndex.TypeSpec when !context.TraversedTokens.Add(token):
- context.ReaderContext.BadImage("Infinite metadata loop was detected.");
- result = InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.MetadataLoop);
- break;
-
- // Any other type is legal.
- case TableIndex.TypeSpec:
- case TableIndex.TypeDef:
- case TableIndex.TypeRef:
- if (module.TryLookupMember(token, out var member) && member is ITypeDefOrRef typeDefOrRef)
- {
- result = typeDefOrRef;
- }
- else
- {
- context.ReaderContext.BadImage($"Metadata token in type signature refers to a non-existing TypeDefOrRef member {token}.");
- result = InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.InvalidCodedIndex);
- }
-
- break;
-
- default:
- context.ReaderContext.BadImage("Invalid coded index.");
- result = InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.InvalidCodedIndex);
- break;
- }
-
- return result;
+ return context.TypeSignatureResolver.ResolveToken(context, token);
}
///
diff --git a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj
index 456d762fe..cbb134178 100644
--- a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj
+++ b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj
@@ -20,6 +20,7 @@
+
diff --git a/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs b/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs
index c65bba058..229a27713 100644
--- a/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs
+++ b/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs
@@ -4,12 +4,13 @@
using System.Reflection;
using System.Reflection.Emit;
using AsmResolver.DotNet.Code.Cil;
-using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Types;
+using AsmResolver.DotNet.TestCases.Generics;
using AsmResolver.DotNet.TestCases.Methods;
+using AsmResolver.IO;
using AsmResolver.PE.DotNet.Cil;
+using AsmResolver.PE.DotNet.Metadata.Tables;
using Xunit;
-using MethodAttributes = AsmResolver.PE.DotNet.Metadata.Tables.Rows.MethodAttributes;
namespace AsmResolver.DotNet.Dynamic.Tests
{
@@ -116,6 +117,117 @@ public void ImportNestedType()
Assert.Equal(nameof(DynamicMethodDefinitionTest), declaringType.DeclaringType.Name);
}
+ [Fact]
+ public void ImportGenericTypeInstantiation()
+ {
+ var method = new DynamicMethod("Test", typeof(void), Type.EmptyTypes);
+ var cil = method.GetILGenerator();
+ cil.Emit(OpCodes.Call, typeof(GenericType)
+ .GetMethod(nameof(GenericType.NonGenericMethodInGenericType))!);
+ cil.Emit(OpCodes.Ret);
+
+ var contextModule = ModuleDefinition.FromFile(typeof(DynamicMethodDefinitionTest).Assembly.Location);
+ var definition = new DynamicMethodDefinition(contextModule, method);
+
+ Assert.NotNull(definition.CilMethodBody);
+ var instruction = definition.CilMethodBody.Instructions[0];
+ Assert.Equal(CilOpCodes.Call, instruction.OpCode);
+ var operand = Assert.IsAssignableFrom(instruction.Operand);
+ var type = Assert.IsAssignableFrom(operand.DeclaringType);
+ var signature = Assert.IsAssignableFrom(type.Signature);
+ Assert.Equal("Int32", signature.TypeArguments[0].Name);
+ Assert.Equal("String", signature.TypeArguments[1].Name);
+ Assert.Equal("Stream", signature.TypeArguments[2].Name);
+ }
+
+ [Fact]
+ public void ImportGenericMethodInstantiation()
+ {
+ var method = new DynamicMethod("Test", typeof(void), Type.EmptyTypes);
+ var cil = method.GetILGenerator();
+ cil.Emit(OpCodes.Call, typeof(NonGenericType)
+ .GetMethod(nameof(NonGenericType.GenericMethodInNonGenericType))!
+ .MakeGenericMethod(typeof(int), typeof(string), typeof(Stream)));
+ cil.Emit(OpCodes.Ret);
+
+ var contextModule = ModuleDefinition.FromFile(typeof(DynamicMethodDefinitionTest).Assembly.Location);
+ var definition = new DynamicMethodDefinition(contextModule, method);
+
+ Assert.NotNull(definition.CilMethodBody);
+ var instruction = definition.CilMethodBody.Instructions[0];
+ Assert.Equal(CilOpCodes.Call, instruction.OpCode);
+ var operand = Assert.IsAssignableFrom(instruction.Operand);
+ Assert.Equal(nameof(NonGenericType.GenericMethodInNonGenericType), operand.Name);
+ Assert.NotNull(operand.Signature);
+ Assert.Equal(3, operand.Method!.Signature!.GenericParameterCount);
+ Assert.Equal("Int32", operand.Signature.TypeArguments[0].Name);
+ Assert.Equal("String", operand.Signature.TypeArguments[1].Name);
+ Assert.Equal("Stream", operand.Signature.TypeArguments[2].Name);
+ }
+
+ [Fact]
+ public void ReadDynamicMethodInitializedByDynamicILInfoWithTokens()
+ {
+ // Create new dynamic method.
+ var method = new DynamicMethod("Test", typeof(void), Type.EmptyTypes);
+ var info = method.GetDynamicILInfo();
+ info.SetLocalSignature(new byte[] { 0x7, 0x0 });
+
+ // Write some IL.
+ using var codeStream = new MemoryStream();
+ var assembler = new CilAssembler(
+ new BinaryStreamWriter(codeStream),
+ new CilOperandBuilder(new OriginalMetadataTokenProvider(null), EmptyErrorListener.Instance));
+ uint token = (uint)info.GetTokenFor(typeof(Console).GetMethod("WriteLine", Type.EmptyTypes).MethodHandle);
+ assembler.WriteInstruction(new CilInstruction(CilOpCodes.Call, new MetadataToken(token)));
+ assembler.WriteInstruction(new CilInstruction(CilOpCodes.Ret));
+
+ // Set code.
+ info.SetCode(codeStream.ToArray(), 1);
+
+ // Pass into DynamicMethodDefinition
+ var contextModule = ModuleDefinition.FromFile(typeof(DynamicMethodDefinitionTest).Assembly.Location);
+ var definition = new DynamicMethodDefinition(contextModule, method);
+
+ // Verify
+ Assert.NotNull(definition.CilMethodBody);
+ var instruction = definition.CilMethodBody.Instructions[0];
+ Assert.Equal(CilOpCodes.Call, instruction.OpCode);
+ var reference = Assert.IsAssignableFrom(instruction.Operand);
+ Assert.Equal("WriteLine", reference.Name);
+ }
+
+ [Fact]
+ public void ReadDynamicMethodInitializedByDynamicILInfoWithLocals()
+ {
+ // Create new dynamic method.
+ var method = new DynamicMethod("Test", typeof(void), Type.EmptyTypes);
+ var info = method.GetDynamicILInfo();
+
+ var helper = SignatureHelper.GetLocalVarSigHelper();
+ helper.AddArgument(typeof(int));
+ helper.AddArgument(typeof(bool));
+ helper.AddArgument(typeof(Stream));
+ helper.AddArgument(typeof(Stream[]));
+ info.SetLocalSignature(helper.GetSignature());
+
+ // Write some IL.
+ info.SetCode(new byte[] {0x2a}, 1);
+
+ // Pass into DynamicMethodDefinition
+ var contextModule = ModuleDefinition.FromFile(typeof(DynamicMethodDefinitionTest).Assembly.Location);
+ var definition = new DynamicMethodDefinition(contextModule, method);
+
+ // Verify
+ Assert.NotNull(definition.CilMethodBody);
+ var locals = definition.CilMethodBody.LocalVariables;
+ Assert.Equal(4, locals.Count);
+ Assert.Equal("Int32", locals[0].VariableType.Name);
+ Assert.Equal("Boolean", locals[1].VariableType.Name);
+ Assert.Equal("Stream", locals[2].VariableType.Name);
+ Assert.Equal("Stream", Assert.IsAssignableFrom(locals[3].VariableType).BaseType.Name);
+ }
+
internal static class NestedClass
{
public static void TestMethod() => Console.WriteLine("TestMethod");
diff --git a/test/AsmResolver.DotNet.Tests/ReferenceImporterTest.cs b/test/AsmResolver.DotNet.Tests/ReferenceImporterTest.cs
index 8112872b0..2bbd52f5b 100644
--- a/test/AsmResolver.DotNet.Tests/ReferenceImporterTest.cs
+++ b/test/AsmResolver.DotNet.Tests/ReferenceImporterTest.cs
@@ -5,6 +5,7 @@
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.DotNet.TestCases.Fields;
+using AsmResolver.DotNet.TestCases.Generics;
using AsmResolver.DotNet.TestCases.NestedClasses;
using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
using Xunit;
@@ -501,5 +502,47 @@ public void ImportInstanceFieldByReflectionShouldConstructValidFieldSignature()
Assert.NotNull(resolved);
Assert.Equal(field, Assert.IsAssignableFrom(resolved), Comparer);
}
+
+ [Fact]
+ public void ImportGenericTypeInstantiationViaReflection()
+ {
+ var type = typeof(GenericType);
+
+ var imported = Assert.IsAssignableFrom(_importer.ImportType(type));
+ var signature = Assert.IsAssignableFrom(imported.Signature);
+ Assert.Equal("Int32", signature.TypeArguments[0].Name);
+ Assert.Equal("String", signature.TypeArguments[1].Name);
+ Assert.Equal("Stream", signature.TypeArguments[2].Name);
+ }
+
+ [Fact]
+ public void ImportGenericMethodInstantiationViaReflection()
+ {
+ var method = typeof(NonGenericType)
+ .GetMethod(nameof(NonGenericType.GenericMethodInNonGenericType))!
+ .MakeGenericMethod(typeof(int), typeof(string), typeof(Stream));
+
+ var imported = Assert.IsAssignableFrom(_importer.ImportMethod(method));
+ Assert.Equal(nameof(NonGenericType.GenericMethodInNonGenericType), imported.Name);
+ Assert.NotNull(imported.Signature);
+ Assert.True(imported.Method!.Signature!.IsGeneric);
+ Assert.Equal(3, imported.Method!.Signature!.GenericParameterCount);
+ Assert.Equal("Int32", imported.Signature.TypeArguments[0].Name);
+ Assert.Equal("String", imported.Signature.TypeArguments[1].Name);
+ Assert.Equal("Stream", imported.Signature.TypeArguments[2].Name);
+ }
+
+ [Fact]
+ public void ImportGenericMethodInstantiationWithReturnTypeViaReflection()
+ {
+ var method = typeof(GenericType)
+ .GetMethod(nameof(GenericType.NonGenericMethodWithReturnType))!;
+
+ var imported = Assert.IsAssignableFrom(_importer.ImportMethod(method));
+ Assert.Equal(nameof(GenericType.NonGenericMethodWithReturnType), imported.Name);
+ Assert.NotNull(imported.Signature);
+ var signature = Assert.IsAssignableFrom(imported.Signature.ReturnType);
+ Assert.Equal(2, signature.Index);
+ }
}
}
diff --git a/test/AsmResolver.PE.Tests/Exports/ExportDirectoryTest.cs b/test/AsmResolver.PE.Tests/Exports/ExportDirectoryTest.cs
index b60f1ed9d..0b63631d2 100644
--- a/test/AsmResolver.PE.Tests/Exports/ExportDirectoryTest.cs
+++ b/test/AsmResolver.PE.Tests/Exports/ExportDirectoryTest.cs
@@ -32,22 +32,24 @@ public void ReadExportNames()
public void ReadExportAddresses()
{
var image = PEImage.FromBytes(Properties.Resources.SimpleDll_Exports);
+ Assert.NotNull(image.Exports);
Assert.Equal(new[]
{
0x000111DBu,
0x00011320u,
- }, image.Exports?.Entries.Select(e => e.Address.Rva));
+ }, image.Exports.Entries.Select(e => e.Address.Rva));
}
[Fact]
public void ReadOrdinals()
{
var image = PEImage.FromBytes(Properties.Resources.SimpleDll_Exports);
+ Assert.NotNull(image.Exports);
Assert.Equal(new[]
{
1u,
2u,
- }, image.Exports?.Entries.Select(e => e.Ordinal));
+ }, image.Exports.Entries.Select(e => e.Ordinal));
}
[Fact]
diff --git a/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/GenericType.cs b/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/GenericType.cs
index 8c0cbb875..bfddea4ec 100644
--- a/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/GenericType.cs
+++ b/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/GenericType.cs
@@ -7,9 +7,14 @@ public class GenericType
public static void NonGenericMethodInGenericType()
{
}
-
+
public static void GenericMethodInGenericType()
{
}
+
+ public static T3 NonGenericMethodWithReturnType()
+ {
+ return default;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/NonGenericType.cs b/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/NonGenericType.cs
index 8465cc90c..9b499bfe4 100644
--- a/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/NonGenericType.cs
+++ b/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Generics/NonGenericType.cs
@@ -7,7 +7,7 @@ public class NonGenericType
public static void NonGenericMethodInNonGenericType()
{
}
-
+
public static void GenericMethodInNonGenericType()
{
}
@@ -16,8 +16,14 @@ public static void GenericMethodWithConstraints()
where T1 : IFoo
where T2 : IFoo, IBar
{
-
+
+ }
+
+ public static T GenericMethodWithReturnType()
+ {
+ return default;
}
+
}
public interface IFoo
@@ -27,4 +33,4 @@ public interface IFoo
public interface IBar
{
}
-}
\ No newline at end of file
+}