8227260: JNI upcalls should bypass class initialization barrier in c2i adapter
Reviewed-by: eosterlund, dholmes, mdoerr
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -1277,6 +1277,7 @@
c2i_entry = __ pc();
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
@@ -1295,11 +1296,12 @@
__ bctr();
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, call_interpreter, ientry);
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
#ifdef COMPILER2
--- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -2713,6 +2713,7 @@
address c2i_entry = __ pc();
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
@@ -2729,11 +2730,12 @@
__ z_br(klass);
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
// This function returns the adjust size (in number of words) to a c2i adapter
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -971,10 +971,8 @@
address c2i_entry = __ pc();
- BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
- bs->c2i_entry_barrier(masm);
-
// Class initialization barrier for static methods
+ address c2i_no_clinit_check_entry = NULL;
if (VM_Version::supports_fast_class_init_checks()) {
Label L_skip_barrier;
Register method = rbx;
@@ -993,12 +991,16 @@
__ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
__ bind(L_skip_barrier);
+ c2i_no_clinit_check_entry = __ pc();
}
+ BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs->c2i_entry_barrier(masm);
+
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
__ flush();
- return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
--- a/src/hotspot/share/oops/method.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/share/oops/method.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -146,6 +146,12 @@
return adapter()->get_c2i_unverified_entry();
}
+address Method::get_c2i_no_clinit_check_entry() {
+ assert(VM_Version::supports_fast_class_init_checks(), "");
+ assert(adapter() != NULL, "must have");
+ return adapter()->get_c2i_no_clinit_check_entry();
+}
+
char* Method::name_and_sig_as_C_string() const {
return name_and_sig_as_C_string(constants()->pool_holder(), name(), signature());
}
@@ -1048,7 +1054,7 @@
_c2i_entry ---------------------------------+->[c2i entry..]
_i2i_entry -------------+ _i2c_entry ---------------+-> [i2c entry..] |
_from_interpreted_entry | _c2i_unverified_entry | |
- | | | |
+ | | _c2i_no_clinit_check_entry| |
| | (_cds_entry_table: CODE) | |
| +->[0]: jmp _entry_table[0] --> (i2i_entry_for "zero_locals") | |
| | (allocated at run time) | |
--- a/src/hotspot/share/oops/method.hpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/share/oops/method.hpp Wed Jul 24 10:50:40 2019 +0300
@@ -481,6 +481,7 @@
address get_i2c_entry();
address get_c2i_entry();
address get_c2i_unverified_entry();
+ address get_c2i_no_clinit_check_entry();
AdapterHandlerEntry* adapter() const {
return constMethod()->adapter();
}
--- a/src/hotspot/share/runtime/sharedRuntime.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -1446,7 +1446,19 @@
guarantee(callee != NULL && callee->is_method(), "bad handshake");
thread->set_vm_result_2(callee);
thread->set_callee_target(NULL);
- return callee->get_c2i_entry();
+ if (caller_frame.is_entry_frame() && VM_Version::supports_fast_class_init_checks()) {
+ // Bypass class initialization checks in c2i when caller is in native.
+ // JNI calls to static methods don't have class initialization checks.
+ // Fast class initialization checks are present in c2i adapters and call into
+ // SharedRuntime::handle_wrong_method() on the slow path.
+ //
+ // JVM upcalls may land here as well, but there's a proper check present in
+ // LinkResolver::resolve_static_call (called from JavaCalls::call_static),
+ // so bypassing it in c2i adapter is benign.
+ return callee->get_c2i_no_clinit_check_entry();
+ } else {
+ return callee->get_c2i_entry();
+ }
}
// Must be compiled to compiled path which is safe to stackwalk
@@ -2450,9 +2462,9 @@
: BasicHashtable<mtCode>(293, (DumpSharedSpaces ? sizeof(CDSAdapterHandlerEntry) : sizeof(AdapterHandlerEntry))) { }
// Create a new entry suitable for insertion in the table
- AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+ AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry) {
AdapterHandlerEntry* entry = (AdapterHandlerEntry*)BasicHashtable<mtCode>::new_entry(fingerprint->compute_hash());
- entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
if (DumpSharedSpaces) {
((CDSAdapterHandlerEntry*)entry)->init();
}
@@ -2601,8 +2613,9 @@
AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint,
address i2c_entry,
address c2i_entry,
- address c2i_unverified_entry) {
- return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+ address c2i_unverified_entry,
+ address c2i_no_clinit_check_entry) {
+ return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
}
AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
@@ -2778,6 +2791,7 @@
if (base == NULL) base = _c2i_entry;
assert(base <= _c2i_entry || _c2i_entry == NULL, "");
assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == NULL, "");
+ assert(base <= _c2i_no_clinit_check_entry || _c2i_no_clinit_check_entry == NULL, "");
return base;
}
@@ -2791,6 +2805,8 @@
_c2i_entry += delta;
if (_c2i_unverified_entry != NULL)
_c2i_unverified_entry += delta;
+ if (_c2i_no_clinit_check_entry != NULL)
+ _c2i_no_clinit_check_entry += delta;
assert(base_address() == new_base, "");
}
@@ -3129,10 +3145,20 @@
}
void AdapterHandlerEntry::print_adapter_on(outputStream* st) const {
- st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
- p2i(this), fingerprint()->as_string(),
- p2i(get_i2c_entry()), p2i(get_c2i_entry()), p2i(get_c2i_unverified_entry()));
-
+ st->print("AHE@" INTPTR_FORMAT ": %s", p2i(this), fingerprint()->as_string());
+ if (get_i2c_entry() != NULL) {
+ st->print(" i2c: " INTPTR_FORMAT, p2i(get_i2c_entry()));
+ }
+ if (get_c2i_entry() != NULL) {
+ st->print(" c2i: " INTPTR_FORMAT, p2i(get_c2i_entry()));
+ }
+ if (get_c2i_unverified_entry() != NULL) {
+ st->print(" c2iUV: " INTPTR_FORMAT, p2i(get_c2i_unverified_entry()));
+ }
+ if (get_c2i_no_clinit_check_entry() != NULL) {
+ st->print(" c2iNCI: " INTPTR_FORMAT, p2i(get_c2i_no_clinit_check_entry()));
+ }
+ st->cr();
}
#if INCLUDE_CDS
--- a/src/hotspot/share/runtime/sharedRuntime.hpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp Wed Jul 24 10:50:40 2019 +0300
@@ -636,6 +636,7 @@
address _i2c_entry;
address _c2i_entry;
address _c2i_unverified_entry;
+ address _c2i_no_clinit_check_entry;
#ifdef ASSERT
// Captures code and signature used to generate this adapter when
@@ -644,11 +645,12 @@
int _saved_code_length;
#endif
- void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+ void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry) {
_fingerprint = fingerprint;
_i2c_entry = i2c_entry;
_c2i_entry = c2i_entry;
_c2i_unverified_entry = c2i_unverified_entry;
+ _c2i_no_clinit_check_entry = c2i_no_clinit_check_entry;
#ifdef ASSERT
_saved_code = NULL;
_saved_code_length = 0;
@@ -661,9 +663,11 @@
AdapterHandlerEntry();
public:
- address get_i2c_entry() const { return _i2c_entry; }
- address get_c2i_entry() const { return _c2i_entry; }
- address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+ address get_i2c_entry() const { return _i2c_entry; }
+ address get_c2i_entry() const { return _c2i_entry; }
+ address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+ address get_c2i_no_clinit_check_entry() const { return _c2i_no_clinit_check_entry; }
+
address base_address();
void relocate(address new_base);
@@ -709,7 +713,10 @@
public:
static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint,
- address i2c_entry, address c2i_entry, address c2i_unverified_entry);
+ address i2c_entry,
+ address c2i_entry,
+ address c2i_unverified_entry,
+ address c2i_no_clinit_check_entry = NULL);
static void create_native_wrapper(const methodHandle& method);
static AdapterHandlerEntry* get_adapter(const methodHandle& method);
--- a/src/hotspot/share/runtime/thread.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/src/hotspot/share/runtime/thread.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -2969,11 +2969,6 @@
}
}
- // callee_target is never live across a gc point so NULL it here should
- // it still contain a methdOop.
-
- set_callee_target(NULL);
-
assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!");
// If we have deferred set_locals there might be oops waiting to be
// written
--- a/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java Wed Jul 24 10:09:30 2019 +0800
+++ b/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java Wed Jul 24 10:50:40 2019 +0300
@@ -27,24 +27,24 @@
*
* @requires !vm.graal.enabled
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=true ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint -DTHROW=true -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
*
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=exclude,*::static* ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
*/
import jdk.test.lib.Asserts;
@@ -70,6 +70,10 @@
static class Test {
static class A {
static {
+ if (!init(B.class)) {
+ throw new Error("init failed");
+ }
+
changePhase(Phase.IN_PROGRESS);
runTests(); // interpreted mode
warmup(); // trigger compilation
@@ -89,13 +93,15 @@
int f;
void m() {}
+
+ static native boolean init(Class<B> cls);
}
static class B extends A {}
- static void testInvokeStatic(Runnable action) { A.staticM(action); }
- static void testInvokeStaticSync(Runnable action) { A.staticS(action); }
- static void testInvokeStaticNative(Runnable action) { A.staticN(action); }
+ static void testInvokeStatic(Runnable action) { A.staticM(action); }
+ static void testInvokeStaticSync(Runnable action) { A.staticS(action); }
+ static void testInvokeStaticNative(Runnable action) { A.staticN(action); }
static int testGetStatic(Runnable action) { int v = A.staticF; action.run(); return v; }
static void testPutStatic(Runnable action) { A.staticF = 1; action.run(); }
@@ -106,20 +112,45 @@
static void testPutField(A recv, Runnable action) { recv.f = 1; action.run(); }
static void testInvokeVirtual(A recv, Runnable action) { recv.m(); action.run(); }
+ static native void testInvokeStaticJNI(Runnable action);
+ static native void testInvokeStaticSyncJNI(Runnable action);
+ static native void testInvokeStaticNativeJNI(Runnable action);
+
+ static native int testGetStaticJNI(Runnable action);
+ static native void testPutStaticJNI(Runnable action);
+ static native A testNewInstanceAJNI(Runnable action);
+ static native B testNewInstanceBJNI(Runnable action);
+
+ static native int testGetFieldJNI(A recv, Runnable action);
+ static native void testPutFieldJNI(A recv, Runnable action);
+ static native void testInvokeVirtualJNI(A recv, Runnable action);
+
static void runTests() {
checkBlockingAction(Test::testInvokeStatic); // invokestatic
+ checkBlockingAction(Test::testInvokeStaticSync); // invokestatic
checkBlockingAction(Test::testInvokeStaticNative); // invokestatic
- checkBlockingAction(Test::testInvokeStaticSync); // invokestatic
checkBlockingAction(Test::testGetStatic); // getstatic
checkBlockingAction(Test::testPutStatic); // putstatic
checkBlockingAction(Test::testNewInstanceA); // new
+ checkNonBlockingAction(Test::testInvokeStaticJNI); // invokestatic
+ checkNonBlockingAction(Test::testInvokeStaticSyncJNI); // invokestatic
+ checkNonBlockingAction(Test::testInvokeStaticNativeJNI); // invokestatic
+ checkNonBlockingAction(Test::testGetStaticJNI); // getstatic
+ checkNonBlockingAction(Test::testPutStaticJNI); // putstatic
+ checkBlockingAction(Test::testNewInstanceAJNI); // new
+
A recv = testNewInstanceB(NON_BLOCKING.get()); // trigger B initialization
checkNonBlockingAction(Test::testNewInstanceB); // new: NO BLOCKING: same thread: A being initialized, B fully initialized
checkNonBlockingAction(recv, Test::testGetField); // getfield
checkNonBlockingAction(recv, Test::testPutField); // putfield
checkNonBlockingAction(recv, Test::testInvokeVirtual); // invokevirtual
+
+ checkNonBlockingAction(Test::testNewInstanceBJNI); // new: NO BLOCKING: same thread: A being initialized, B fully initialized
+ checkNonBlockingAction(recv, Test::testGetFieldJNI); // getfield
+ checkNonBlockingAction(recv, Test::testPutFieldJNI); // putfield
+ checkNonBlockingAction(recv, Test::testInvokeVirtualJNI); // invokevirtual
}
static void warmup() {
--- a/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp Wed Jul 24 10:09:30 2019 +0800
+++ b/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp Wed Jul 24 10:50:40 2019 +0300
@@ -25,6 +25,17 @@
static jmethodID methodId;
+static jclass test_class_A;
+static jclass test_class_B;
+
+static jmethodID test_staticM_id;
+static jmethodID test_staticS_id;
+static jmethodID test_staticN_id;
+static jmethodID test_A_m_id;
+
+static jfieldID test_staticF_id;
+static jfieldID test_A_f_id;
+
extern "C" {
JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_init(JNIEnv* env, jclass cls) {
jclass runnable = env->FindClass("java/lang/Runnable");
@@ -36,7 +47,103 @@
return JNI_TRUE;
}
+ JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_00024Test_00024A_init(JNIEnv* env, jclass cls, jclass arg1) {
+ test_class_A = (jclass)env->NewGlobalRef(cls);
+ if (test_class_A == NULL) return JNI_FALSE;
+
+ test_class_B = (jclass)env->NewGlobalRef(arg1);
+ if (test_class_B == NULL) return JNI_FALSE;
+
+ test_staticM_id = env->GetStaticMethodID(test_class_A, "staticM", "(Ljava/lang/Runnable;)V");
+ if (test_staticM_id == NULL) return JNI_FALSE;
+
+ test_staticS_id = env->GetStaticMethodID(test_class_A, "staticS", "(Ljava/lang/Runnable;)V");
+ if (test_staticS_id == NULL) return JNI_FALSE;
+
+ test_staticN_id = env->GetStaticMethodID(test_class_A, "staticN", "(Ljava/lang/Runnable;)V");
+ if (test_staticN_id == NULL) return JNI_FALSE;
+
+ test_A_m_id = env->GetMethodID(test_class_A, "m", "()V");
+ if (test_A_m_id == NULL) return JNI_FALSE;
+
+ test_staticF_id = env->GetStaticFieldID(test_class_A, "staticF", "I");
+ if (test_staticF_id == NULL) return JNI_FALSE;
+
+ test_A_f_id = env->GetFieldID(test_class_A, "f", "I");
+ if (test_A_f_id == NULL) return JNI_FALSE;
+
+ return JNI_TRUE;
+ }
+
JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_00024A_staticN(JNIEnv* env, jclass cls, jobject action) {
env->CallVoidMethod(action, methodId);
}
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->CallStaticVoidMethod(test_class_A, test_staticM_id, action);
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticSyncJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->CallStaticVoidMethod(test_class_A, test_staticS_id, action);
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticNativeJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->CallStaticVoidMethod(test_class_A, test_staticN_id, action);
+ }
+
+ JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+ jint v = env->GetStaticIntField(test_class_A, test_staticF_id); // int v = A.staticF;
+ env->CallVoidMethod(action, methodId); // action.run();
+ return v;
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testPutStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+ env->SetStaticIntField(test_class_A, test_staticF_id, 1); // A.staticF = 1;
+ env->CallVoidMethod(action, methodId); // action.run();
+ }
+
+ JNIEXPORT jobject JNICALL Java_ClassInitBarrier_00024Test_testNewInstanceAJNI(JNIEnv* env, jclass cls, jobject action) {
+ jobject obj = env->AllocObject(test_class_A); // A obj = new A();
+ if (env->ExceptionOccurred()) {
+ return NULL;
+ } else if (obj == NULL) {
+ jclass errorClass = env->FindClass("java/lang/AssertionError");
+ int ret = env->ThrowNew(errorClass, "JNI: AllocObject: allocation failed, but no exception thrown");
+ return NULL;
+ }
+ env->CallVoidMethod(action, methodId); // action.run();
+ return obj;
+ }
+
+ JNIEXPORT jobject JNICALL Java_ClassInitBarrier_00024Test_testNewInstanceBJNI(JNIEnv* env, jclass cls, jobject action) {
+ jobject obj = env->AllocObject(test_class_B); // B obj = new B();
+ if (env->ExceptionOccurred()) {
+ return NULL;
+ } else if (obj == NULL) {
+ jclass errorClass = env->FindClass("java/lang/AssertionError");
+ int ret = env->ThrowNew(errorClass, "JNI: AllocObject: allocation failed, but no exception thrown");
+ return NULL;
+ }
+ env->CallVoidMethod(action, methodId); // action.run();
+ return obj;
+ }
+
+ JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetFieldJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+ jint v = env->GetIntField(recv, test_A_f_id); // int v = recv.f;
+ env->CallVoidMethod(action, methodId); // action.run();
+ return v;
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testPutFieldJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+ env->SetIntField(recv, test_A_f_id, 1); // A.staticF = 1;
+ env->CallVoidMethod(action, methodId); // action.run();
+ }
+
+ JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeVirtualJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+ env->CallVoidMethod(recv, test_A_m_id); // recv.m();
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ env->CallVoidMethod(action, methodId); // action.run();
+ }
}