-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Closed
Labels
area-System.Reflection.Emitin-prThere is an active PR which will close this issue when it is mergedThere is an active PR which will close this issue when it is merged
Milestone
Description
While experimenting with the Calli opcode, it appears JIT compilation is more restrictive using a physical assembly vs. a DynamicMethod compilation, and there is not a good diagnostic message to explain why.
The sample below for DynamicMethod works, while the same IL using PersistedAssemblyBuilder fails:
GenerateDynamicMethod Result:2ca1c70b-eefc-4287-96a1-653bd93dff77
GenerateMethodInPersistedAssembly Result:Unhandled exception. System.InvalidProgramException: Common Language Runtime detected an invalid program.
at MyType.GetGuid(MyClass, IntPtr)
The sample attempts to pass an object instance and a function pointer to a generated method, which then invokes the object's method via the function pointer via calli.
The IL of the generated method from the persisted assembly:
.method public static valuetype [System.Private.CoreLib]System.Guid
GetGuid(class [ConsoleApp351]ConsoleApp351.MyClass A_0,
native int A_1) cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: calli valuetype [System.Private.CoreLib]System.Guid()
IL_0007: ret
} // end of method MyType::GetGuid
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Loader;
namespace ConsoleApp
{
class Program
{
static unsafe void Main()
{
IntPtr fn = typeof(MyClass).GetProperty("MYGUID")!.GetGetMethod()!.MethodHandle.GetFunctionPointer();
MyClass obj = new MyClass();
// using "object" instead of "MyClass" should also work for the param type, but just use "MyClass" for now.
// With dynamic method
{
Console.Write($"GenerateDynamicMethod Result:");
Func<MyClass, IntPtr, Guid> callMe = GenerateDynamicMethod();
Guid result = callMe(obj, fn);
Console.WriteLine(result);
}
// With persisted assembly
{
Console.Write($"GenerateMethodInPersistedAssembly Result:");
Func<MyClass, IntPtr, Guid> callMe = GenerateMethodInPersistedAssembly();
Guid result = callMe(obj, fn);
Console.WriteLine(result);
}
}
static unsafe Func<MyClass, IntPtr, Guid> GenerateDynamicMethod()
{
DynamicMethod dynamicMethod = new DynamicMethod(
"GetGuid",
returnType: typeof(Guid),
parameterTypes: new Type[] { typeof(MyClass), typeof(IntPtr) },
typeof(Program).Module,
skipVisibility: false);
ILGenerator il = dynamicMethod.GetILGenerator();
EmitCall(il);
return (Func<MyClass, IntPtr, Guid>)dynamicMethod.CreateDelegate(typeof(Func<MyClass, IntPtr, Guid>));
}
static unsafe Func<MyClass, IntPtr, Guid> GenerateMethodInPersistedAssembly()
{
AssemblyName assemblyName = new ("MyAssembly");
PersistedAssemblyBuilder persistedAssemblyBuilder = new (assemblyName, typeof(object).Assembly);
ModuleBuilder moduleBuilder = persistedAssemblyBuilder.DefineDynamicModule("MyModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
"GetGuid",
MethodAttributes.Public | MethodAttributes.Static,
returnType: typeof(Guid),
parameterTypes: new Type[] { typeof(MyClass), typeof(IntPtr)});
ILGenerator il = methodBuilder.GetILGenerator();
EmitCall(il);
// Create the type
typeBuilder.CreateType();
// Save the assembly to a file
string filePath = "MyAssembly.dll";
using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
persistedAssemblyBuilder.Save(fileStream);
fileStream.Close();
// Load the assembly from the file
Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(filePath));
// Get the method
return (Func<MyClass, IntPtr, Guid>)
assembly.GetType("MyType")!.
GetMethod("GetGuid")!.
CreateDelegate(typeof(Func<MyClass, IntPtr, Guid>));
}
static void EmitCall(ILGenerator il)
{
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Ldarg_1); // fn
il.EmitCalli(OpCodes.Calli, CallingConventions.HasThis, typeof(Guid), parameterTypes: null, null);
il.Emit(OpCodes.Ret);
}
}
public class MyClass
{
private Guid _guid = new Guid("{2CA1C70B-EEFC-4287-96A1-653BD93DFF77}");
public Guid MYGUID
{
get => _guid;
set => _guid = value;
}
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area-System.Reflection.Emitin-prThere is an active PR which will close this issue when it is mergedThere is an active PR which will close this issue when it is merged