镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Default/Static interface method desugar remapping not working in .NET 7 #7663

Closed

Description

@radimitrov

Android application type

Android for .NET (net6.0-android, etc.)

Affected platform version

VS 2022, net7.0-android

Description

App crashes at runtime for debug build with a JNI error related to default/static Java interface method binding. Doesn't matter if the debugger is attached. Might be related to #7125 but reverting to the old binding behaviour with <AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>false</AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods> didn't help. Same for true value.

Steps to Reproduce

Bind any Java library with a default static interface method.

Did you find any workaround?

Release build

Relevant log output

12-16 22:24:43.893 18210 18210 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
12-16 22:24:43.893 18210 18210 F DEBUG   : Abort message: 'java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: can't call static org.eclipse.tm4e.core.registry.IThemeSource org.eclipse.tm4e.core.registry.IThemeSource$-CC.fromString(org.eclipse.tm4e.core.registry.IThemeSource$ContentType, java.lang.String) with class java.lang.Class<org.eclipse.tm4e.core.registry.IThemeSource>'
12-16 22:24:43.893 18210 18210 F DEBUG   :     x0  0000000000000000  x1  00000000000046e8  x2  0000000000000006  x3  0000000000000008
12-16 22:24:43.893 18210 18210 F DEBUG   :     x4  fefeff734dce4667  x5  fefeff734dce4667  x6  fefeff734dce4667  x7  7f7f7f7f7fff7f7f
12-16 22:24:43.893 18210 18210 F DEBUG   :     x8  0000000000000083  x9  dd61bb06df041932  x10 0000000000000000  x11 fffffffc7ffffbdf
12-16 22:24:43.893 18210 18210 F DEBUG   :     x12 0000000000000001  x13 ffffffffffffffff  x14 ffffffffff000000  x15 ffffffffffffffff
12-16 22:24:43.893 18210 18210 F DEBUG   :     x16 00000074528692b0  x17 0000007452788cd0  x18 0000000000000010  x19 00000000000046e8
12-16 22:24:43.893 18210 18210 F DEBUG   :     x20 00000000000046e8  x21 00000073be9f5b00  x22 000000000000000b  x23 00000073cdd27443
12-16 22:24:43.893 18210 18210 F DEBUG   :     x24 00000073cdd272f7  x25 0000000000000001  x26 0000007fd92f07e0  x27 0000000000000043
12-16 22:24:43.893 18210 18210 F DEBUG   :     x28 00000073ce513720  x29 0000007fd92f0690
12-16 22:24:43.893 18210 18210 F DEBUG   :     sp  0000007fd92f0650  lr  000000745277c144  pc  000000745277c16c
12-16 22:24:43.923 18210 18210 F DEBUG   : 
12-16 22:24:43.923 18210 18210 F DEBUG   : backtrace:
12-16 22:24:43.923 18210 18210 F DEBUG   :     #00 pc 000000000002216c  /system/lib64/libc.so (abort+116)
12-16 22:24:43.923 18210 18210 F DEBUG   :     #01 pc 0000000000465684  /system/lib64/libart.so (art::Runtime::Abort(char const*)+1196)
12-16 22:24:43.923 18210 18210 F DEBUG   :     #02 pc 0000000000008ce0  /system/lib64/libbase.so (android::base::LogMessage::~LogMessage()+724)
12-16 22:24:43.923 18210 18210 F DEBUG   :     #03 pc 00000000002e5ec8  /system/lib64/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1640)
12-16 22:24:43.924 18210 18210 F DEBUG   :     #04 pc 00000000002e6038  /system/lib64/libart.so (art::JavaVMExt::JniAbortV(char const*, char const*, std::__va_list)+116)
12-16 22:24:43.924 18210 18210 F DEBUG   :     #05 pc 00000000000fdabc  /system/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::AbortF(char const*, ...)+148)
12-16 22:24:43.924 18210 18210 F DEBUG   :     #06 pc 00000000001017d0  /system/lib64/libart.so (art::(anonymous namespace)::ScopedCheck::CheckMethodAndSig(art::ScopedObjectAccess&, _jobject*, _jclass*, _jmethodID*, art::Primitive::Type, art::InvokeType)+1160)
12-16 22:24:43.924 18210 18210 F DEBUG   :     #07 pc 0000000000101e40  /system/lib64/libart.so (art::(anonymous namespace)::CheckJNI::CallMethodA(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, jvalue*, art::Primitive::Type, art::InvokeType)+748)
12-16 22:24:43.924 18210 18210 F DEBUG   :     #08 pc 000000000004d680  /data/app/com.companyname.AndroidBindingsJNICrashes-OMT4AzuyDOaf_ESYip9Szg==/lib/arm64/libmonodroid.so (java_interop_jnienv_call_static_object_method_a+48)
12-16 22:24:43.924 18210 18210 F DEBUG   :     #09 pc 00000000002af4c4  /data/app/com.companyname.AndroidBindingsJNICrashes-OMT4AzuyDOaf_ESYip9Szg==/lib/arm64/libmonosgen-2.0.so
12-16 22:24:43.924 18210 18210 F DEBUG   :     #10 pc 00000000002ae210  /data/app/com.companyname.AndroidBindingsJNICrashes-OMT4AzuyDOaf_ESYip9Szg==/lib/arm64/libmonosgen-2.0.so
12-16 22:24:43.924 18210 18210 F DEBUG   :     #11 pc 00000000002a48a0  /data/app/com.companyname.AndroidBindingsJNICrashes-OMT4AzuyDOaf_ESYip9Szg==/lib/arm64/libmonosgen-2.0.so
12-16 22:24:43.924 18210 18210 F DEBUG   :     #12 pc 00000000002afdf4  /data/app/com.companyname.AndroidBindingsJNICrashes-OMT4AzuyDOaf_ESYip9Szg==/lib/arm64/libmonosgen-2.0.so
12-16 22:24:43.924 18210 18210 F DEBUG   :     #13 pc 00000000002b035c  /data/app/com.companyname.AndroidBindingsJNICrashes-OMT4AzuyDOaf_ESYip9Szg==/lib/arm64/libmonosgen-2.0.so
12-16 22:24:43.924 18210 18210 F DEBUG   :     #14 pc 000000000000b7f0  <anonymous:000000744e8c1000>
12-16 22:24:44.237  1077  1077 E /system/bin/tombstoned: Tombstone written to: /data/tombstones/tombstone_07

