Skip to content

Commit

Permalink
Allow open generic in dynamic method (experimental, not a legal opera…
Browse files Browse the repository at this point in the history
…tion, with unpredictable problems)

Fix 0xd4d#551
  • Loading branch information
wwh1004 committed May 8, 2024
1 parent 0b2dc95 commit 4c05087
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 14 deletions.
10 changes: 5 additions & 5 deletions src/DotNet/Emit/DynamicMethodBodyReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,16 +420,16 @@ public MethodDef GetMethod() {
protected override string ReadInlineString(Instruction instr) => ReadToken(reader.ReadUInt32()) as string ?? string.Empty;

/// <inheritdoc/>
protected override ITokenOperand ReadInlineTok(Instruction instr) => ReadToken(reader.ReadUInt32()) as ITokenOperand;
protected override ITokenOperand ReadInlineTok(Instruction instr) => ReadToken(reader.ReadUInt32(), false) as ITokenOperand;

/// <inheritdoc/>
protected override ITypeDefOrRef ReadInlineType(Instruction instr) => ReadToken(reader.ReadUInt32()) as ITypeDefOrRef;

object ReadToken(uint token) {
object ReadToken(uint token, bool? treatAsGenericInst = null) {
uint rid = token & 0x00FFFFFF;
switch (token >> 24) {
case 0x02:
return ImportType(rid);
return ImportType(rid, treatAsGenericInst);

case 0x04:
return ImportField(rid);
Expand Down Expand Up @@ -521,10 +521,10 @@ IField ImportField(uint rid) {
return null;
}

ITypeDefOrRef ImportType(uint rid) {
ITypeDefOrRef ImportType(uint rid, bool? treatAsGenericInst = null) {
var obj = Resolve(rid);
if (obj is RuntimeTypeHandle)
return importer.Import(Type.GetTypeFromHandle((RuntimeTypeHandle)obj));
return importer.Import(Type.GetTypeFromHandle((RuntimeTypeHandle)obj), treatAsGenericInst);

return null;
}
Expand Down
61 changes: 52 additions & 9 deletions src/DotNet/Importer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public struct Importer {
readonly ModuleDef module;
internal readonly GenericParamContext gpContext;
readonly ImportMapper mapper;
readonly Type originalDeclaringType;
RecursionCounter recursionCounter;
ImporterOptions options;

Expand Down Expand Up @@ -158,12 +159,30 @@ public Importer(ModuleDef module, ImporterOptions options, GenericParamContext g
/// <param name="options">Importer options</param>
/// <param name="gpContext">Generic parameter context</param>
/// <param name="mapper">Mapper for renamed entities</param>
public Importer(ModuleDef module, ImporterOptions options, GenericParamContext gpContext, ImportMapper mapper) {
public Importer(ModuleDef module, ImporterOptions options, GenericParamContext gpContext, ImportMapper mapper)
: this(module, options, gpContext, mapper, null) {
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="module">The module that will own all references</param>
/// <param name="options">Importer options</param>
/// <param name="gpContext">Generic parameter context</param>
/// <param name="mapper">Mapper for renamed entities</param>
/// <param name="originalDeclaringType">
/// WARNING:<br/>
/// The declaring type of the method when reading a dynamic method.<br/>Pass a non-null value only
/// when you need to allow open generic, which changes some importing behavior to be compatible
/// with illegal open generic dynamic methods.
/// </param>
public Importer(ModuleDef module, ImporterOptions options, GenericParamContext gpContext, ImportMapper mapper, Type originalDeclaringType) {
this.module = module;
recursionCounter = new RecursionCounter();
this.options = options;
this.gpContext = gpContext;
this.mapper = mapper;
this.originalDeclaringType = originalDeclaringType;
}

/// <summary>
Expand All @@ -173,6 +192,18 @@ public Importer(ModuleDef module, ImporterOptions options, GenericParamContext g
/// <returns>The imported type or <c>null</c> if <paramref name="type"/> is invalid</returns>
public ITypeDefOrRef Import(Type type) => module.UpdateRowId(ImportAsTypeSig(type).ToTypeDefOrRef());

/// <summary>
/// Imports a <see cref="Type"/> as a <see cref="ITypeDefOrRef"/>.
/// </summary>
/// <param name="type">The type</param>
/// <param name="treatAsGenericInst">
/// In the .NET metadata (method sig), the parameter is a generic instance type, but the CLR treats it as
/// if it's just a generic type def. This seems to happen only if the parameter type is exactly the same
/// type as the declaring type, eg. a method similar to: <c>MyType&lt;!0&gt; MyType::SomeMethod()</c>.
/// </param>
/// <returns>The imported type or <c>null</c> if <paramref name="type"/> is invalid</returns>
public ITypeDefOrRef Import(Type type, bool? treatAsGenericInst) => module.UpdateRowId(ImportAsTypeSig(type, treatAsGenericInst).ToTypeDefOrRef());

/// <summary>
/// Imports a <see cref="Type"/> as a <see cref="ITypeDefOrRef"/>. See also <see cref="Import(Type)"/>
/// </summary>
Expand All @@ -196,7 +227,19 @@ public ITypeDefOrRef Import(Type type, IList<Type> requiredModifiers, IList<Type
/// </summary>
/// <param name="type">The type</param>
/// <returns>The imported type or <c>null</c> if <paramref name="type"/> is invalid</returns>
public TypeSig ImportAsTypeSig(Type type) => ImportAsTypeSig(type, null, false);
public TypeSig ImportAsTypeSig(Type type) => ImportAsTypeSig(type, false);

/// <summary>
/// Imports a <see cref="Type"/> as a <see cref="TypeSig"/>
/// </summary>
/// <param name="type">The type</param>
/// <param name="treatAsGenericInst">
/// In the .NET metadata (method sig), the parameter is a generic instance type, but the CLR treats it as
/// if it's just a generic type def. This seems to happen only if the parameter type is exactly the same
/// type as the declaring type, eg. a method similar to: <c>MyType&lt;!0&gt; MyType::SomeMethod()</c>.
/// </param>
/// <returns>The imported type or <c>null</c> if <paramref name="type"/> is invalid</returns>
public TypeSig ImportAsTypeSig(Type type, bool? treatAsGenericInst) => ImportAsTypeSig(type, originalDeclaringType, treatAsGenericInst);

TypeSig ImportAsTypeSig(Type type, Type declaringType, bool? treatAsGenericInst = null) {
if (type is null)
Expand Down Expand Up @@ -406,7 +449,7 @@ IResolutionScope CreateScopeReference(Type type) {
/// <param name="optionalModifiers">A list of all optional modifiers or <c>null</c></param>
/// <returns>The imported type or <c>null</c> if <paramref name="type"/> is invalid</returns>
public TypeSig ImportAsTypeSig(Type type, IList<Type> requiredModifiers, IList<Type> optionalModifiers) =>
ImportAsTypeSig(type, requiredModifiers, optionalModifiers, null);
ImportAsTypeSig(type, requiredModifiers, optionalModifiers, originalDeclaringType);

TypeSig ImportAsTypeSig(Type type, IList<Type> requiredModifiers, IList<Type> optionalModifiers, Type declaringType) {
if (type is null)
Expand Down Expand Up @@ -482,13 +525,13 @@ IMethod ImportInternal(MethodBase methodBase, bool forceFixSignature) {
IMethodDefOrRef method;
var origMethod = methodBase.Module.ResolveMethod(methodBase.MetadataToken);
if (methodBase.DeclaringType.GetElementType2() == ElementType.GenericInst)
method = module.UpdateRowId(new MemberRefUser(module, methodBase.Name, CreateMethodSig(origMethod), Import(methodBase.DeclaringType)));
method = module.UpdateRowId(new MemberRefUser(module, methodBase.Name, CreateMethodSig(origMethod), Import(methodBase.DeclaringType, null)));
else
method = ImportInternal(origMethod) as IMethodDefOrRef;

method = TryResolveMethod(method);
if (methodBase.ContainsGenericParameters)
return method; // Declaring type is instantiated but method itself is not
if (methodBase.ContainsGenericParameters && originalDeclaringType is null)
return method; // Declaring type is instantiated but method itself is not, this is a MemberRef. When importing a dynamic method, treat it as open generic.

var gim = CreateGenericInstMethodSig(methodBase);
var methodSpec = module.UpdateRowId(new MethodSpecUser(method, gim));
Expand All @@ -504,7 +547,7 @@ IMethod ImportInternal(MethodBase methodBase, bool forceFixSignature) {
parent = GetModuleParent(methodBase.Module);
}
else
parent = Import(methodBase.DeclaringType);
parent = Import(methodBase.DeclaringType, null);
if (parent is null)
return null;

Expand Down Expand Up @@ -587,7 +630,7 @@ GenericInstMethodSig CreateGenericInstMethodSig(MethodBase mb) {
var genMethodArgs = mb.GetGenericArguments();
var gim = new GenericInstMethodSig(CallingConvention.GenericInst, (uint)genMethodArgs.Length);
foreach (var gma in genMethodArgs)
gim.GenericArguments.Add(ImportAsTypeSig(gma));
gim.GenericArguments.Add(ImportAsTypeSig(gma, null));
return gim;
}

Expand Down Expand Up @@ -642,7 +685,7 @@ public IField Import(FieldInfo fieldInfo, bool forceFixSignature) {
parent = GetModuleParent(fieldInfo.Module);
}
else
parent = Import(fieldInfo.DeclaringType);
parent = Import(fieldInfo.DeclaringType, null);
if (parent is null)
return null;

Expand Down

0 comments on commit 4c05087

Please sign in to comment.