When to initialize the method's class for MethodHandles.Lookup.findStatic()?
Cheng Jin
jincheng at ca.ibm.com
Thu Mar 17 23:02:11 UTC 2022
Hi Raffaello,
The code snippet I posted previously was not the original test I verified on Java 17 (which was generated via asmtools to trigger verification)
Here's the pretty much the test I used:
Test_1.java
import java.lang.invoke.*;
public class Test_1 {
static MethodHandle mh;
static {
try {
mh = MethodHandles.lookup().findStatic(Test_2.class,
"testMethod", MethodType.methodType(int[].class));
} catch (NoSuchMethodException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Throwable {
//Test_1.mh.invoke();
}
}
Test_2.jasm
super public class Test_2
version 52:0
{
static Method "<clinit>":"()V"
stack 5 locals 12
{
bipush -2;
istore 7;
iload 7;
sipush 997;
if_icmplt L127;
iconst_0;
istore 8;
L127: stack_frame_type full;
locals_map int, class "[B", class "[B", class "[Ljava/lang/Class;", class "[I", class "[I", class "[I", int, bogus, bogus, float, float;
iload 8;
istore 8;
return;
}
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
static Method testMethod:"()[I"
stack 6 locals 1
{
getstatic Field testArray:"[I";
areturn;
}
} // end Class Test_2
Jdk17/bin/java -jar asmtools.jar jasm Test_2.jasm // create the .class file of Test_2
Jdk17/bin/java Test_1
java.lang.IllegalAccessException: no such method: Test_2.testMethod()int[]/invokeStatic
at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:972)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1117)
at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3649)
at java.base/java.lang.invoke.MethodHandles$Lookup.findStatic(MethodHandles.java:2588)
at Test_1.<clinit>(Test_1.java:9)
Caused by: java.lang.VerifyError: Inconsistent stackmap frames at branch target 15
Exception Details:
Location:
Test_2.<clinit>()V @15: iload
Reason:
Type top (current frame, locals[0]) is not assignable to integer (stack map, locals[0])
Current Frame:
bci: @9
flags: { }
locals: { top, top, top, top, top, top, top, integer }
stack: { integer, integer }
Stackmap Frame:
bci: @15
flags: { }
locals: { integer, '[B', '[B', '[Ljava/lang/Class;', '[I', '[I', '[I', integer, top, top, float, float }
stack: { }
Bytecode:
0000000: 10fe 3607 1507 1103 e5a1 0006 0336 0815
0000010: 0836 08b1
Stackmap Table:
full_frame(@15,{Integer,Object[#11],Object[#11],Object[#20],Object[#5],Object[#5],Object[#5],Integer,Top,Top,Float,Float},{})
at java.base/java.lang.invoke.MethodHandleNatives.resolve(Native Method)
at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:1085)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1114)
... 3 more
--------
Test_2 was intentionally modified to throw VerifyError if initialized.
As you see above, mh.inovke() was commented out in Test_1.main() but the Test_2 was still verified in the test.
So it is impossible to verify Test_2 if it is not initialized, which only means Test_2 is initialized during the lookup.findstatic rather than mh.invoke().
Best Regards
Cheng
----- Original Message -----
> From: "Cheng Jin" <jincheng at ca.ibm.com>
> To: "core-libs-dev" <core-libs-dev at openjdk.java.net>
> Sent: Thursday, March 17, 2022 5:42:57 PM
> Subject: When to initialize the method's class for MethodHandles.Lookup.findStatic()?
> Hi there,
>
> The document of
> INVALID URI REMOVED
> n_java_javase_17_docs_api_java.base_java_lang_invoke_MethodHandles.Loo
> kup.html-23findStatic-28java.lang.Class-2Cjava.lang.String-2Cjava.lang
> .invoke.MethodType-29&d=DwIFaQ&c=jf_iaSHvJObTbx-siA1ZOg&r=X90f3XIRXAH8
> hbNam6bIUlWfF_qUAezL9ue7M7bFuPQ&m=Xt-10pHYoPWnY6dByKowR4yeLtEs7kZkKUgt
> bxKUGvM&s=dPAMGMxphLLXT9N4ZdukiNvWyvRPAGkcGCBLTy_sm0c&e=
> in the Java API is ambiguous in terms of when to initialize the
> method's class as follows (the same description as in other OpenJDK
> versions)
>
> If the returned method handle is invoked, the method's class will be
> initialized, if it has not already been initialized.
>
>
> It occurs to me that the method's class should be initialized when
> invoking the method handle but OpenJDK actually chooses to do the
> initialization in
> lookup.findStatic() rather than in mh.invoke() e.g.
> import java.lang.invoke.*;
>
> public class Test_1 {
> static MethodHandle mh = MethodHandles.lookup().findStatic(Test_2.class,
> "testMethod", MethodType.methodType(int.class, int.class)); <-----------
> Test_2.class gets initialized and verified.
>
> public static void main(String[] args) throws Throwable {
> Test_1.mh.invoke();
> }
> }
>
> public class Test_2 {
> static int testMethod(int value) { return (value + 1); } }
>
> So there should be more clear explanation what is the correct or
> expected behaviour at this point and why OpenJDK doesn't comply with
> the document to delay the initialization of the method's class to mh.invoke().
Hi,
if by initialization you mean running the static block, then it's a bug.
As far as i remember, the JVMS spec cleanly separate the Linking exceptions from the Runtime exceptions.
https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.invokestatic
The linking exceptions occurs when calling findStatic() while the runtime exceptions will appear at runtime when calling invokeExact()/invoke().
>
> Best Regards
> Cheng Jin
regards,
Rémi
More information about the core-libs-dev
mailing list