Skip to content

Instantly share code, notes, and snippets.

@lrytz
Last active April 20, 2016 12:55
Show Gist options
  • Save lrytz/6f1451031b09381eb872acd031b00b0b to your computer and use it in GitHub Desktop.
Save lrytz/6f1451031b09381eb872acd031b00b0b to your computer and use it in GitHub Desktop.
javac produces bytecode that causes NoSuchMethodError
// class version 52.0 (52)
// access flags 0x21
public class A {
// compiled from: A.java
// access flags 0x9
public static INNERCLASS A$K A K
// access flags 0x609
public static abstract INNERCLASS A$I2 A I2
// access flags 0x609
public static abstract INNERCLASS A$I1 A I1
// access flags 0x1
public <init>()V
L0
LINENUMBER 1 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static f(LA$I2;)V
L0
LINENUMBER 12 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 0
INVOKEINTERFACE A$I2.clone ()Ljava/lang/Object;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
RETURN
MAXSTACK = 2
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 15 L0
NEW A$K
DUP
INVOKESPECIAL A$K.<init> ()V
INVOKESTATIC A.f (LA$I2;)V
L1
LINENUMBER 16 L1
RETURN
MAXSTACK = 2
MAXLOCALS = 1
}
public class A {
public interface I1 {
default Object clone() { return "hi"; }
}
public interface I2 extends I1 { }
public static class K implements I2 {
public Object clone() { return "Ka"; }
}
public static void f(I2 i) { System.out.println(i.clone()); }
public static void main(String[] args) {
f(new K());
}
}
$ javac -version
javac 1.8.0_77
$ javac A.java
$ java A
Exception in thread "main" java.lang.NoSuchMethodError: A$I2.clone()Ljava/lang/Object;
at A.f(A.java:12)
at A.main(A.java:15)
@lrytz
Copy link
Author

lrytz commented Apr 19, 2016

INVOKEINTERFACE A$I2.clone ()Ljava/lang/Object; causes the problem. I think that interface method resolution (https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.4) is not implemented according to the spec in the VM:

  1. If C is not an interface, interface method resolution throws an IncompatibleClassChangeError.

A$I2 is an interface, so continue

  1. Otherwise, if C declares a method with the name and descriptor specified by the interface method reference, method lookup succeeds.

A$I2 does not define a method named clone, so continue

  1. Otherwise, if the class Object declares a method with the name and descriptor specified by the interface method reference, which has its ACC_PUBLIC flag set and does not have its ACC_STATIC flag set, method lookup succeeds.

Object does not define a public method named clone, so continue

  1. Otherwise, if the maximally-specific superinterface methods (§5.4.3.3) of C for the name and descriptor specified by the method reference include exactly one method that does not have its ACC_ABSTRACT flag set, then this method is chosen and method lookup succeeds.

Here the JVM should find A$I1.clone, but it seems it doesn't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment