8211432: [REDO] Handle JNIGlobalRefLocker.cpp
Summary: Adding a JNI verification wrapper for tests
Reviewed-by: amenkov, sspitsyn, phh
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/JNIGlobalRefLocker.cpp Thu Oct 11 11:31:37 2018 -0400
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/JNIGlobalRefLocker.cpp Thu Oct 11 09:30:10 2018 -0700
@@ -20,10 +20,12 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
#include <jni.h>
#include <stdio.h>
#include <time.h>
#include "jni_tools.h"
+#include "ExceptionCheckingJniEnv.hpp"
extern "C" {
@@ -35,28 +37,18 @@
* Signature: (JJ)V
*/
JNIEXPORT void JNICALL Java_nsk_share_gc_lock_jniref_JNIGlobalRefLocker_criticalNative
- (JNIEnv *env, jobject o, jlong enterTime, jlong sleepTime) {
+ (JNIEnv *jni_env, jobject o, jlong enterTime, jlong sleepTime) {
+ ExceptionCheckingJniEnvPtr env(jni_env);
+
jobject obj;
jobject gref;
time_t start_time, current_time;
if (objFieldId == NULL) {
jclass klass = env->GetObjectClass(o);
- if (klass == NULL) {
- printf("Error: GetObjectClass returned NULL\n");
- return;
- }
objFieldId = env->GetFieldID(klass, "obj", "Ljava/lang/Object;");
- if (objFieldId == NULL) {
- printf("Error: GetFieldID returned NULL\n");
- return;
- }
}
obj = env->GetObjectField(o, objFieldId);
- if (obj == NULL) {
- printf("Error: GetObjectField returned NULL\n");
- return;
- }
env->SetObjectField(o, objFieldId, NULL);
start_time = time(NULL);
enterTime /= 1000;
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/libJNIGlobalRefLocker.cpp Thu Oct 11 11:31:37 2018 -0400
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/libJNIGlobalRefLocker.cpp Thu Oct 11 09:30:10 2018 -0700
@@ -24,3 +24,4 @@
#include "JNIGlobalRefLocker.cpp"
#include "jni_tools.cpp"
#include "nsk_tools.cpp"
+#include "ExceptionCheckingJniEnv.cpp"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jni/ExceptionCheckingJniEnv.cpp Thu Oct 11 09:30:10 2018 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ExceptionCheckingJniEnv.hpp"
+
+namespace {
+
+template<class T = void*>
+class JNIVerifier {
+ public:
+ JNIVerifier(ExceptionCheckingJniEnv *env, const char* base_msg)
+ : _env(env), _base_msg(base_msg), _return_error(NULL) {
+ }
+
+ ~JNIVerifier() {
+ JNIEnv* jni_env = _env->GetJNIEnv();
+ if (jni_env->ExceptionCheck()) {
+ _env->HandleError(_base_msg);
+ return;
+ }
+
+ if (_return_error != NULL) {
+ ProcessReturnError();
+ }
+ }
+
+ void ProcessReturnError() {
+ int len = snprintf(NULL, 0, "%s : %s", _base_msg, _return_error) + 1;
+
+ if (len <= 0) {
+ _env->HandleError(_return_error);
+ return;
+ }
+
+ char* full_message = (char*) malloc(len);
+ if (full_message == NULL) {
+ _env->HandleError(_return_error);
+ return;
+ }
+
+ snprintf(full_message, len, "%s : %s", _base_msg, _return_error);
+
+ _env->HandleError(full_message);
+ free(full_message);
+ }
+
+ T ResultNotNull(T ptr) {
+ if (ptr == NULL) {
+ _return_error = "Return is NULL";
+ }
+ return ptr;
+ }
+
+ private:
+ ExceptionCheckingJniEnv* _env;
+ const char* const _base_msg;
+ const char* _return_error;
+};
+
+}
+
+jclass ExceptionCheckingJniEnv::GetObjectClass(jobject obj) {
+ JNIVerifier<jclass> marker(this, "GetObjectClass");
+ return marker.ResultNotNull(_jni_env->GetObjectClass(obj));
+}
+
+jfieldID ExceptionCheckingJniEnv::GetFieldID(jclass klass, const char *name, const char* type) {
+ JNIVerifier<jfieldID> marker(this, "GetObjectClass");
+ return marker.ResultNotNull(_jni_env->GetFieldID(klass, name, type));
+}
+
+jobject ExceptionCheckingJniEnv::GetObjectField(jobject obj, jfieldID field) {
+ JNIVerifier<jobject> marker(this, "GetObjectField");
+ return marker.ResultNotNull(_jni_env->GetObjectField(obj, field));
+}
+
+void ExceptionCheckingJniEnv::SetObjectField(jobject obj, jfieldID field, jobject value) {
+ JNIVerifier<> marker(this, "SetObjectField");
+ _jni_env->SetObjectField(obj, field, value);
+}
+
+jobject ExceptionCheckingJniEnv::NewGlobalRef(jobject obj) {
+ JNIVerifier<jobject> marker(this, "GetObjectField");
+ return marker.ResultNotNull(_jni_env->NewGlobalRef(obj));
+}
+
+void ExceptionCheckingJniEnv::DeleteGlobalRef(jobject obj) {
+ JNIVerifier<> marker(this, "DeleteGlobalRef");
+ _jni_env->DeleteGlobalRef(obj);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jni/ExceptionCheckingJniEnv.hpp Thu Oct 11 09:30:10 2018 -0700
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+#ifndef NSK_EXCEPTIONCHECKINGJNIENV_DEFINED
+#define NSK_EXCEPTIONCHECKINGJNIENV_DEFINED
+
+#include <jni.h>
+
+/**
+ * ExceptionCheckingJniEnv wraps around the JNIEnv data structure and
+ * methods to enable automatic exception checking. This allows test writers
+ * and readers to concentrate on what the test is to do and leave the
+ * error checking and throwing to this data structure and subsystem.
+ *
+ * For example:
+ *
+ * ... JNIEnv* env ...
+ * jclass klass = env->GetObjectClass(o);
+ * if (klass == NULL) {
+ * printf("Error: GetObjectClass returned NULL\n");
+ * return;
+ * }
+ * if (env->ExceptionCheck()) {
+ * ...
+ * }
+ *
+ * Can be simplified to:
+ * ... ExceptionCheckingJniEnv* env ...
+ * jclass klass = env->GetObjectClass(o);
+ *
+ * Where now the JNI Exception checking and the NULL return checking are done
+ * internally and will perform whatever action the ErrorHandler requires.
+ *
+ * By default, the error handler describes the exception via the JNI
+ * ExceptionDescribe method and calls FatalError.
+ *
+ * Note: at a future date, this will also include the tracing mechanism done in
+ * NSK_VERIFY, which will thus embed its logic into the ExceptionCheckingJniEnv
+ * and clearing that up for the code readers and writers.
+ */
+class ExceptionCheckingJniEnv {
+ public:
+ // JNIEnv API redefinitions.
+ jfieldID GetFieldID(jclass klass, const char *name, const char* type);
+ jclass GetObjectClass(jobject obj);
+ jobject GetObjectField(jobject obj, jfieldID field);
+ void SetObjectField(jobject obj, jfieldID field, jobject value);
+
+ jobject NewGlobalRef(jobject obj);
+ void DeleteGlobalRef(jobject obj);
+
+ // ExceptionCheckingJniEnv methods.
+ JNIEnv* GetJNIEnv() {
+ return _jni_env;
+ }
+
+ void HandleError(const char* msg) {
+ if (_error_handler) {
+ _error_handler(_jni_env, msg);
+ }
+ }
+
+ typedef void (*ErrorHandler)(JNIEnv* env, const char* error_message);
+
+ static void FatalError(JNIEnv* env, const char* message) {
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ }
+ env->FatalError(message);
+ }
+
+ ExceptionCheckingJniEnv(JNIEnv* jni_env, ErrorHandler error_handler) :
+ _jni_env(jni_env), _error_handler(error_handler) {}
+
+ private:
+ JNIEnv* _jni_env;
+ ErrorHandler _error_handler;
+};
+
+// We cannot use unique_ptr due to this being gnu98++, so use this instead:
+class ExceptionCheckingJniEnvPtr {
+ private:
+ ExceptionCheckingJniEnv _env;
+
+ public:
+ ExceptionCheckingJniEnv* operator->() {
+ return &_env;
+ }
+
+ ExceptionCheckingJniEnvPtr(
+ JNIEnv* jni_env,
+ ExceptionCheckingJniEnv::ErrorHandler error_handler = ExceptionCheckingJniEnv::FatalError) :
+ _env(jni_env, error_handler) {
+ }
+};
+
+#endif