Skip to content

Commit 591e71e

Browse files
committed
8354890: AOT-initialize j.l.i.MethodHandleImpl and inner classes
Reviewed-by: liach, vlivanov
1 parent 53ad4b2 commit 591e71e

File tree

8 files changed

+176
-12
lines changed

8 files changed

+176
-12
lines changed

src/hotspot/share/cds/aotClassInitializer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
321321
if (is_allowed(indy_specs, ik)) {
322322
return true;
323323
}
324+
325+
if (ik->name()->starts_with("java/lang/invoke/MethodHandleImpl")) {
326+
return true;
327+
}
324328
}
325329

326330
#ifdef ASSERT
@@ -339,6 +343,7 @@ bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) {
339343
return ik == vmClasses::Class_klass() ||
340344
ik == vmClasses::internal_Unsafe_klass() ||
341345
ik == vmClasses::ConcurrentHashMap_klass() ||
346+
ik == vmClasses::MethodHandleImpl_klass() ||
342347
ik == vmClasses::Reference_klass();
343348
}
344349

src/hotspot/share/cds/aotConstantPoolResolver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ bool AOTConstantPoolResolver::is_indy_resolution_deterministic(ConstantPool* cp,
506506
Symbol* factory_type_sig = cp->uncached_signature_ref_at(cp_index);
507507
if (log_is_enabled(Debug, cds, resolve)) {
508508
ResourceMark rm;
509-
log_debug(cds, resolve)("Checking indy callsite signature [%d]: %s", cp_index, factory_type_sig->as_C_string());
509+
log_debug(cds, resolve)("Checking lambda callsite signature [%d]: %s", cp_index, factory_type_sig->as_C_string());
510510
}
511511

512512
if (!check_lambda_metafactory_signature(cp, factory_type_sig)) {

src/hotspot/share/cds/cdsHeapVerifier.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,10 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
138138
"CD_Object_array", // E same as <...>ConstantUtils.CD_Object_array::CD_Object
139139
"INVOKER_SUPER_DESC"); // E same as java.lang.constant.ConstantDescs::CD_Object
140140

141-
ADD_EXCL("java/lang/invoke/MethodHandleImpl$ArrayAccessor",
142-
"OBJECT_ARRAY_GETTER", // D
143-
"OBJECT_ARRAY_SETTER", // D
144-
"OBJECT_ARRAY_LENGTH"); // D
145-
141+
ADD_EXCL("java/lang/runtime/ObjectMethods", "CLASS_IS_INSTANCE", // D
142+
"FALSE", // D
143+
"TRUE", // D
144+
"ZERO"); // D
146145
}
147146

148147
# undef ADD_EXCL

src/hotspot/share/classfile/vmClassMacros.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
do_klass(VarHandle_klass, java_lang_invoke_VarHandle ) \
115115
do_klass(MemberName_klass, java_lang_invoke_MemberName ) \
116116
do_klass(ResolvedMethodName_klass, java_lang_invoke_ResolvedMethodName ) \
117+
do_klass(MethodHandleImpl_klass, java_lang_invoke_MethodHandleImpl ) \
117118
do_klass(MethodHandleNatives_klass, java_lang_invoke_MethodHandleNatives ) \
118119
do_klass(LambdaForm_klass, java_lang_invoke_LambdaForm ) \
119120
do_klass(MethodType_klass, java_lang_invoke_MethodType ) \

src/hotspot/share/oops/instanceKlass.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,12 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) {
860860
return;
861861
}
862862

863+
if (is_runtime_setup_required()) {
864+
// Need to take the slow path, which will call the runtimeSetup() function instead
865+
// of <clinit>
866+
initialize(CHECK);
867+
return;
868+
}
863869
if (log_is_enabled(Info, cds, init)) {
864870
ResourceMark rm;
865871
log_info(cds, init)("%s (aot-inited)", external_name());
@@ -878,7 +884,6 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) {
878884
#endif
879885

880886
set_init_thread(THREAD);
881-
AOTClassInitializer::call_runtime_setup(THREAD, this);
882887
set_initialization_state_and_notify(fully_initialized, CHECK);
883888
}
884889
#endif

src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,11 @@ private static NamedFunction createFunction(byte func) {
15261526
}
15271527

15281528
static {
1529+
runtimeSetup();
1530+
}
1531+
1532+
// Also called from JVM when loading an AOT cache
1533+
private static void runtimeSetup() {
15291534
SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
15301535
@Override
15311536
public Class<?> getDeclaringClass(Object rmname) {

src/java.base/share/classes/java/lang/ref/Reference.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,11 @@ static void startReferenceHandlerThread(ThreadGroup tg) {
306306
handler.start();
307307
}
308308

309-
// Called from JVM when loading an AOT cache
310309
static {
311310
runtimeSetup();
312311
}
313312

313+
// Also called from JVM when loading an AOT cache
314314
private static void runtimeSetup() {
315315
// provide access in SharedSecrets
316316
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {

test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/MethodHandleTest.java

Lines changed: 153 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,22 @@
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;
3943
import java.lang.invoke.MethodHandle;
4044
import java.lang.invoke.MethodHandles;
4145
import java.lang.invoke.MethodType;
4246
import java.lang.invoke.VarHandle;
47+
import java.lang.runtime.ObjectMethods;
4348
import jdk.test.lib.cds.CDSAppTester;
4449
import jdk.test.lib.process.OutputAnalyzer;
4550
import jdk.test.lib.helpers.ClassFileInstaller;
51+
import static java.lang.invoke.MethodType.methodType;
4652

4753
public 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

Comments
 (0)