1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 #include <stdlib.h>
  26 #include <iostream>
  27 
  28 #include "ExceptionCheckingJniEnv.hpp"
  29 #include "nsk_tools.h"
  30 
  31 namespace {
  32 
  33 static const char* get_basename(const char* fullname) {
  34   const char* p;
  35   const char* base = fullname;;
  36 
  37   if (fullname == NULL) {
  38     return NULL;
  39   }
  40 
  41   for (p = fullname; *p != '\0'; p++) {
  42     if (*p == '/' || *p == '\\') {
  43       base = p + 1;
  44     }
  45   }
  46   return base;
  47 }
  48 
  49 template<class T = void*>
  50 class JNIVerifier {
  51  public:
  52   JNIVerifier(ExceptionCheckingJniEnv *env, const char* base_msg,
  53               int line, const char* file)
  54       : _env(env), _base_message(base_msg), _error_message(NULL),
  55         _line(line), _file(get_basename(file)) {
  56   }
  57 
  58   // Until C++11 is supported, we have to write multiple template constructors.
  59   template <typename U>
  60   JNIVerifier(ExceptionCheckingJniEnv *env, const char* base_msg,
  61               U parameter,
  62               int line, const char* file)
  63       : _env(env), _base_message(base_msg), _error_message(NULL),
  64         _line(line), _file(get_basename(file)) {
  65           PrintPreCall(parameter);
  66   }
  67 
  68   template <typename U, typename V>
  69   JNIVerifier(ExceptionCheckingJniEnv *env, const char* base_msg,
  70               U parameter1,
  71               V parameter2,
  72               int line, const char* file)
  73       : _env(env), _base_message(base_msg), _error_message(NULL),
  74         _line(line), _file(get_basename(file)) {
  75           PrintPreCall(parameter1, parameter2);
  76   }
  77 
  78   template <typename U, typename V, typename W>
  79   JNIVerifier(ExceptionCheckingJniEnv *env, const char* base_msg,
  80               U parameter1, V parameter2, W parameter3,
  81               int line, const char* file)
  82       : _env(env), _base_message(base_msg), _error_message(NULL),
  83         _line(line), _file(get_basename(file)) {
  84           PrintPreCall(parameter1, parameter2, parameter3);
  85   }
  86 
  87   ~JNIVerifier() {
  88     PrintPostCall();
  89 
  90     JNIEnv* jni_env = _env->GetJNIEnv();
  91     if (jni_env->ExceptionCheck()) {
  92       _error_message = "internal error";
  93     }
  94 
  95     if (_error_message != NULL) {
  96       GenerateErrorMessage();
  97     }
  98   }
  99 
 100   void GenerateErrorMessage() {
 101     int len = snprintf(NULL, 0, "JNI method %s : %s from %s:%d", _base_message, _error_message,
 102                        _file, _line) + 1;
 103 
 104     if (len <= 0) {
 105       _env->HandleError(_error_message);
 106       return;
 107     }
 108 
 109     char* full_message = (char*) malloc(len);
 110     if (full_message == NULL) {
 111       _env->HandleError(_error_message);
 112       return;
 113     }
 114 
 115     snprintf(full_message, len, "JNI method %s : %s from %s:%d", _base_message, _error_message,
 116              _file, _line);
 117 
 118     _env->HandleError(full_message);
 119     free(full_message);
 120   }
 121 
 122   T ResultNotNull(T ptr) {
 123     if (ptr == NULL) {
 124       _error_message = "Return is NULL";
 125     }
 126     return ptr;
 127   }
 128 
 129   T ResultIsZero(T value) {
 130     if (value != 0) {
 131       _error_message = "Return is not zero";
 132     }
 133     return value;
 134   }
 135 
 136   void PrintPreCallHeader() {
 137     if (!nsk_getVerboseMode()) {
 138       return;
 139     }
 140 
 141     std::cout << ">> Calling JNI method " << _base_message << " from " << _file
 142               << ":" << _line << std::endl;
 143     std::cout << ">> Calling with these parameter(s):" << std::endl;
 144   }
 145 
 146   // Until C++11 is supported, we have to write multiple PrintPreCall.
 147   template<class U>
 148   void PrintPreCall(U first_parameter) {
 149     if (!nsk_getVerboseMode()) {
 150       return;
 151     }
 152 
 153     PrintPreCallHeader();
 154     std::cout << "\t" << first_parameter << std::endl;
 155   }
 156 
 157   template<class U, class V>
 158   void PrintPreCall(U parameter1, V parameter2) {
 159     if (!nsk_getVerboseMode()) {
 160       return;
 161     }
 162 
 163     PrintPreCallHeader();
 164     std::cout << "\t" << parameter1 << std::endl;
 165     std::cout << "\t" << parameter2 << std::endl;
 166   }
 167 
 168   template<class U, class V, class W>
 169   void PrintPreCall(U parameter1, V parameter2, W parameter3) {
 170     if (!nsk_getVerboseMode()) {
 171       return;
 172     }
 173 
 174     PrintPreCallHeader();
 175     std::cout << "\t" << parameter1 << std::endl;
 176     std::cout << "\t" << parameter2 << std::endl;
 177     std::cout << "\t" << parameter3 << std::endl;
 178   }
 179 
 180   void PrintPostCall() {
 181     if (!nsk_getVerboseMode()) {
 182       return;
 183     }
 184 
 185     std::cout << "<< Called JNI method " << _base_message << " from " << _file
 186               << ":" << _line << std::endl;
 187   }
 188 
 189  private:
 190   ExceptionCheckingJniEnv* _env;
 191   const char* const _base_message;
 192   const char* _error_message;
 193   int _line;
 194   const char* const _file;
 195 };
 196 
 197 }
 198 
 199 jclass ExceptionCheckingJniEnv::FindClass(const char *class_name,
 200                                           int line, const char* file_name) {
 201   JNIVerifier<jclass> marker(this, "FindClass", class_name, line, file_name);
 202   return marker.ResultNotNull(_jni_env->FindClass(class_name));
 203 }
 204 
 205 jint ExceptionCheckingJniEnv::RegisterNatives(jclass clazz,
 206                                               const JNINativeMethod *methods,
 207                                               jint nMethods,
 208                                               int line,
 209                                               const char* file_name) {
 210   JNIVerifier<jint> marker(this, "RegisterNatives", methods, nMethods, line, file_name);
 211   return marker.ResultIsZero(_jni_env->RegisterNatives(clazz, methods, nMethods));
 212 }
 213 
 214 jclass ExceptionCheckingJniEnv::GetObjectClass(jobject obj, int line,
 215                                                const char* file_name) {
 216   JNIVerifier<jclass> marker(this, "GetObjectClass", obj, line, file_name);
 217   return marker.ResultNotNull(_jni_env->GetObjectClass(obj));
 218 }
 219 
 220 jfieldID ExceptionCheckingJniEnv::GetStaticFieldID(jclass klass, const char *name,
 221                                                    const char* type,
 222                                                    int line, const char* file_name) {
 223   JNIVerifier<jfieldID> marker(this, "GetStaticFieldID", klass, name, type,
 224                                line, file_name);
 225   return marker.ResultNotNull(_jni_env->GetStaticFieldID(klass, name, type));
 226 }
 227 
 228 jfieldID ExceptionCheckingJniEnv::GetFieldID(jclass klass, const char *name,
 229                                              const char* type,
 230                                              int line, const char* file_name) {
 231   JNIVerifier<jfieldID> marker(this, "GetFieldID", klass, name, type, line, file_name);
 232   return marker.ResultNotNull(_jni_env->GetFieldID(klass, name, type));
 233 }
 234 
 235 jobject ExceptionCheckingJniEnv::GetStaticObjectField(jclass klass, jfieldID field,
 236                                                       int line, const char* file_name) {
 237   JNIVerifier<jobject> marker(this, "GetStaticObjectField", klass, field,
 238                               line, file_name);
 239   return marker.ResultNotNull(_jni_env->GetStaticObjectField(klass, field));
 240 }
 241 
 242 jobject ExceptionCheckingJniEnv::GetObjectField(jobject obj, jfieldID field,
 243                                                 int line, const char* file_name) {
 244   JNIVerifier<jobject> marker(this, "GetObjectField", obj, field, line, file_name);
 245   return marker.ResultNotNull(_jni_env->GetObjectField(obj, field));
 246 }
 247 
 248 void ExceptionCheckingJniEnv::SetObjectField(jobject obj, jfieldID field, jobject value,
 249                                              int line, const char* file_name) {
 250   JNIVerifier<> marker(this, "SetObjectField", obj, field, value, line, file_name);
 251   _jni_env->SetObjectField(obj, field, value);
 252 }
 253 
 254 jobject ExceptionCheckingJniEnv::NewGlobalRef(jobject obj, int line, const char* file_name) {
 255   JNIVerifier<jobject> marker(this, "NewGlobalRef", obj, line, file_name);
 256   return marker.ResultNotNull(_jni_env->NewGlobalRef(obj));
 257 }
 258 
 259 void ExceptionCheckingJniEnv::DeleteGlobalRef(jobject obj, int line, const char* file_name) {
 260   JNIVerifier<> marker(this, "DeleteGlobalRef", obj, line, file_name);
 261   _jni_env->DeleteGlobalRef(obj);
 262 }
 263 
 264 jobject ExceptionCheckingJniEnv::NewLocalRef(jobject obj, int line, const char* file_name) {
 265   JNIVerifier<jobject> marker(this, "NewLocalRef", obj, line, file_name);
 266   return marker.ResultNotNull(_jni_env->NewLocalRef(obj));
 267 }
 268 
 269 void ExceptionCheckingJniEnv::DeleteLocalRef(jobject obj, int line, const char* file_name) {
 270   JNIVerifier<> marker(this, "DeleteLocalRef", obj, line, file_name);
 271   _jni_env->DeleteLocalRef(obj);
 272 }
 273 
 274 jweak ExceptionCheckingJniEnv::NewWeakGlobalRef(jobject obj, int line, const char* file_name) {
 275   JNIVerifier<jweak> marker(this, "NewWeakGlobalRef", obj, line, file_name);
 276   return marker.ResultNotNull(_jni_env->NewWeakGlobalRef(obj));
 277 }
 278 
 279 void ExceptionCheckingJniEnv::DeleteWeakGlobalRef(jweak weak_ref, int line, const char* file_name) {
 280   JNIVerifier<> marker(this, "DeleteWeakGlobalRef", weak_ref, line, file_name);
 281   _jni_env->DeleteWeakGlobalRef(weak_ref);
 282 }
 283 
 284 jsize ExceptionCheckingJniEnv::GetArrayLength(jarray array, int line, const char* file_name) {
 285   JNIVerifier<> marker(this, "GetArrayLength", array, line, file_name);
 286   return _jni_env->GetArrayLength(array);
 287 }
 288 
 289 jsize ExceptionCheckingJniEnv::GetStringLength(jstring str, int line, const char* file_name) {
 290   JNIVerifier<> marker(this, "GetStringLength", str, line, file_name);
 291   return _jni_env->GetStringLength(str);
 292 }
 293 
 294 void* ExceptionCheckingJniEnv::GetPrimitiveArrayCritical(jarray array, jboolean* is_copy,
 295                                                          int line, const char* file_name) {
 296   JNIVerifier<> marker(this, "GetPrimitiveArrayCritical", array, is_copy, line, file_name);
 297   return marker.ResultNotNull(_jni_env->GetPrimitiveArrayCritical(array, is_copy));
 298 }
 299 
 300 void ExceptionCheckingJniEnv::ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode,
 301                                                             int line, const char* file_name) {
 302   JNIVerifier<> marker(this, "ReleasePrimitiveArrayCritical", array, carray, mode,
 303                        line, file_name);
 304   _jni_env->ReleasePrimitiveArrayCritical(array, carray, mode);
 305 }
 306 
 307 const jchar* ExceptionCheckingJniEnv::GetStringCritical(jstring str, jboolean* is_copy,
 308                                                         int line, const char* file_name) {
 309   JNIVerifier<const jchar*> marker(this, "GetPrimitiveArrayCritical", str, is_copy,
 310                                    line, file_name);
 311   return marker.ResultNotNull(_jni_env->GetStringCritical(str, is_copy));
 312 }
 313 
 314 void ExceptionCheckingJniEnv::ReleaseStringCritical(jstring str, const jchar* carray,
 315                                                     int line, const char* file_name) {
 316   JNIVerifier<> marker(this, "ReleaseStringCritical", str, carray, line, file_name);
 317   _jni_env->ReleaseStringCritical(str, carray);
 318 }
 319 
 320 jbyte* ExceptionCheckingJniEnv::GetByteArrayElements(jbyteArray array, jboolean* is_copy,
 321                                                    int line, const char* file_name) {
 322   JNIVerifier<jbyte*> marker(this, "GetByteArrayElements", array, is_copy, line, file_name);
 323   return marker.ResultNotNull(_jni_env->GetByteArrayElements(array, is_copy));
 324 }
 325 
 326 void ExceptionCheckingJniEnv::ReleaseByteArrayElements(jbyteArray array, jbyte* byte_array, jint mode,
 327                                                        int line, const char* file_name) {
 328   JNIVerifier<> marker(this, "ReleaseByteArrayElements", array, byte_array, mode,
 329                        line, file_name);
 330   _jni_env->ReleaseByteArrayElements(array, byte_array, mode);
 331 }
 332 
 333 jmethodID ExceptionCheckingJniEnv::GetMethodID(jclass klass, const char* name, const char* sig,
 334                                                int line, const char* file_name) {
 335   JNIVerifier<jmethodID> marker(this, "GetMethodID", klass, name, sig, line, file_name);
 336   return marker.ResultNotNull(_jni_env->GetMethodID(klass, name, sig));
 337 }
 338 
 339 jobject ExceptionCheckingJniEnv::NewObject(jclass klass, jmethodID methodID,
 340                                            int line, const char* file_name, ...) {
 341   // In the case of NewObject, we miss the extra arguments passed to NewObject sadly.
 342   JNIVerifier<jobject> marker(this, "NewObject", klass, methodID, line, file_name);
 343 
 344   va_list args;
 345   va_start(args, file_name);
 346   jobject result = marker.ResultNotNull(_jni_env->NewObjectV(klass, methodID, args));
 347   va_end(args);
 348   return result;
 349 }