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

Leonid Kuskov leonid.kuskov at oracle.com
Mon Mar 30 23:08:53 UTC 2020


Hello Vladimir,

Thanks for catching this.  See my comments below:

> There are two problems:
> 1) 0-th boostrap method references LambdaMetafactory.metafactory() as
> InterfaceMethodRef #9 but the declaring class is not an interface.
You are right, there the jasm parser doesn't take into account a type of 
the method.
I've filled out the defect: 
https://bugs.openjdk.java.net/browse/CODETOOLS-7902648
Fix is known I will put it back asap.
> 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.
Initially, asmtools was made for testing around java binaries. Its jasm 
parser is straightforward and can't correctly interpret
some class/interface cross-references without extra tags.
Converting Interf.class to an jasm ouput (jdis tool) shows that the 
expected format of the callerSpecial method slightly differs from your code:

public Method callerSpecial:"(LInterf;)LInterf;"
...
         invokespecial    Method foo:"(LInterf;)Ljava/lang/Object;";
...

please just update the invoke instruction:

invokespecial  InterfaceMethod foo:"(LInterf;)Ljava/lang/Object;";

to get rid of the 2nd ICCE exception.

Regards,
Leonid

On 3/29/20 21:30, Vladimir Parfinenko wrote:
> 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