3838
3939import java .io .File ;
4040import java .io .IOException ;
41+ import java .lang .invoke .CallSite ;
4142import java .lang .invoke .ConstantBootstraps ;
4243import java .lang .invoke .MethodHandles ;
44+ import java .lang .invoke .MethodType ;
4345import java .lang .invoke .StringConcatFactory ;
4446import java .lang .reflect .Method ;
4547import java .nio .file .Files ;
4648import java .util .List ;
49+ import java .util .Set ;
4750
4851import org .testng .Assert ;
4952import org .testng .annotations .Test ;
@@ -201,6 +204,14 @@ byte[] generateClassfile() {
201204 concat .visitMaxs (0 , 0 );
202205 concat .visitEnd ();
203206
207+ MethodVisitor shouldNotBeCalled = cw .visitMethod (PUBLIC_STATIC , "shouldNotBeCalled" , "()V" , null , null );
208+ sig = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;" ;
209+ handle = new Handle (H_INVOKESTATIC , testClassInternalName , "shouldNotBeCalledBSM" , sig , false );
210+ shouldNotBeCalled .visitInvokeDynamicInsn ("do_shouldNotBeCalled" , "()V" , handle );
211+ shouldNotBeCalled .visitInsn (RETURN );
212+ shouldNotBeCalled .visitMaxs (0 , 0 );
213+ shouldNotBeCalled .visitEnd ();
214+
204215 cw .visitEnd ();
205216 return cw .toByteArray ();
206217 }
@@ -235,6 +246,37 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
235246 }
236247 }
237248
249+ /**
250+ * Asserts that {@link ConstantPool#lookupConstant(int, boolean)} with {@code resolve == false}
251+ * returns null for all resolvable constant entries.
252+ */
253+ private static void assertNoEagerConstantResolution (Class <?> testClass , ConstantPool cp , Method getTagAt ) throws Exception {
254+ for (int cpi = 1 ; cpi < cp .length (); cpi ++) {
255+ String tag = String .valueOf (getTagAt .invoke (cp , cpi ));
256+ switch (tag ) {
257+ case "MethodHandle" :
258+ case "MethodType" :
259+ case "Dynamic" : {
260+ Object con = cp .lookupConstant (cpi , false );
261+ Assert .assertNull (con , "Unexpected eager resolution in " + testClass + " at cpi " + cpi + " (tag: " + tag + ")" );
262+ break ;
263+ }
264+ }
265+ }
266+ }
267+
268+ /**
269+ * Ensures {@link ConstantPool#lookupBootstrapMethodInvocation} does not invoke the associated bootstrap method.
270+ */
271+ private static void assertLookupBMIDoesNotInvokeBM (MetaAccessProvider metaAccess , Class <?> testClass ) throws Exception {
272+ ResolvedJavaMethod shouldNotBeCalled = metaAccess .lookupJavaMethod (testClass .getDeclaredMethod ("shouldNotBeCalled" ));
273+ ConstantPool cp = shouldNotBeCalled .getConstantPool ();
274+ int cpi = getFirstInvokedynamicOperand (shouldNotBeCalled );
275+ BootstrapMethodInvocation bmi = cp .lookupBootstrapMethodInvocation (cpi , INVOKEDYNAMIC );
276+ Assert .assertEquals (bmi .getName (), "do_shouldNotBeCalled" );
277+ Assert .assertEquals (bmi .getMethod ().getName (), "shouldNotBeCalledBSM" );
278+ }
279+
238280 @ SuppressWarnings ("try" )
239281 @ Test
240282 public void test () throws Throwable {
@@ -263,25 +305,15 @@ public void test() throws Throwable {
263305 Method m = testClass .getDeclaredMethod ("run" );
264306 ResolvedJavaMethod run = metaAccess .lookupJavaMethod (m );
265307 ConstantPool cp = run .getConstantPool ();
308+
309+ assertNoEagerConstantResolution (testClass , cp , getTagAt );
310+ assertLookupBMIDoesNotInvokeBM (metaAccess , testClass );
311+
266312 Object lastConstant = null ;
267313 for (int cpi = 1 ; cpi < cp .length (); cpi ++) {
268314 String tag = String .valueOf (getTagAt .invoke (cp , cpi ));
269- switch (tag ) {
270- case "MethodHandle" :
271- case "MethodType" :
272- case "Dynamic" : {
273- Object con = cp .lookupConstant (cpi , false );
274- Assert .assertNull (con , "Unexpected eager resolution" );
275-
276- con = cp .lookupConstant (cpi , true );
277- Assert .assertNotNull (con , "Eager resolution failed" );
278-
279- if (tag .equals ("Dynamic" )) {
280- lastConstant = cp .lookupConstant (cpi );
281- Assert .assertEquals (con , lastConstant );
282- }
283- break ;
284- }
315+ if (tag .equals ("Dynamic" )) {
316+ lastConstant = cp .lookupConstant (cpi );
285317 }
286318 }
287319 Assert .assertTrue (lastConstant != null , "No Dynamic entries in constant pool of " + testClass .getName ());
@@ -315,7 +347,12 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
315347 Method m = testClass .getDeclaredMethod ("concat" );
316348 ResolvedJavaMethod concat = metaAccess .lookupJavaMethod (m );
317349 ConstantPool cp = concat .getConstantPool ();
318- Object lastConstant = null ;
350+
351+ Set <String > expectedBSMs = Set .of (
352+ "jdk.vm.ci.hotspot.test.TestDynamicConstant.shouldNotBeCalledBSM" ,
353+ "java.lang.invoke.StringConcatFactory.makeConcatWithConstants"
354+ );
355+
319356 for (int cpi = 1 ; cpi < cp .length (); cpi ++) {
320357 String tag = String .valueOf (getTagAt .invoke (cp , cpi ));
321358 BootstrapMethodInvocation bsmi = cp .lookupBootstrapMethodInvocation (cpi , -1 );
@@ -324,7 +361,7 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
324361 String bsm = bsmi .getMethod ().format ("%H.%n" );
325362 if (tag .equals ("InvokeDynamic" )) {
326363 Assert .assertTrue (bsmi .isInvokeDynamic ());
327- Assert .assertEquals ( bsm , "java.lang.invoke.StringConcatFactory.makeConcatWithConstants" );
364+ Assert .assertTrue ( expectedBSMs . contains ( bsm ), expectedBSMs . toString () );
328365 } else {
329366 Assert .assertFalse (bsmi .isInvokeDynamic ());
330367 if (condyType == CondyType .CALL_DIRECT_BSM ) {
@@ -338,24 +375,41 @@ private void testLookupBootstrapMethodInvocation(CondyType condyType, MetaAccess
338375 }
339376 }
340377
341- testLoadReferencedType (concat );
378+ testLoadReferencedType (concat , cp );
342379 }
343380
344381 private static int beS4 (byte [] data , int bci ) {
345382 return (data [bci ] << 24 ) | ((data [bci + 1 ] & 0xff ) << 16 ) | ((data [bci + 2 ] & 0xff ) << 8 ) | (data [bci + 3 ] & 0xff );
346383 }
347384
385+ private static int beU1 (byte [] data , int bci ) {
386+ return data [bci ] & 0xff ;
387+ }
348388 private static final int LDC2_W = 20 ;
349- private static void testLoadReferencedType (ResolvedJavaMethod method ) {
350- // Make sure that loadReferencedType for an invokedynamic call site works.
389+
390+ /**
391+ * Gets the operand of the first invokedynamic in {@code method}. This
392+ * assumes that the bytecode of {@code method} is an INVOKEDYNAMIC instruction,
393+ * possibly preceded by an LDC instruction.
394+ */
395+ private static int getFirstInvokedynamicOperand (ResolvedJavaMethod method ) {
351396 byte [] code = method .getCode ();
352- Assert .assertTrue (code [0 ] == LDC || code [0 ] == LDC2_W , "unexpected ldc sequence" );
353- int bci = code [0 ] == LDC ? 2 : 3 ;
354- Assert .assertTrue ((code [bci ] & 0xff ) == INVOKEDYNAMIC , "unexpected bytecode" );
355- int cpi = beS4 (code , bci + 1 );
356- method .getConstantPool ().loadReferencedType (cpi , INVOKEDYNAMIC , false );
357- BootstrapMethodInvocation bmi = method .getConstantPool ().lookupBootstrapMethodInvocation (cpi , INVOKEDYNAMIC );
358- Assert .assertEquals (bmi .getName (), "do_concat" );
397+ int opcode = beU1 (code , 0 );
398+ if (opcode == INVOKEDYNAMIC ) {
399+ return beS4 (code , 1 );
400+ }
401+ Assert .assertTrue (opcode == LDC || opcode == LDC2_W , String .valueOf (opcode ));
402+ int bci = opcode == LDC ? 2 : 3 ;
403+ Assert .assertEquals (beU1 (code , bci ), INVOKEDYNAMIC );
404+ return beS4 (code , bci + 1 );
405+ }
406+
407+ /**
408+ * Ensures that loadReferencedType for an invokedynamic call site does not throw an exception.
409+ */
410+ private static void testLoadReferencedType (ResolvedJavaMethod method , ConstantPool cp ) {
411+ int cpi = getFirstInvokedynamicOperand (method );
412+ cp .loadReferencedType (cpi , INVOKEDYNAMIC , false );
359413 }
360414
361415 // @formatter:off
@@ -396,4 +450,9 @@ private static void testLoadReferencedType(ResolvedJavaMethod method) {
396450 public static Object getObject (Object v1 , Object v2 ) { return null ; }
397451 public static List <?> getList (List <?> v1 , List <?> v2 ) { return List .of (v1 , v2 ); }
398452 // @formatter:on
453+
454+ // A bootstrap method that should never be called
455+ public static CallSite shouldNotBeCalledBSM (MethodHandles .Lookup caller , String name , MethodType type ) throws Exception {
456+ throw new RuntimeException ("should not be called" );
457+ }
399458}
0 commit comments