Closed
Description
Description
Field offsets aren't being set in the IL for types with an offset of zero. This causes the types to fail to load at runtime.
The cause, as far as I can tell, seems to be from lines 641 in the WriteFields function in ModuleBuilderImpl.cs
if (field._offset > 0 && (typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0)
{
AddFieldLayout(handle, field._offset);
}
Reproduction Steps
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Loader;
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly);
ModuleBuilder mob = ab.DefineDynamicModule("MyModule");
TypeBuilder exp = mob.DefineType("ExplicitLayoutType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.ExplicitLayout, typeof(ValueType), 8);
var f1 = exp.DefineField("first", typeof(int), FieldAttributes.Public);
f1.SetOffset(0);
var f2 = exp.DefineField("second", typeof(int), FieldAttributes.Public);
f2.SetOffset(4);
exp.CreateType();
TypeBuilder tb = mob.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
tb.DefineField("structfield", exp, FieldAttributes.Public | FieldAttributes.Static);
MethodBuilder meb = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static,
typeof(int), new Type[] { typeof(int), typeof(int) });
ILGenerator il = meb.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
tb.CreateType();
using var stream = new MemoryStream();
ab.Save(stream);
stream.Seek(0, SeekOrigin.Begin);
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(stream);
MethodInfo method = assembly.GetType("MyType").GetMethod("SumMethod");
Console.WriteLine(method.Invoke(null, new object[] { 5, 10 }));
Expected behavior
Type loads, program prints 15.
Actual behavior
Unhandled exception. System.TypeLoadException: Could not load type 'ExplicitLayoutType' from assembly 'MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because field 'first' was not given an explicit offset.
at System.Reflection.RuntimeAssembly.GetTypeCore(QCallAssembly assembly, String typeName, ReadOnlySpan`1 nestedTypeNames, Int32 nestedTypeNamesLength, ObjectHandleOnStack retType)
at System.Reflection.RuntimeAssembly.GetTypeCore(String typeName, ReadOnlySpan`1 nestedTypeNames, Boolean throwOnError, Boolean ignoreCase)
at System.Reflection.TypeNameResolver.GetType(String escapedTypeName, ReadOnlySpan`1 nestedTypeNames, TypeName parsedName)
at System.Reflection.TypeNameResolver.GetSimpleType(TypeName typeName)
at System.Reflection.TypeNameResolver.Resolve(TypeName typeName)
at System.Reflection.TypeNameResolver.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Assembly topLevelAssembly)
at Program.<Main>$(String[] args) in Program.cs:line 32
Regression?
No response
Known Workarounds
No response
Configuration
Arch Linux x64, dotnet 9.0.100-rc.1.24381.3
Other information
No response