Description
Description
Sub-issue of #101298.
There is an error in this code, but the error message that gets raised in response is not only unhelpful, it's wrong.
Minimal reproduction steps
var sorter = typeof(ISortable).GetMethod("Sort")!;
sorter.Invoke(null, null);
public interface ISortable
{
static abstract void Sort();
}
Original reproduction steps
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
namespace Repro
{
public interface ISortable<T>
{
static abstract IEnumerable<T> Sort(IEnumerable<T> items);
}
internal class Program
{
static void Main()
{
try
{
TypeBuilder typeBuilder = BuildType();
var realType = typeBuilder.CreateType();
var arr = Array.CreateInstance(realType, 10);
var caster = typeof(Enumerable).GetMethod("Cast")!.MakeGenericMethod(realType);
var typedArr = caster.Invoke(null, [arr]);
var sorter = typeof(ISortable<>).MakeGenericType(realType).GetMethod("Sort")!;
//var sorter = realType.GetMethod("Sort")!;
var sorted = sorter.Invoke(null, [typedArr])!;
var result = ((IEnumerable)sorted).Cast<object>().ToArray();
} catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private static TypeBuilder BuildType()
{
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new("MyAssembly"), AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyAssembly");
var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public, typeof(ValueType));
var intfType = typeof(ISortable<>).MakeGenericType(typeBuilder);
typeBuilder.AddInterfaceImplementation(intfType);
var field = typeBuilder.DefineField("field", typeof(int), FieldAttributes.Public);
var extractor = typeBuilder.DefineMethod("GetField", MethodAttributes.Private | MethodAttributes.Static, typeof(int), [typeBuilder]);
var il = extractor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Ret);
var enumType = typeof(IEnumerable<>).MakeGenericType(typeBuilder);
var impl = typeBuilder.DefineMethod("Sort", MethodAttributes.Public | MethodAttributes.Static, enumType, [enumType]);
il = impl.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldftn, extractor);
il.Emit(OpCodes.Newobj, TypeBuilder.GetConstructor(typeof(Func<,>).MakeGenericType(typeBuilder, typeof(int)), typeof(Func<,>).GetConstructor([typeof(object), typeof(nint)])!));
var orderFunc = typeof(Enumerable).GetMethods().Single(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
il.Emit(OpCodes.Call, orderFunc.MakeGenericMethod(typeBuilder, typeof(int))); // call Enumerable.OrderBy(enumerable, extractor)
il.Emit(OpCodes.Castclass, enumType);
il.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(impl, TypeBuilder.GetMethod(intfType, typeof(ISortable<>).GetMethod("Sort")!));
return typeBuilder;
}
}
}
Expected behavior
This will error out on the call to sorter.Invoke
. A useful error message giving some clue as to the nature of the problem that would meaningfully aid in debugging it is expected.
Actual behavior
The error message provided is:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (0x8007000B)
at Repro.ISortable`1.Sort(IEnumerable`1 items)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
--- End of inner exception stack trace ---
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Repro.Program.Main() in C:\Users\mason\source\repos\Repro2\Repro2\Program.cs:line 27
This message is incorrect. This is not "a program with an incorrect format" at all; the problem is that the code is attempting to invoke a method on an interface, rather than on the type that implements the interface. It's an incorrect invocation of perfectly valid and correct code, as can be seen if the commented-out line is used instead of the incorrect one.
This is particularly problematic because it is misleading; it suggests to the developer that the place to look for the problem is in the refemit code, when the actual error is in the invocation.
Regression?
No response
Known Workarounds
No response
Configuration
.NET Core 8, Windows 10, x64.
Other information
No response