JDK-8356942 : invokeinterface Throws AbstractMethodError Instead of IncompatibleClassChangeErr
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 11
  • Priority: P4
  • Status: New
  • Resolution: Unresolved
  • OS: windows
  • CPU: x86_64
  • Submitted: 2025-05-12
  • Updated: 2025-05-14
Description
ADDITIONAL SYSTEM INFORMATION :
ADDITIONAL SYSTEM INFORMATION :
OS:
	Operating System Name: Windows 11
	Operating System Architecture: amd64
	Operating System Version: 10.0
	
OpenJDK version:
java version "1.8.0_441"
Java(TM) SE Runtime Environment (build 1.8.0_441-b07)
Java HotSpot(TM) 64-Bit Server VM (build 25.441-b07, mixed mode)

java version "11.0.26" 2025-01-21 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.26+7-LTS-187)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.26+7-LTS-187, mixed mode)

A DESCRIPTION OF THE PROBLEM :
Given a test case, we found that the execution results of this test case on  different versions are different, the simplified test case can be found below. In summary, Hotspot(1.8.0_441) threw IncompatibleClassChangeError while the other threw AbstractMethodError. 

REGRESSION : Last worked in version 11

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The following code can be used to generate the test cases(.class file) that reproduces the above process:

```java
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class BytecodeUtil {
    public static void main(String args[]) {
        // create package
        String baseDir = "./your_dir"; // your base dir
        String packageName = "P0";
        File folder = new File(baseDir + "/" + packageName);
        folder.mkdirs();

        String P0_I0_bytecodeInStr = "CAFEBABE00000034000F01000550302F49300700010100106A6176612F6C616E672F4F626A6563740700030100016D01001528294C6A6176612F6C616E672F496E74656765723B0100116A6176612F6C616E672F496E746567657207000703000000000100063C696E69743E010004284929560C000A000B0A0008000C010004436F64650601000200040000000000010001000500060001000E00000016000300010000000ABB0008591209B7000DB0000000000000" ;
        String P0_C1_bytecodeInStr = "CAFEBABE00000034000E01000550302F43310700010100106A6176612F6C616E672F4F626A65637407000301000550302F493007000501000550302F49320700070100063C696E69743E0100032829560C0009000A0A0004000B010004436F64650021000200040002000600080000000100010009000A0001000D0000001100010001000000052AB7000CB1000000000000" ;
        String P0_Helper_bytecodeInStr = "CAFEBABE00000034001901000950302F48656C7065720700010100106A6176612F6C616E672F4F626A6563740700030100063C696E69743E0100032829560C000500060A00040007010005676574433101000928294C50302F43313B01000550302F433107000B0A000C0007010005676574433301000928294C50302F43333B01000550302F43330700100A00110007010005676574433401000928294C50302F43343B01000550302F43340700150A00160007010004436F6465002100020004000000000004000100050006000100180000001100010001000000052AB70008B10000000000090009000A0001001800000016000200010000000ABB000C59B7000D4B2AB0000000000009000E000F0001001800000016000200010000000ABB001159B700124B2AB0000000000009001300140001001800000016000200010000000ABB001659B700174B2AB0000000000000" ;
        String P0_I2_bytecodeInStr = "CAFEBABE00000034000F01000550302F49320700010100106A6176612F6C616E672F4F626A6563740700030100016D01001528294C6A6176612F6C616E672F496E74656765723B0100116A6176612F6C616E672F496E746567657207000703000000020100063C696E69743E010004284929560C000A000B0A0008000C010004436F64650601000200040000000000010001000500060001000E00000016000300010000000ABB0008591209B7000DB0000000000000" ;
        String P0_C3_bytecodeInStr = "CAFEBABE00000034002D01000550302F43330700010100106A6176612F6C616E672F4F626A65637407000301000550302F49300700050100063C696E69743E0100032829560C000700080A000400090100047465737401001528294C6A6176612F6C616E672F496E74656765723B01000950302F48656C70657207000D010005676574433401000928294C50302F43343B0C000F00100A000E00110100016D0C0013000C0B000600140100046D61696E010016285B4C6A6176612F6C616E672F537472696E673B29560C000B000C0A000200180100106A6176612F6C616E672F53797374656D07001A0100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C001C001D09001B001E01000E52657475726E2076616C75653A200800200100136A6176612F696F2F5072696E7453747265616D0700220100057072696E74010015284C6A6176612F6C616E672F537472696E673B29560C002400250A002300260100077072696E746C6E010015284C6A6176612F6C616E672F4F626A6563743B29560C002800290A0023002A010004436F646500210002000400010006000000030001000700080001002C0000001100010001000000052AB7000AB1000000000009000B000C0001002C00000017000100010000000BB800124B2AB900150100B0000000000009001600170001002C0000001E0003000200000012B800194CB2001F591221B600272BB6002BB1000000000000" ;
        String P0_C4_bytecodeInStr = "CAFEBABE00000034000E01000550302F43340700010100106A6176612F6C616E672F4F626A65637407000301000550302F493007000501000550302F49350700070100063C696E69743E0100032829560C0009000A0A0004000B010004436F64650021000200040002000600080000000100010009000A0001000D0000001100010001000000052AB7000CB1000000000000" ;
        String P0_I5_bytecodeInStr = "CAFEBABE00000034000F01000550302F49350700010100106A6176612F6C616E672F4F626A6563740700030100016D01001528294C6A6176612F6C616E672F496E74656765723B0100116A6176612F6C616E672F496E746567657207000703000000050100063C696E69743E010004284929560C000A000B0A0008000C010004436F64650601000200040000000000010001000500060001000E00000016000300010000000ABB0008591209B7000DB0000000000000" ;

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "I0.class")) {
            byte[] P0_I0_bytecode =  hexStringToByteArray(P0_I0_bytecodeInStr);
            fos.write(P0_I0_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "C1.class")) {
            byte[] P0_I0_bytecode =  hexStringToByteArray(P0_C1_bytecodeInStr);
            fos.write(P0_I0_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "Helper.class")) {
            byte[] P0_Helper_bytecode =  hexStringToByteArray(P0_Helper_bytecodeInStr);
            fos.write(P0_Helper_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "I2.class")) {
            byte[] P0_I2_bytecode =  hexStringToByteArray(P0_I2_bytecodeInStr);
            fos.write(P0_I2_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "C3.class")) {
            byte[] P0_C3_bytecode =  hexStringToByteArray(P0_C3_bytecodeInStr);
            fos.write(P0_C3_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "C4.class")) {
            byte[] P0_C4_bytecode =  hexStringToByteArray(P0_C4_bytecodeInStr);
            fos.write(P0_C4_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (FileOutputStream fos = new FileOutputStream(folder.getAbsolutePath() + "/" + "I5.class")) {
            byte[] P0_I5_bytecode =  hexStringToByteArray(P0_I5_bytecodeInStr);
            fos.write(P0_I5_bytecode);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static byte[] hexStringToByteArray(String hexString) {
        int length = hexString.length();
        byte[] byteArray = new byte[length / 2];
        for (int i = 0; i < length; i += 2) {
            int byteValue = Integer.parseInt(hexString.substring(i, i + 2), 16);
            byteArray[i / 2] = (byte) byteValue;
        }
        return byteArray;
    }
}

```

