Skip to content

Android: having a method reference to a private method causes normal method invocations to fail #18

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
luontola opened this issue May 18, 2014 · 3 comments

Comments

@luontola
Copy link
Owner

See #17 (comment)

It might have to do with the normal method call still using invokespecial even though Retrolambda changes the method's visibility from private to package-private. Oracle JVM doesn't mind it, but maybe Android does.

Reproducing: https://github.com/naixx/PrivateMethodAndroidBug

set ANDROID_HOME=...\adt-bundle-windows-x86_64-20140321\sdk
[in "AVD Manager.exe" create virtual device and start the emulator]
adt-bundle-windows-x86_64-20140321\sdk\tools\monitor.bat

gradlew clean build
adt-bundle-windows-x86_64-20140321\sdk\platform-tools\adb.exe install -r app\build\apk\app-debug-unaligned.apk
[start the TestBug app in the emulator]
@luontola
Copy link
Owner Author

This is enough to reproduce:

package com.example.testbug.app;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;

import rx.Observable;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        methodCall();
    }

    private void problemPrivateMethod() {
    }

    void methodCall() {
        Runnable ref = MainActivity.this::problemPrivateMethod;
        problemPrivateMethod();
    }
}
05-18 17:12:54.986: E/AndroidRuntime(1263): java.lang.NoSuchMethodError: com.example.testbug.app.MainActivity.problemPrivateMethod
05-18 17:12:54.986: E/AndroidRuntime(1263):     at com.example.testbug.app.MainActivity.methodCall(MainActivity.java:22)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at com.example.testbug.app.MainActivity.onCreate(MainActivity.java:14)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.Activity.performCreate(Activity.java:5231)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.ActivityThread.access$800(ActivityThread.java:135)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.os.Handler.dispatchMessage(Handler.java:102)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.os.Looper.loop(Looper.java:136)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at android.app.ActivityThread.main(ActivityThread.java:5017)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at java.lang.reflect.Method.invokeNative(Native Method)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at java.lang.reflect.Method.invoke(Method.java:515)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
05-18 17:12:54.986: E/AndroidRuntime(1263):     at dalvik.system.NativeStart.main(Native Method)

@naixx
Copy link

naixx commented May 19, 2014

Subscribing to the issue

@luontola
Copy link
Owner Author

This fix is included in Retrolambda 1.2.3

Arneball added a commit to Arneball/retrolambda that referenced this issue Aug 11, 2014
public interface MyInterface {
    default String def() {
        return "[" + toString() + ", " + "def]";
    }

    default String join(MyInterface other) {
        return def() + other.def();
    }
}

class C implements MyInterface{}  compiles to:

public class testpackage.MyClass implements testpackage.MyInterface {
  public testpackage.MyClass();
    Code:
       0: aload_0
       1: invokespecial luontola#16                 // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.String join(testpackage.MyInterface);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  luontola#46                 // Method testpackage/MyInterfacehelper.join:(Ltestpackage/MyInterface;Ltestpackage/MyInterface;)Ljava/lang/String;
       5: areturn

  public java.lang.String def();
    Code:
       0: aload_0
       1: invokestatic  luontola#49                 // Method testpackage/MyInterfacehelper.def:(Ltestpackage/MyInterface;)Ljava/lang/String;
       4: areturn
}

Where testpackage.MyInterfacehelper is compiled to
public class testpackage.MyInterfacehelper {
  private testpackage.MyInterfacehelper();
    Code:
       0: aload_0
       1: invokespecial luontola#9                  // Method java/lang/Object."<init>":()V
       4: return

  public static java.lang.String def(testpackage.MyInterface);
    Code:
       0: new           luontola#13                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial luontola#14                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           luontola#16                 // String [
       9: invokevirtual luontola#20                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: invokevirtual luontola#24                 // Method java/lang/Object.toString:()Ljava/lang/String;
      16: invokevirtual luontola#20                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           luontola#26                 // String ,
      21: invokevirtual luontola#20                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: ldc           luontola#28                 // String def]
      26: invokevirtual luontola#20                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      29: invokevirtual luontola#29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      32: areturn

