-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
I have found a possible issue with the new PersistedAssemblyBuilder introduced in .NET 9 that causes invalid IL to be generated when loading fields of generic types that have a generic type parameter as the field type.
When decompiling the generated IL using ildasm or similar tools, the field reference shows up as having the concrete type instead of the type parameter.
Reproduction Steps
The following code reproduces the problem in .NET 9. It doesn't produce a working executable by itself since I did not set the entry point, so to execute code from the generated assembly, you would need to reference it by some other assembly.
using System;
using System.Reflection;
using System.Reflection.Emit;
PersistedAssemblyBuilder ab = new(new("a"), typeof(object).Assembly);
ModuleBuilder mb = ab.DefineDynamicModule("a");
TypeBuilder tb = mb.DefineType("Program");
MethodBuilder methb = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), []);
ILGenerator il = methb.GetILGenerator();
il.Emit(OpCodes.Newobj, typeof(C<int>).GetConstructor([]));
il.Emit(OpCodes.Ldfld, typeof(C<int>).GetField("F"));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, null, [typeof(int)], []));
il.Emit(OpCodes.Ret);
tb.CreateType();
ab.Save("a.dll");
public class C<T>
{
public C() { }
public T F;
}Expected behavior
The following IL should be emitted:
newobj instance void class [Test]C`1<int32>::.ctor()
ldfld !0 class [Test]C`1<int32>::F
call void [mscorlib]System.Console::WriteLine(int32)
ret(Above is IL generated when running the code example on .NET 4.8.1, which is why it is using mscorlib.)
When the code is executed, the following output should be printed:
0
Actual behavior
The following IL is generated:
newobj instance void class [Test]C`1<int32>::.ctor()
ldfld int32 class [Test]C`1<int32>::F
call void [System.Console]System.Console::WriteLine(int32)
retAs you can see, the second line references the type int32 directly, instead of the type parameter !0. This causes the following exception to be thrown when this code is exectued:
Unhandled exception. System.MissingFieldException: Field not found: 'C`1.F'.
at Program.Main()
Regression?
API was newly introduced in .NET 9. On .NET 4.8.1, when using the regular AssemblyBuilder, it generates a working assembly.
Known Workarounds
No response
Configuration
- .NET 9.0.100
- Windows 11 23H2
- Debug, x64
Other information
I asked a question on the topic on StackOverflow, a user provided an explanation which goes into more depth than I would have been able to.