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" <jinch...@ca.ibm.com>
To: "core-libs-dev" <core-libs-dev@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

Reply via email to