Note: modify the paths according to your needs.

Repoduce:

>>> path_to_jdk/Hotspot_1.8.0_441/bin/java -cp . P0.C3

>>> path_to_jdk/Hotspot_11.0.26/bin/java -cp . P0.C3

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
According to this interesting result, we first decompiled P0.C3 using javap. The results are as follows: 

```java
Classfile /D:/Lab/TestJDoc/JSmith-main/your_dir/P0/C3.class
  Last modified May 12, 2025; size 539 bytes
  MD5 checksum a0ff4ab4a79276d3a778adc7888a35bf
public class P0.C3 implements P0.I0
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Utf8               P0/C3
   #2 = Class              #1             // P0/C3
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               P0/I0
   #6 = Class              #5             // P0/I0
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = NameAndType        #7:#8          // "<init>":()V
  #10 = Methodref          #4.#9          // java/lang/Object."<init>":()V
  #11 = Utf8               test
  #12 = Utf8               ()Ljava/lang/Integer;
  #13 = Utf8               P0/Helper
  #14 = Class              #13            // P0/Helper
  #15 = Utf8               getC4
  #16 = Utf8               ()LP0/C4;
  #17 = NameAndType        #15:#16        // getC4:()LP0/C4;
  #18 = Methodref          #14.#17        // P0/Helper.getC4:()LP0/C4;
  #19 = Utf8               m
  #20 = NameAndType        #19:#12        // m:()Ljava/lang/Integer;
  #21 = InterfaceMethodref #6.#20         // P0/I0.m:()Ljava/lang/Integer;
  #22 = Utf8               main
  #23 = Utf8               ([Ljava/lang/String;)V
  #24 = NameAndType        #11:#12        // test:()Ljava/lang/Integer;
  #25 = Methodref          #2.#24         // P0/C3.test:()Ljava/lang/Integer;
  #26 = Utf8               java/lang/System
  #27 = Class              #26            // java/lang/System
  #28 = Utf8               out
  #29 = Utf8               Ljava/io/PrintStream;
  #30 = NameAndType        #28:#29        // out:Ljava/io/PrintStream;
  #31 = Fieldref           #27.#30        // java/lang/System.out:Ljava/io/PrintStream;
  #32 = Utf8               Return value:
  #33 = String             #32            // Return value:
  #34 = Utf8               java/io/PrintStream
  #35 = Class              #34            // java/io/PrintStream
  #36 = Utf8               print
  #37 = Utf8               (Ljava/lang/String;)V
  #38 = NameAndType        #36:#37        // print:(Ljava/lang/String;)V
  #39 = Methodref          #35.#38        // java/io/PrintStream.print:(Ljava/lang/String;)V
  #40 = Utf8               println
  #41 = Utf8               (Ljava/lang/Object;)V
  #42 = NameAndType        #40:#41        // println:(Ljava/lang/Object;)V
  #43 = Methodref          #35.#42        // java/io/PrintStream.println:(Ljava/lang/Object;)V
  #44 = Utf8               Code
{
  public P0.C3();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: return

  public static java.lang.Integer test();
    descriptor: ()Ljava/lang/Integer;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: invokestatic  #18                 // Method P0/Helper.getC4:()LP0/C4;
         3: astore_0
         4: aload_0
         5: invokeinterface #21,  1           // InterfaceMethod P0/I0.m:()Ljava/lang/Integer;
        10: areturn

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: invokestatic  #25                 // Method test:()Ljava/lang/Integer;
         3: astore_1
         4: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         7: dup
         8: ldc           #33                 // String Return value:
        10: invokevirtual #39                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
        13: aload_1
        14: invokevirtual #43                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        17: return
}

```

We observed that the call to `var0.m()` in `P0\C3.test()` uses `invokeinterface`. As a result, we reviewed the relevant sections on `invokeinterface` in both JVM SE8 and SE11.

In SE11, the [Method Selection](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-5.html#jvms-5.4.6) rule states, "Otherwise, the maximally-specific superinterface methods of C are determined ([§5.4.3.3](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-5.html#jvms-5.4.3.3)). If exactly one matches the method's name and descriptor and is not abstract, then it is the selected method." Therefore, the selected method should be `null` because two superinterface methods were found (`P0\I0.m()` and `P0\I5.m()`). According to the [SE11 `invokeinterface` specification](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokeinterface), "Otherwise, if no method is selected, and there are multiple maximally-specific superinterface methods of C that match the resolved method's name and descriptor and are not abstract, `invokeinterface` throws an `IncompatibleClassChangeError`." Therefore, the expected behavior in Hotspot (11.0.26) should be an `IncompatibleClassChangeError` rather than an `AbstractMethodError`.
ACTUAL -
The output is as follows:

Hotspot(1.8.0_441):
```
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: P0/I0.m P0/I5.m
at P0.C4.m(Unknown Source)
at P0.C3.test(Unknown Source)
at P0.C3.main(Unknown Source)
```

Hotspot(11.0.26):
```
Exception in thread "main" java.lang.AbstractMethodError: Receiver class P0.C4 does not define or inherit an implementation of the resolved method 'java.lang.Integer m()' of interface P0.I0.
at P0.C3.test(Unknown Source)
at P0.C3.main(Unknown Source)
```

---------- BEGIN SOURCE ----------
After obtaining the test cases, you can derive the following test cases through decompilation:

```java
package P0;

import java.io.PrintStream;

public class C3 implements I0 {
    public C3() {
    }

    public static Integer test() {
        C4 var0 = Helper.getC4();
        return var0.m();
    }

    public static void main(String[] var0) {
        Integer var1 = test();
        PrintStream var10000 = System.out;
        var10000.print("Return value: ");
        var10000.println(var1);
    }
}
```

```java
package P0;

public class C4 implements I0, I5 {
    public C4() {
    }
}
```

```java
package P0;

public interface I0 {
    default Integer m() {
        return new Integer(0);
    }
}
```

```java
package P0;

public interface I5 {
    default Integer m() {
        return new Integer(5);
    }
}
```
---------- END SOURCE ----------