3333 * @build MethodHandleTest
3434 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar mh.jar
3535 * MethodHandleTestApp MethodHandleTestApp$A MethodHandleTestApp$B
36+ * UnsupportedBSMs UnsupportedBSMs$MyEnum
37+ * ObjectMethodsTest ObjectMethodsTest$C
3638 * @run driver MethodHandleTest AOT
3739 */
3840
41+ import java .io .Serializable ;
42+ import java .lang .invoke .CallSite ;
3943import java .lang .invoke .MethodHandle ;
4044import java .lang .invoke .MethodHandles ;
4145import java .lang .invoke .MethodType ;
4246import java .lang .invoke .VarHandle ;
47+ import java .lang .runtime .ObjectMethods ;
4348import jdk .test .lib .cds .CDSAppTester ;
4449import jdk .test .lib .process .OutputAnalyzer ;
4550import jdk .test .lib .helpers .ClassFileInstaller ;
51+ import static java .lang .invoke .MethodType .methodType ;
4652
4753public class MethodHandleTest {
4854 static final String appJar = ClassFileInstaller .getJarPath ("mh.jar" );
@@ -87,6 +93,7 @@ public String[] appCommandLine(RunMode runMode) {
8793 @ Override
8894 public void checkExecution (OutputAnalyzer out , RunMode runMode ) throws Exception {
8995 out .shouldHaveExitValue (0 );
96+ out .shouldContain ("SwitchBootstraps.typeSwitch: 5678" );
9097
9198 if (!runMode .isProductionRun ()) {
9299 // MethodHandleTestApp should be initialized in the assembly phase as well,
@@ -95,6 +102,7 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception
95102 } else {
96103 // Make sure MethodHandleTestApp is aot-initialized in the production run.
97104 out .shouldNotContain ("MethodHandleTestApp.<clinit>" );
105+ out .shouldContain ("intElm = 777" );
98106 }
99107 }
100108 }
@@ -141,17 +149,29 @@ static class B {
141149 static VarHandle staticVH ;
142150 static VarHandle instanceVH ;
143151
152+ static MethodHandle arrayGetMH ;
153+
154+ // Created in assembly phase.
155+ // Used in production run.
156+ static MethodHandle ObjectMethodsTest_handle ;
157+
144158 static {
145159 System .out .println ("MethodHandleTestApp.<clinit>" );
146160
147161 try {
148- setupCachedStatics ();
162+ setupCachedMHs ();
163+ ObjectMethodsTest_handle = ObjectMethodsTest .makeHandle ();
164+ UnsupportedBSMs .invokeUnsupportedBSMs ();
149165 } catch (Throwable t ) {
150166 throw new RuntimeException ("Unexpected exception" , t );
151167 }
152168 }
153169
154- static void setupCachedStatics () throws Throwable {
170+ // This method is executed during the assembly phase.
171+ //
172+ // Store some MHs into the AOT cache. Make sure they can be used during the production run.
173+ // Also check that the class initialization order is consistent with specification.
174+ static void setupCachedMHs () throws Throwable {
155175 MethodHandles .Lookup LOOKUP = MethodHandles .lookup ();
156176 virtualMH = LOOKUP .findVirtual (A .class , "virtualMethod" , MethodType .methodType (void .class ));
157177 instanceVH = LOOKUP .findVarHandle (B .class , "instanceField" , long .class );
@@ -161,11 +181,13 @@ static void setupCachedStatics() throws Throwable {
161181 A .staticMethod ();
162182 staticMH = LOOKUP .findStatic (A .class , "staticMethod" , MethodType .methodType (void .class ));
163183
164-
165184 // Make sure B is initialized before create staticVH, but the AOT-cached staticVH
166185 // should still include the init barrier even if B was initialized in the assembly phase.
167186 B .staticField += 5678 ;
168187 staticVH = LOOKUP .findStaticVarHandle (B .class , "staticField" , long .class );
188+
189+ // Array access MHs
190+ arrayGetMH = MethodHandles .arrayElementGetter (int [].class );
169191 }
170192
171193 private static Object invoke (MethodHandle mh , Object ... args ) {
@@ -184,8 +206,11 @@ public static void main(String[] args) throws Throwable {
184206
185207 testMethodHandles (isProduction );
186208 testVarHandles (isProduction );
187- }
188209
210+ ObjectMethodsTest .testEqualsC (ObjectMethodsTest_handle );
211+
212+ UnsupportedBSMs .invokeUnsupportedBSMs ();
213+ }
189214
190215 static void testMethodHandles (boolean isProduction ) throws Throwable {
191216 state_A = 0 ;
@@ -212,6 +237,14 @@ static void testMethodHandles(boolean isProduction) throws Throwable {
212237 throw new RuntimeException ("state_A should be 6 but is: " + state_A );
213238 }
214239 }
240+
241+ // (3) Test an array access MH
242+ int [] intArray = new int [] {111 , 222 , 777 };
243+ int intElm = (Integer )arrayGetMH .invoke (intArray , 2 );
244+ System .out .println ("intElm = " + intElm );
245+ if (intElm != 777 ) {
246+ throw new RuntimeException ("intElm should be 777 but is: " + intElm );
247+ }
215248 }
216249
217250 static void testVarHandles (boolean isProduction ) throws Throwable {
@@ -246,3 +279,119 @@ static void testVarHandles(boolean isProduction) throws Throwable {
246279 }
247280 }
248281}
282+
283+ // Excerpt from test/jdk/java/lang/runtime/ObjectMethodsTest.java
284+ class ObjectMethodsTest {
285+ public static class C {
286+ static final MethodType EQUALS_DESC = methodType (boolean .class , C .class , Object .class );
287+ static final MethodType HASHCODE_DESC = methodType (int .class , C .class );
288+ static final MethodType TO_STRING_DESC = methodType (String .class , C .class );
289+
290+ static final MethodHandle [] ACCESSORS = accessors ();
291+ static final String NAME_LIST = "x;y" ;
292+ private static MethodHandle [] accessors () {
293+ try {
294+ return new MethodHandle []{
295+ MethodHandles .lookup ().findGetter (C .class , "x" , int .class ),
296+ MethodHandles .lookup ().findGetter (C .class , "y" , int .class ),
297+ };
298+ } catch (Exception e ) {
299+ throw new AssertionError (e );
300+ }
301+ }
302+
303+ private final int x ;
304+ private final int y ;
305+ C (int x , int y ) { this .x = x ; this .y = y ; }
306+ public int x () { return x ; }
307+ public int y () { return y ; }
308+ }
309+
310+ public static MethodHandle makeHandle () throws Throwable {
311+ MethodHandles .Lookup LOOKUP = MethodHandles .lookup ();
312+
313+ CallSite cs = (CallSite )ObjectMethods .bootstrap (LOOKUP , "equals" , C .EQUALS_DESC , C .class , C .NAME_LIST , C .ACCESSORS );
314+ return cs .dynamicInvoker ();
315+ }
316+
317+ public static void testEqualsC (MethodHandle handle ) throws Throwable {
318+ C c = new C (5 , 5 );
319+ assertTrue ((boolean )handle .invokeExact (c , (Object )c ));
320+ assertTrue ((boolean )handle .invokeExact (c , (Object )new C (5 , 5 )));
321+ assertFalse ((boolean )handle .invokeExact (c , (Object )new C (5 , 4 )));
322+ assertFalse ((boolean )handle .invokeExact (c , (Object )new C (4 , 5 )));
323+ assertFalse ((boolean )handle .invokeExact (c , (Object )null ));
324+ assertFalse ((boolean )handle .invokeExact (c , new Object ()));
325+ }
326+
327+ private static void assertTrue (boolean b ) {
328+ if (b != true ) {
329+ throw new RuntimeException ("Assertion fails" );
330+ }
331+ }
332+
333+ private static void assertFalse (boolean b ) {
334+ assertTrue (!b );
335+ }
336+ }
337+
338+ class UnsupportedBSMs {
339+ // This method is executed during the assembly phase.
340+ //
341+ // Try to invoke some BSMs that are normally not executed in the assembly phase. However, these
342+ // BSMs may be executed in rare cases (such as when loading signed classes -- see JDK-8353330.)
343+ // Let's make sure the assembly phase can tolerate such BSMs, even if the call sites that they
344+ // produce are not stored into the AOT cache.
345+ //
346+ // Hopefully with enough testing in here, we can avoid situations where innocent changes in
347+ // core libs might cause the AOT assembly phase to fail.
348+ static void invokeUnsupportedBSMs () throws Throwable {
349+ int n = testTypeSwitch ((Integer )1234 );
350+ System .out .println ("SwitchBootstraps.typeSwitch: " + n );
351+ if (n != 5678 ) {
352+ throw new RuntimeException ("n should be " + 5678 + " but is: " + n );
353+ }
354+
355+ Object o = getRunnableAndSerializable ();
356+ System .out .println (o .getClass ());
357+ if (!(o instanceof Runnable ) || !(o instanceof Serializable )) {
358+ throw new RuntimeException ("o has wrong interfaces" );
359+ }
360+
361+ String s = statementEnum (MyEnum .A );
362+ if (!s .equals ("A" )) {
363+ throw new RuntimeException ("enum switch incorrect" );
364+ }
365+ }
366+
367+ static int testTypeSwitch (Number n ) {
368+ // BSM = java/lang/runtime/SwitchBootstraps::typeSwitch
369+ return switch (n ) {
370+ case Integer in -> {
371+ yield 5678 ;
372+ }
373+ default -> {
374+ yield 0 ;
375+ }
376+ };
377+ }
378+
379+ static Runnable getRunnableAndSerializable () {
380+ // BSM = java/lang/invoke/LambdaMetafactory.altMetafactory
381+ return (Runnable & Serializable ) () -> {
382+ System .out .println ("Inside getRunnableAndSerializable" );
383+ };
384+ }
385+
386+ // Excerpt from test/langtools/tools/javac/patterns/EnumTypeChanges.java
387+ enum MyEnum { A , B ; }
388+ static String statementEnum (MyEnum e ) {
389+ // BSM = java/lang/runtime/SwitchBootstraps.enumSwitch
390+ switch (e ) {
391+ case A -> { return "A" ; }
392+ case B -> { return "B" ; }
393+ case MyEnum e1 when e1 == null -> throw new AssertionError ();
394+ default -> { return "D" ; }
395+ }
396+ }
397+ }
0 commit comments