Skip to content

BadImageFormatException when invoking static abstract method through reflection #79331

Open
@wallymathieu

Description

@wallymathieu

Description

Write generic code to work around that you can't get a static reference to a static method. Since you can cache the reflected instance or use roslyn generator some these limitations have workarounds. However, what we have found is that trying to invoke the static abstract interface method by fetching the method using reflection and then invoking it causes BadImageFormatException to be thrown.

Reproduction Steps

From dotnet/fsharp#14391 (comment)

namespace Foo
{
    class C
    {
        public interface IFoo<T> where T : IFoo<T>
        {
            static abstract int A();
            abstract int B();
        }

        public struct Foo : IFoo<Foo>
        {
            public static int A() { return 2; }
            public int B() { return 3; }
        }

        public static int Main(string[] _)
        {
            var foo = new Foo();
            var x = typeof(Foo).GetInterface("IFoo`1").GetMethod("B").Invoke(foo, null);
            System.Console.WriteLine(x);
            var y = typeof(Foo).GetInterface("IFoo`1").GetMethod("A").Invoke(null, null);
            System.Console.WriteLine(y);
            /* Throws
            System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
             ---> System.BadImageFormatException: Bad IL format.
               at Foo.C.IFoo`1.A()
               at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
               at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
               --- End of inner exception stack trace ---
               at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
               at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
               at Foo.C.Main(String[] _)
               at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
               at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
             */
            return 0;
        }
    }
}

Expected behavior

The expected behaviour would be to match the behaviour of regular instance methods on interfaces (you can invoke them through reflection).

Actual behavior

Instead of being able to call the method we get a BadImageFormatException saying that it is Bad IL format:

            System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
             ---> System.BadImageFormatException: Bad IL format.
               at Foo.C.IFoo`1.A()
               at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
               at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
               --- End of inner exception stack trace ---
               at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
               at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
               at Foo.C.Main(String[] _)
               at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
               at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)

Regression?

This is a new feature in .net.

Known Workarounds

The workaround is to not use the feature (i.e. use a dummy instance and instance interface methods).

Configuration

Operating system (Windows Appveyor Visual Studio 2022 image, Mac OS X, GitHub Ubuntu latest (2022-11) )
.NET Runtime kind (.NET 7.0.100)

Other information

Originally tracked as issue on F# but then found out that it is not F# specific.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions