8210842: Handle JNIGlobalRefLocker.cpp
authorjcbeyler
Fri, 28 Sep 2018 13:01:28 -0700
changeset 51957 2712735bc434
parent 51956 f1f7ff620f83
child 51958 53a4760e9fcc
8210842: Handle JNIGlobalRefLocker.cpp Summary: Add checking for JNI calls via a new ExceptionCheckingJniEnv Reviewed-by: sspitsyn, amenkov, dholmes, mikael
test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/JNIGlobalRefLocker.cpp
test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/libJNIGlobalRefLocker.cpp
test/hotspot/jtreg/vmTestbase/nsk/share/jni/ExceptionCheckingJniEnv.cpp
test/hotspot/jtreg/vmTestbase/nsk/share/jni/ExceptionCheckingJniEnv.hpp
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/JNIGlobalRefLocker.cpp	Fri Sep 28 14:31:36 2018 -0400
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/JNIGlobalRefLocker.cpp	Fri Sep 28 13:01:28 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	Fri Sep 28 14:31:36 2018 -0400
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/lock/jniref/libJNIGlobalRefLocker.cpp	Fri Sep 28 13:01:28 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	Fri Sep 28 13:01:28 2018 -0700
@@ -0,0 +1,93 @@
+/*
+ * 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 <string>
+
+#include "ExceptionCheckingJniEnv.hpp"
+
+namespace {
+
+template<class T = void*>
+class JNIVerifier {
+ public:
+  JNIVerifier(ExceptionCheckingJniEnv *env, const std::string& base_msg)
+      : _env(env), _base_msg(base_msg) {
+  }
+
+  ~JNIVerifier() {
+    JNIEnv* jni_env = _env->GetJNIEnv();
+    if (jni_env->ExceptionCheck()) {
+      _env->HandleError(_base_msg.c_str());
+    } else {
+      if (!_return_error.empty()) {
+        const std::string msg = _base_msg + ":" + _return_error;
+        _env->HandleError(msg.c_str());
+      }
+    }
+  }
+
+  T ResultNotNull(T ptr) {
+    if (ptr == NULL) {
+      _return_error = "Return is NULL";
+    }
+    return ptr;
+  }
+
+ private:
+  ExceptionCheckingJniEnv* _env;
+  std::string _base_msg;
+  std::string _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	Fri Sep 28 13:01:28 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