Activity

  1. added
    Area: BindingsIssues in Java Library Binding projects.
    and removed
    needs-triageIssues that need to be assigned.
    on Jan 4, 2023
  2. jpobst commented on Jan 9, 2023

    @jpobst
    Contributor

    Simplified test case
    AndroidApp2.zip

  3. jpobst commented on Jan 9, 2023

    @jpobst
    Contributor

    Default and static methods are not supported by Android before API Level 24:
    #4574

    We thought we had worked around it for .NET 7, but there seems to be a bug preventing it from working like we expected. We will continue to investigate that.

    The workaround is to target API-24+ by setting this in your project file:

    <SupportedOSPlatformVersion>24</SupportedOSPlatformVersion>
  4. changed the title [-]Debug build JNI crash on bindings of static interface methods[/-] [+]Default/Static interface method desugar remapping not working in .NET 7[/+] on Jan 9, 2023
  5. jonpryor commented on Jan 9, 2023

    @jonpryor
    Contributor

    On the investigation front, consider AndroidApp2.zip (earlier comment): it has a static method invocation on an interface:

    var val = IStaticMethodsInterface.Value;

    which results in a crash:

    JNI DETECTED ERROR IN APPLICATION: can't call static int com.xamarin.android.StaticMethodsInterface$-CC.getValue() with class java.lang.Class<com.xamarin.android.StaticMethodsInterface>
        in call to CallStaticIntMethodA
        from void crc641149b9fe658fbe8e.MainActivity.n_onCreate(android.os.Bundle)
    

    However, if I manually do what the binding should be doing -- I still need to verify + review that the binding is doing this -- then it works:

    static void TryInvokeStaticInterfaceMethod()
    {
    	Console.WriteLine ("# jonp: trying to get class `com/xamarin/android/StaticMethodsInterface$-CC`…");
    	var t = new Java.Interop.JniType ("com/xamarin/android/StaticMethodsInterface$-CC");
    	Console.WriteLine ($"# jonp: t={t}");
    
    	Console.WriteLine ("# jonp: trying to get static method `getValue()I`…");
    	var m = t.GetStaticMethod ("getValue", "()I");
    	Console.WriteLine ($"# jonp: m={m}");
    
    	Console.WriteLine ("# jonp: Invoking static method `getValue()I`…");
    	var r = Java.Interop.JniEnvironment.StaticMethods.CallStaticIntMethod (t.PeerReference, m);
    	Console.WriteLine ($"# jonp: r={r}");
    }

    Invoking TryInvokeStaticInterfaceMethod() works as expected, with no crash:

    # jonp: trying to get class `com/xamarin/android/StaticMethodsInterface$-CC`…
    # jonp: t=JniType(Name='com/xamarin/android/StaticMethodsInterface$-CC' PeerReference=0x1cb6/G)
    # jonp: trying to get static method `getValue()I`…
    # jonp: m=JniMethodInfo(ID=0x1c3)
    # jonp: Invoking static method `getValue()I`…
    # jonp: r=3
    

    TODO: figure out why the current binding infrastructure isn't doing what the above manual binding does.

  6. jonpryor commented on Jan 9, 2023

    @jonpryor
    Contributor

    @jonpryor wrote:

    TODO: figure out why the current binding infrastructure isn't doing what the above manual binding does.

    The answer is because we don't pass the jclass corresponding to the type where we found the method; see also:

    What we do is more like:

    var from = new Java.Interop.JniType ("com/xamarin/android/StaticMethodsInterface");
    var to   = new Java.Interop.JniType ("com/xamarin/android/StaticMethodsInterface$-CC");
    var m    = to.GetStaticMethod ("getValue", "()I");
    
    // Note: needs to use `to.PeerReference`, *not* `from.PeerReference`
    var r = Java.Interop.JniEnvironment.StaticMethods.CallStaticIntMethod (from.PeerReference, m);
  7. jonpryor commented on Jan 10, 2023

    @jonpryor
    Contributor

    On the investigation front, consider AndroidApp2.zip (earlier comment): it has a default interface method invocation:

    var foo = new MyDefaultClass ();
    var val2 = (foo as IDefaultMethodsInterface).Bar;

    which results in a crash:

    Java.Lang.AbstractMethodError: abstract method "int com.xamarin.android.DefaultMethodsInterface.getBar()"
       at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualIntMethod(JniObjectReference instance, JniObjectReference type, JniMethodInfo method, JniArgumentValue* args)
       at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualInt32Method(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters)
       at Com.Xamarin.Android.IDefaultMethodsInterface.get_Bar()
       at AndroidApp2.MainActivity.OnCreate(Bundle savedInstanceState)
       at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_(IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState)
       at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V(_JniMarshal_PPL_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0)
      --- End of managed Java.Lang.AbstractMethodError stack trace ---
    java.lang.AbstractMethodError: abstract method "int com.xamarin.android.DefaultMethodsInterface.getBar()"
        at crc641149b9fe658fbe8e.MainActivity.n_onCreate(Native Method)
        at crc641149b9fe658fbe8e.MainActivity.onCreate(MainActivity.java:30)
        …
    

    This is also due to desugaring, which is enabled when $(SupportedOSPlatformVersion) is less than 24. When desugaring is not enabled, default interface method invocation works as expected, and it works by performing a non-virtual method invocation on the method declared in the interface.

    When desugaring is enabled, things fail because nothing is where we expect it; if you use dexdump (in the Android SDK, in e.g. build-tools/31.0.3) on classes.dex:

    % unzip bin/Debug/net7.0-android33.0/com.companyname.AndroidApp2-Signed.apk classes.dex
    % $ANDROID_SDK_PATH/build-tools/31.0.3/dexdump classes.dex
    

    and look for the getBar method, you see two things:

    1. An abstract declaration is on DefaultMethodsInterface:

       Class #6            -
         Class descriptor  : 'Lcom/xamarin/android/DefaultMethodsInterface;'
         …
         Virtual methods   -
           …
           #1              : (in Lcom/xamarin/android/DefaultMethodsInterface;)
             name          : 'getBar'
             type          : '()I'
             access        : 0x0401 (PUBLIC ABSTRACT)
             code          : (none)
      
    2. The non-abstract method containing the default interface method body is in DefaultMethodsInterface$-CC (?!) in a method with a $default$ prefix (?!), and "this" becomes a parameter:

       Class #5            -
         Class descriptor  : 'Lcom/xamarin/android/DefaultMethodsInterface$-CC;'
         Access flags      : 0x1011 (PUBLIC FINAL SYNTHETIC)
         …
         Direct methods    -
           …
           #1              : (in Lcom/xamarin/android/DefaultMethodsInterface$-CC;)
             name          : '$default$getBar'
             type          : '(Lcom/xamarin/android/DefaultMethodsInterface;)I'
             access        : 0x0009 (PUBLIC STATIC)
             code          -
      

    Rephrased, when attempting to non-virtually invoke DefaultMethodsInterface.getBar():

    DefaultMethodsInterface instance = …
    int b = instance.getBar();

    when Desugaring is present, we need to instead invoke a static method and reorder the parameters, a'la:

    DefaultMethodsInterface instance = …
    
    int b = DefaultMethodsInterface$-CC.getBar(instance);

    This actually feels reminiscent of the "ClassRewrites[*].Methods[*] with MakeStatic: true" support in dotnet/java-interop@1f27ab5

  8. jpobst commented on Apr 17, 2024

    @jpobst
    Contributor

    We believe desugar remapping is now fully supported in net8.0-android and beyond.

    dotnet/java-interop#1077

  9. locked and limited conversation to collaborators on May 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Area: BindingsIssues in Java Library Binding projects.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions