mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8354890: AOT-initialize j.l.i.MethodHandleImpl and inner classes
Reviewed-by: liach, vlivanov
This commit is contained in:
parent
53ad4b2ad2
commit
591e71ebe5
@ -321,6 +321,10 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
|
||||
if (is_allowed(indy_specs, ik)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ik->name()->starts_with("java/lang/invoke/MethodHandleImpl")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
@ -339,6 +343,7 @@ bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) {
|
||||
return ik == vmClasses::Class_klass() ||
|
||||
ik == vmClasses::internal_Unsafe_klass() ||
|
||||
ik == vmClasses::ConcurrentHashMap_klass() ||
|
||||
ik == vmClasses::MethodHandleImpl_klass() ||
|
||||
ik == vmClasses::Reference_klass();
|
||||
}
|
||||
|
||||
|
||||
@ -506,7 +506,7 @@ bool AOTConstantPoolResolver::is_indy_resolution_deterministic(ConstantPool* cp,
|
||||
Symbol* factory_type_sig = cp->uncached_signature_ref_at(cp_index);
|
||||
if (log_is_enabled(Debug, cds, resolve)) {
|
||||
ResourceMark rm;
|
||||
log_debug(cds, resolve)("Checking indy callsite signature [%d]: %s", cp_index, factory_type_sig->as_C_string());
|
||||
log_debug(cds, resolve)("Checking lambda callsite signature [%d]: %s", cp_index, factory_type_sig->as_C_string());
|
||||
}
|
||||
|
||||
if (!check_lambda_metafactory_signature(cp, factory_type_sig)) {
|
||||
|
||||
@ -138,11 +138,10 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
|
||||
"CD_Object_array", // E same as <...>ConstantUtils.CD_Object_array::CD_Object
|
||||
"INVOKER_SUPER_DESC"); // E same as java.lang.constant.ConstantDescs::CD_Object
|
||||
|
||||
ADD_EXCL("java/lang/invoke/MethodHandleImpl$ArrayAccessor",
|
||||
"OBJECT_ARRAY_GETTER", // D
|
||||
"OBJECT_ARRAY_SETTER", // D
|
||||
"OBJECT_ARRAY_LENGTH"); // D
|
||||
|
||||
ADD_EXCL("java/lang/runtime/ObjectMethods", "CLASS_IS_INSTANCE", // D
|
||||
"FALSE", // D
|
||||
"TRUE", // D
|
||||
"ZERO"); // D
|
||||
}
|
||||
|
||||
# undef ADD_EXCL
|
||||
|
||||
@ -114,6 +114,7 @@
|
||||
do_klass(VarHandle_klass, java_lang_invoke_VarHandle ) \
|
||||
do_klass(MemberName_klass, java_lang_invoke_MemberName ) \
|
||||
do_klass(ResolvedMethodName_klass, java_lang_invoke_ResolvedMethodName ) \
|
||||
do_klass(MethodHandleImpl_klass, java_lang_invoke_MethodHandleImpl ) \
|
||||
do_klass(MethodHandleNatives_klass, java_lang_invoke_MethodHandleNatives ) \
|
||||
do_klass(LambdaForm_klass, java_lang_invoke_LambdaForm ) \
|
||||
do_klass(MethodType_klass, java_lang_invoke_MethodType ) \
|
||||
|
||||
@ -860,6 +860,12 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_runtime_setup_required()) {
|
||||
// Need to take the slow path, which will call the runtimeSetup() function instead
|
||||
// of <clinit>
|
||||
initialize(CHECK);
|
||||
return;
|
||||
}
|
||||
if (log_is_enabled(Info, cds, init)) {
|
||||
ResourceMark rm;
|
||||
log_info(cds, init)("%s (aot-inited)", external_name());
|
||||
@ -878,7 +884,6 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) {
|
||||
#endif
|
||||
|
||||
set_init_thread(THREAD);
|
||||
AOTClassInitializer::call_runtime_setup(THREAD, this);
|
||||
set_initialization_state_and_notify(fully_initialized, CHECK);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1526,6 +1526,11 @@ abstract class MethodHandleImpl {
|
||||
}
|
||||
|
||||
static {
|
||||
runtimeSetup();
|
||||
}
|
||||
|
||||
// Also called from JVM when loading an AOT cache
|
||||
private static void runtimeSetup() {
|
||||
SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
|
||||
@Override
|
||||
public Class<?> getDeclaringClass(Object rmname) {
|
||||
|
||||
@ -306,11 +306,11 @@ public abstract sealed class Reference<T>
|
||||
handler.start();
|
||||
}
|
||||
|
||||
// Called from JVM when loading an AOT cache
|
||||
static {
|
||||
runtimeSetup();
|
||||
}
|
||||
|
||||
// Also called from JVM when loading an AOT cache
|
||||
private static void runtimeSetup() {
|
||||
// provide access in SharedSecrets
|
||||
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
|
||||
|
||||
@ -33,16 +33,22 @@
|
||||
* @build MethodHandleTest
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar mh.jar
|
||||
* MethodHandleTestApp MethodHandleTestApp$A MethodHandleTestApp$B
|
||||
* UnsupportedBSMs UnsupportedBSMs$MyEnum
|
||||
* ObjectMethodsTest ObjectMethodsTest$C
|
||||
* @run driver MethodHandleTest AOT
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.runtime.ObjectMethods;
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class MethodHandleTest {
|
||||
static final String appJar = ClassFileInstaller.getJarPath("mh.jar");
|
||||
@ -87,6 +93,7 @@ public class MethodHandleTest {
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
|
||||
out.shouldHaveExitValue(0);
|
||||
out.shouldContain("SwitchBootstraps.typeSwitch: 5678");
|
||||
|
||||
if (!runMode.isProductionRun()) {
|
||||
// MethodHandleTestApp should be initialized in the assembly phase as well,
|
||||
@ -95,6 +102,7 @@ public class MethodHandleTest {
|
||||
} else {
|
||||
// Make sure MethodHandleTestApp is aot-initialized in the production run.
|
||||
out.shouldNotContain("MethodHandleTestApp.<clinit>");
|
||||
out.shouldContain("intElm = 777");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,17 +149,29 @@ class MethodHandleTestApp {
|
||||
static VarHandle staticVH;
|
||||
static VarHandle instanceVH;
|
||||
|
||||
static MethodHandle arrayGetMH;
|
||||
|
||||
// Created in assembly phase.
|
||||
// Used in production run.
|
||||
static MethodHandle ObjectMethodsTest_handle;
|
||||
|
||||
static {
|
||||
System.out.println("MethodHandleTestApp.<clinit>");
|
||||
|
||||
try {
|
||||
setupCachedStatics();
|
||||
setupCachedMHs();
|
||||
ObjectMethodsTest_handle = ObjectMethodsTest.makeHandle();
|
||||
UnsupportedBSMs.invokeUnsupportedBSMs();
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Unexpected exception", t);
|
||||
}
|
||||
}
|
||||
|
||||
static void setupCachedStatics() throws Throwable {
|
||||
// This method is executed during the assembly phase.
|
||||
//
|
||||
// Store some MHs into the AOT cache. Make sure they can be used during the production run.
|
||||
// Also check that the class initialization order is consistent with specification.
|
||||
static void setupCachedMHs() throws Throwable {
|
||||
MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
virtualMH = LOOKUP.findVirtual(A.class, "virtualMethod", MethodType.methodType(void.class));
|
||||
instanceVH = LOOKUP.findVarHandle(B.class, "instanceField", long.class);
|
||||
@ -161,11 +181,13 @@ class MethodHandleTestApp {
|
||||
A.staticMethod();
|
||||
staticMH = LOOKUP.findStatic(A.class, "staticMethod", MethodType.methodType(void.class));
|
||||
|
||||
|
||||
// Make sure B is initialized before create staticVH, but the AOT-cached staticVH
|
||||
// should still include the init barrier even if B was initialized in the assembly phase.
|
||||
B.staticField += 5678;
|
||||
staticVH = LOOKUP.findStaticVarHandle(B.class, "staticField", long.class);
|
||||
|
||||
// Array access MHs
|
||||
arrayGetMH = MethodHandles.arrayElementGetter(int[].class);
|
||||
}
|
||||
|
||||
private static Object invoke(MethodHandle mh, Object ... args) {
|
||||
@ -184,8 +206,11 @@ class MethodHandleTestApp {
|
||||
|
||||
testMethodHandles(isProduction);
|
||||
testVarHandles(isProduction);
|
||||
}
|
||||
|
||||
ObjectMethodsTest.testEqualsC(ObjectMethodsTest_handle);
|
||||
|
||||
UnsupportedBSMs.invokeUnsupportedBSMs();
|
||||
}
|
||||
|
||||
static void testMethodHandles(boolean isProduction) throws Throwable {
|
||||
state_A = 0;
|
||||
@ -212,6 +237,14 @@ class MethodHandleTestApp {
|
||||
throw new RuntimeException("state_A should be 6 but is: " + state_A);
|
||||
}
|
||||
}
|
||||
|
||||
// (3) Test an array access MH
|
||||
int[] intArray = new int[] {111, 222, 777};
|
||||
int intElm = (Integer)arrayGetMH.invoke(intArray, 2);
|
||||
System.out.println("intElm = " + intElm);
|
||||
if (intElm != 777) {
|
||||
throw new RuntimeException("intElm should be 777 but is: " + intElm);
|
||||
}
|
||||
}
|
||||
|
||||
static void testVarHandles(boolean isProduction) throws Throwable {
|
||||
@ -246,3 +279,119 @@ class MethodHandleTestApp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Excerpt from test/jdk/java/lang/runtime/ObjectMethodsTest.java
|
||||
class ObjectMethodsTest {
|
||||
public static class C {
|
||||
static final MethodType EQUALS_DESC = methodType(boolean.class, C.class, Object.class);
|
||||
static final MethodType HASHCODE_DESC = methodType(int.class, C.class);
|
||||
static final MethodType TO_STRING_DESC = methodType(String.class, C.class);
|
||||
|
||||
static final MethodHandle[] ACCESSORS = accessors();
|
||||
static final String NAME_LIST = "x;y";
|
||||
private static MethodHandle[] accessors() {
|
||||
try {
|
||||
return new MethodHandle[]{
|
||||
MethodHandles.lookup().findGetter(C.class, "x", int.class),
|
||||
MethodHandles.lookup().findGetter(C.class, "y", int.class),
|
||||
};
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
C (int x, int y) { this.x = x; this.y = y; }
|
||||
public int x() { return x; }
|
||||
public int y() { return y; }
|
||||
}
|
||||
|
||||
public static MethodHandle makeHandle() throws Throwable {
|
||||
MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", C.EQUALS_DESC, C.class, C.NAME_LIST, C.ACCESSORS);
|
||||
return cs.dynamicInvoker();
|
||||
}
|
||||
|
||||
public static void testEqualsC(MethodHandle handle) throws Throwable {
|
||||
C c = new C(5, 5);
|
||||
assertTrue((boolean)handle.invokeExact(c, (Object)c));
|
||||
assertTrue((boolean)handle.invokeExact(c, (Object)new C(5, 5)));
|
||||
assertFalse((boolean)handle.invokeExact(c, (Object)new C(5, 4)));
|
||||
assertFalse((boolean)handle.invokeExact(c, (Object)new C(4, 5)));
|
||||
assertFalse((boolean)handle.invokeExact(c, (Object)null));
|
||||
assertFalse((boolean)handle.invokeExact(c, new Object()));
|
||||
}
|
||||
|
||||
private static void assertTrue(boolean b) {
|
||||
if (b != true) {
|
||||
throw new RuntimeException("Assertion fails");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertFalse(boolean b) {
|
||||
assertTrue(!b);
|
||||
}
|
||||
}
|
||||
|
||||
class UnsupportedBSMs {
|
||||
// This method is executed during the assembly phase.
|
||||
//
|
||||
// Try to invoke some BSMs that are normally not executed in the assembly phase. However, these
|
||||
// BSMs may be executed in rare cases (such as when loading signed classes -- see JDK-8353330.)
|
||||
// Let's make sure the assembly phase can tolerate such BSMs, even if the call sites that they
|
||||
// produce are not stored into the AOT cache.
|
||||
//
|
||||
// Hopefully with enough testing in here, we can avoid situations where innocent changes in
|
||||
// core libs might cause the AOT assembly phase to fail.
|
||||
static void invokeUnsupportedBSMs() throws Throwable {
|
||||
int n = testTypeSwitch((Integer)1234);
|
||||
System.out.println("SwitchBootstraps.typeSwitch: " + n);
|
||||
if (n != 5678) {
|
||||
throw new RuntimeException("n should be " + 5678 + " but is: " + n);
|
||||
}
|
||||
|
||||
Object o = getRunnableAndSerializable();
|
||||
System.out.println(o.getClass());
|
||||
if (!(o instanceof Runnable) || !(o instanceof Serializable)) {
|
||||
throw new RuntimeException("o has wrong interfaces");
|
||||
}
|
||||
|
||||
String s = statementEnum(MyEnum.A);
|
||||
if (!s.equals("A")) {
|
||||
throw new RuntimeException("enum switch incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
static int testTypeSwitch(Number n) {
|
||||
// BSM = java/lang/runtime/SwitchBootstraps::typeSwitch
|
||||
return switch (n) {
|
||||
case Integer in -> {
|
||||
yield 5678;
|
||||
}
|
||||
default -> {
|
||||
yield 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Runnable getRunnableAndSerializable() {
|
||||
// BSM = java/lang/invoke/LambdaMetafactory.altMetafactory
|
||||
return (Runnable & Serializable) () -> {
|
||||
System.out.println("Inside getRunnableAndSerializable");
|
||||
};
|
||||
}
|
||||
|
||||
// Excerpt from test/langtools/tools/javac/patterns/EnumTypeChanges.java
|
||||
enum MyEnum { A, B; }
|
||||
static String statementEnum(MyEnum e) {
|
||||
// BSM = java/lang/runtime/SwitchBootstraps.enumSwitch
|
||||
switch (e) {
|
||||
case A -> { return "A"; }
|
||||
case B -> { return "B"; }
|
||||
case MyEnum e1 when e1 == null -> throw new AssertionError();
|
||||
default -> { return "D"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user