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

Allow open generic in dynamic method (experimental, not a legal operation, with unpredictable problems) #556

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
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
Loading