  public static java.lang.String join(testpackage.MyInterface, testpackage.MyInterface);
    Code:
       0: new           luontola#13                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial luontola#14                 // Method java/lang/StringBuilder."<init>":()V
       7: aload_0
       8: invokeinterface luontola#35,  1           // InterfaceMethod testpackage/MyInterface.def:()Ljava/lang/String;
      13: invokevirtual luontola#20                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      16: aload_1
      17: invokeinterface luontola#35,  1           // InterfaceMethod testpackage/MyInterface.def:()Ljava/lang/String;
      22: invokevirtual luontola#20                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      25: invokevirtual luontola#29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      28: areturn
}

==============
interface StaticTest {
    static <T> T staticMethod(T t) {
        return t;
    }
}
compiles to
public class testpackage.StaticTesthelper {
  private testpackage.StaticTesthelper();
    Code:
       0: aload_0
       1: invokespecial luontola#9                  // Method java/lang/Object."<init>":()V
       4: return

  public static <T> T staticMethod$static(T);
    Code:
       0: aload_0
       1: areturn
}

========

Brigde methods are generated properly in example:
public interface BridgeParent<T> {
    T get();
}
public interface StringBridges extends BridgeParent<String> {
    @OverRide
    default String get() {
        return "default method";
    }

    default String concrete() {
        return "concrete";
    }
}
public class testpackage.StringBridgeshelper
  SourceFile: "testpackage/StringBridges.java"
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   luontola#1 = Utf8               testpackage/StringBridgeshelper
   luontola#2 = Class              luontola#1             //  testpackage/StringBridgeshelper
   luontola#3 = Utf8               java/lang/Object
   luontola#4 = Class              luontola#3             //  java/lang/Object
   luontola#5 = Utf8               testpackage/StringBridges.java
   luontola#6 = Utf8               <init>
   luontola#7 = Utf8               ()V
   luontola#8 = NameAndType        luontola#6:luontola#7          //  "<init>":()V
   luontola#9 = Methodref          luontola#4.luontola#8          //  java/lang/Object."<init>":()V
  luontola#10 = Utf8               get
  luontola#11 = Utf8               (Ltestpackage/StringBridges;)Ljava/lang/String;
  luontola#12 = Utf8               default method
  luontola#13 = String             luontola#12            //  default method
  luontola#14 = Utf8               concrete
  luontola#15 = String             luontola#14            //  concrete
  luontola#16 = Utf8               (Ltestpackage/StringBridges;)Ljava/lang/Object;
  luontola#17 = Utf8               testpackage/StringBridges
  luontola#18 = Class              luontola#17            //  testpackage/StringBridges
  luontola#19 = Utf8               ()Ljava/lang/String;
  luontola#20 = NameAndType        luontola#10:luontola#19        //  get:()Ljava/lang/String;
  luontola#21 = InterfaceMethodref luontola#18.luontola#20        //  testpackage/StringBridges.get:()Ljava/lang/String;
  luontola#22 = Utf8               Code
  luontola#23 = Utf8               LineNumberTable
  luontola#24 = Utf8               SourceFile
{
  private testpackage.StringBridgeshelper();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial luontola#9                  // Method java/lang/Object."<init>":()V
         4: return

  public static java.lang.String get(testpackage.StringBridges);
    descriptor: (Ltestpackage/StringBridges;)Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: ldc           luontola#13                 // String default method
         2: areturn
      LineNumberTable:
        line 9: 0

  public static java.lang.String concrete(testpackage.StringBridges);
    descriptor: (Ltestpackage/StringBridges;)Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: ldc           luontola#15                 // String concrete
         2: areturn
      LineNumberTable:
        line 13: 0

  public static java.lang.Object get(testpackage.StringBridges);
    descriptor: (Ltestpackage/StringBridges;)Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokeinterface luontola#21,  1           // InterfaceMethod testpackage/StringBridges.get:()Ljava/lang/String;
         6: areturn
      LineNumberTable:
        line 6: 0
}
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants