JASM: Incorrect CP entries for interface and non-interface methods

Vladimir Parfinenko vladimir.parfinenko at gmail.com
Mon Mar 30 04:30:57 UTC 2020


Hello all,

There are some problems with generation of CP entries for
invokedynamic of non-interface metafactory method and for
invokespecial of interface method. I'm not sure but they seem to be
somehow connected. Also it might be related to CODETOOLS-7902333.

Minimal example:
------- Interf.jasm -------
@+java/lang/FunctionalInterface { }
public interface  Interf
version 52:0
{

    public Method callerDynamic:"(LInterf;)LInterf;"
        stack 2 locals 2
        {
            aload_0;
            aload_1;
            invokedynamic InvokeDynamic
REF_invokeStatic:java/lang/invoke/LambdaMetafactory.metafactory:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;":bar:"(LInterf;LInterf;)LInterf;"
MethodType "()Ljava/lang/Object;", MethodHandle
REF_invokeSpecial:Interf.foo:"(LInterf;)Ljava/lang/Object;",
MethodType "()Ljava/lang/Object;";
            areturn;
        }

    public Method callerSpecial:"(LInterf;)LInterf;"
        stack 2 locals 2
        {
            aload_0;
            aload_1;
            invokespecial Interf.foo:"(LInterf;)Ljava/lang/Object;";
            areturn;
        }

    private synthetic Method foo:"(LInterf;)Ljava/lang/Object;"
        stack 1 locals 2
        {
            aconst_null;
            areturn;
        }

    public abstract Method bar:"()Ljava/lang/Object;";

}
--------------------------

------- Main.java ------
public class Main implements Interf {
   public static void main(String[] var0) {
      Main x = new Main();
      try {
          System.out.println("Dynamic");
          x.callerDynamic(x);
          System.out.println("OK");
      } catch (Throwable e) {
          e.printStackTrace(System.out);
      }
      try {
          System.out.println("Special");
          x.callerSpecial(x);
          System.out.println("OK");
      } catch (Throwable e) {
          e.printStackTrace(System.out);
      }
   }

   public Object bar() {
      return null;
   }
}
--------------------------

The resulting class Interf looks like the following after compilation
with the latest asmtools (changeset 41):
---------- Interf.javap --------------
Classfile /tmp/asmtools-bugs/Interf.class
  Last modified Mar 30, 2020; size 768 bytes
  MD5 checksum 95fb63d2b77ed98e4fd1a478666a7965
  Compiled from "Interf.jasm"
public interface Interf
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
   #1 = Methodref          #4.#11         //
Interf.foo:(LInterf;)Ljava/lang/Object;
   #2 = InvokeDynamic      #0:#8          // #0:bar:(LInterf;LInterf;)LInterf;
   #3 = Utf8               (LInterf;)Ljava/lang/Object;
   #4 = Class              #18            // Interf
   #5 = Utf8               (LInterf;)LInterf;
   #6 = Utf8               bar
   #7 = Utf8               Interf.jasm
   #8 = NameAndType        #6:#14         // bar:(LInterf;LInterf;)LInterf;
   #9 = MethodHandle       #6:#28         // invokestatic
java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #10 = Utf8               SourceFile
  #11 = NameAndType        #13:#3         // foo:(LInterf;)Ljava/lang/Object;
  #12 = MethodHandle       #7:#23         // invokespecial
Interf.foo:(LInterf;)Ljava/lang/Object;
  #13 = Utf8               foo
  #14 = Utf8               (LInterf;LInterf;)LInterf;
  #15 = NameAndType        #16:#29        //
metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #16 = Utf8               metafactory
  #17 = Utf8               callerDynamic
  #18 = Utf8               Interf
  #19 = Class              #30            // java/lang/Object
  #20 = Class              #31            // java/lang/invoke/LambdaMetafactory
  #21 = Utf8               Ljava/lang/FunctionalInterface;
  #22 = Utf8               RuntimeVisibleAnnotations
  #23 = InterfaceMethodref #4.#11         //
Interf.foo:(LInterf;)Ljava/lang/Object;
  #24 = MethodType         #26            //  ()Ljava/lang/Object;
  #25 = Utf8               BootstrapMethods
  #26 = Utf8               ()Ljava/lang/Object;
  #27 = Utf8               callerSpecial
  #28 = InterfaceMethodref #20.#15        //
java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #29 = Utf8
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #30 = Utf8               java/lang/Object
  #31 = Utf8               java/lang/invoke/LambdaMetafactory
  #32 = Utf8               Code
{
  public Interf callerDynamic(Interf);
    descriptor: (LInterf;)LInterf;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokedynamic #2,  0              // InvokeDynamic
#0:bar:(LInterf;LInterf;)LInterf;
         7: areturn

  public Interf callerSpecial(Interf);
    descriptor: (LInterf;)LInterf;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokespecial #1                  // Method
foo:(LInterf;)Ljava/lang/Object;
         5: areturn

  public abstract java.lang.Object bar();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "Interf.jasm"
RuntimeVisibleAnnotations:
  0: #21()
BootstrapMethods:
  0: #9 invokestatic
java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #24 ()Ljava/lang/Object;
      #12 invokespecial Interf.foo:(LInterf;)Ljava/lang/Object;
      #24 ()Ljava/lang/Object;
---------------------------------

There are two problems:
1) 0-th boostrap method references LambdaMetafactory.metafactory() as
InterfaceMethodRef #9 but the declaring class is not an interface.
2) callerSpecial references method foo via MethodRef #1 which is not
correct because the declaring is an interface . However 0-th bootstrap
method references MethodHandle #12 which references the same method
via InterfaceMethodRef #23.

Running this sample on JDK-13 gives the output:
------ stdout -------
Dynamic
java.lang.IncompatibleClassChangeError: Inconsistent constant pool
data in classfile for class java/lang/invoke/LambdaMetafactory. Method
'java.lang.invoke.CallSite
metafactory(java.lang.invoke.MethodHandles$Lookup, java.lang.String,
java.lang.invoke.MethodType, java.lang.invoke.MethodType,
java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)' at index
9 is CONSTANT_InterfaceMethodRef and should be CONSTANT_MethodRef
        at Interf.callerDynamic(Interf.jasm)
        at Main.main(Main.java:6)
Special
java.lang.IncompatibleClassChangeError: Method 'java.lang.Object
Interf.foo(Interf)' must be InterfaceMethodref constant
        at Interf.callerSpecial(Interf.jasm)
        at Main.main(Main.java:13)
---------------------

Best regards,
Vladimir Parfinenko


More information about the asmtools-dev mailing list