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