When to initialize the method's class for MethodHandles.Lookup.findStatic()?

Raffaello Giulietti raffaello.giulietti at gmail.com
Fri Mar 18 00:20:43 UTC 2022


Hi Cheng,

I'm not sure I understand your point. By class initialization I mean 
what's specified in the JVMS [1].

Are you concerned with initialization or with verification (which 
precedes initialization)?


Raffaello

----

[1] https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-5.html#jvms-5.5



On 2022-03-18 00:02, Cheng Jin wrote:
> 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