1 /*
   2  * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * Copyright 2003 Wily Technology, Inc.
  28  */
  29 
  30 #include    <jni.h>
  31 #include    <jvmti.h>
  32 
  33 #include    "JPLISAssert.h"
  34 #include    "Utilities.h"
  35 #include    "JavaExceptions.h"
  36 
  37 /**
  38  * This module contains utility routines for manipulating Java throwables
  39  * and JNIEnv throwable state from native code.
  40  */
  41 
  42 static jthrowable   sFallbackInternalError  = NULL;
  43 
  44 /*
  45  * Local forward declarations.
  46  */
  47 
  48 /* insist on having a throwable. If we already have one, return it.
  49  * If not, map to fallback
  50  */
  51 jthrowable
  52 forceFallback(jthrowable potentialException);
  53 
  54 
  55 jthrowable
  56 forceFallback(jthrowable potentialException) {
  57     if ( potentialException == NULL ) {
  58         return sFallbackInternalError;
  59     }
  60     else {
  61         return potentialException;
  62     }
  63 }
  64 
  65 /**
  66  *  Returns true if it properly sets up a fallback exception
  67  */
  68 jboolean
  69 initializeFallbackError(JNIEnv* jnienv) {
  70     jplis_assert(isSafeForJNICalls(jnienv));
  71     sFallbackInternalError = createInternalError(jnienv, NULL);
  72     jplis_assert(isSafeForJNICalls(jnienv));
  73     return (sFallbackInternalError != NULL);
  74 }
  75 
  76 
  77 /*
  78  *  Map everything to InternalError.
  79  */
  80 jthrowable
  81 mapAllCheckedToInternalErrorMapper( JNIEnv *    jnienv,
  82                                     jthrowable  throwableToMap) {
  83     jthrowable  mappedThrowable = NULL;
  84     jstring     message         = NULL;
  85 
  86     jplis_assert(throwableToMap != NULL);
  87     jplis_assert(isSafeForJNICalls(jnienv));
  88     jplis_assert(!isUnchecked(jnienv, throwableToMap));
  89 
  90     message = getMessageFromThrowable(jnienv, throwableToMap);
  91     mappedThrowable = createInternalError(jnienv, message);
  92 
  93     jplis_assert(isSafeForJNICalls(jnienv));
  94     return mappedThrowable;
  95 }
  96 
  97 
  98 jboolean
  99 checkForThrowable(  JNIEnv*     jnienv) {
 100     return (*jnienv)->ExceptionCheck(jnienv);
 101 }
 102 
 103 jboolean
 104 isSafeForJNICalls(  JNIEnv * jnienv) {
 105     return !(*jnienv)->ExceptionCheck(jnienv);
 106 }
 107 
 108 
 109 void
 110 logThrowable(   JNIEnv * jnienv) {
 111     if ( checkForThrowable(jnienv) ) {
 112         (*jnienv)->ExceptionDescribe(jnienv);
 113     }
 114 }
 115 
 116 
 117 
 118 /**
 119  *  Creates an exception or error with the fully qualified classname (ie java/lang/Error)
 120  *  and message passed to its constructor
 121  */
 122 jthrowable
 123 createThrowable(    JNIEnv *        jnienv,
 124                     const char *    className,
 125                     jstring         message) {
 126     jthrowable  exception           = NULL;
 127     jmethodID   constructor         = NULL;
 128     jclass      exceptionClass      = NULL;
 129     jboolean    errorOutstanding    = JNI_FALSE;
 130 
 131     jplis_assert(className != NULL);
 132     jplis_assert(isSafeForJNICalls(jnienv));
 133 
 134     /* create new VMError with message from exception */
 135     exceptionClass = (*jnienv)->FindClass(jnienv, className);
 136     errorOutstanding = checkForAndClearThrowable(jnienv);
 137     jplis_assert(!errorOutstanding);
 138 
 139     if (!errorOutstanding) {
 140         constructor = (*jnienv)->GetMethodID(   jnienv,
 141                                                 exceptionClass,
 142                                                 "<init>",
 143                                                 "(Ljava/lang/String;)V");
 144         errorOutstanding = checkForAndClearThrowable(jnienv);
 145         jplis_assert(!errorOutstanding);
 146     }
 147 
 148     if (!errorOutstanding) {
 149         exception = (*jnienv)->NewObject(jnienv, exceptionClass, constructor, message);
 150         errorOutstanding = checkForAndClearThrowable(jnienv);
 151         jplis_assert(!errorOutstanding);
 152     }
 153 
 154     jplis_assert(isSafeForJNICalls(jnienv));
 155     return exception;
 156 }
 157 
 158 jthrowable
 159 createInternalError(JNIEnv * jnienv, jstring message) {
 160     return createThrowable( jnienv,
 161                             "java/lang/InternalError",
 162                             message);
 163 }
 164 
 165 jthrowable
 166 createThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
 167     const char * throwableClassName = NULL;
 168     const char * message            = NULL;
 169     jstring messageString           = NULL;
 170 
 171     switch ( errorCode ) {
 172         case JVMTI_ERROR_NULL_POINTER:
 173                 throwableClassName = "java/lang/NullPointerException";
 174                 break;
 175 
 176         case JVMTI_ERROR_ILLEGAL_ARGUMENT:
 177                 throwableClassName = "java/lang/IllegalArgumentException";
 178                 break;
 179 
 180         case JVMTI_ERROR_OUT_OF_MEMORY:
 181                 throwableClassName = "java/lang/OutOfMemoryError";
 182                 break;
 183 
 184         case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
 185                 throwableClassName = "java/lang/ClassCircularityError";
 186                 break;
 187 
 188         case JVMTI_ERROR_FAILS_VERIFICATION:
 189                 throwableClassName = "java/lang/VerifyError";
 190                 break;
 191 
 192         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
 193                 throwableClassName = "java/lang/UnsupportedOperationException";
 194                 message = "class redefinition failed: attempted to add a method";
 195                 break;
 196 
 197         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
 198                 throwableClassName = "java/lang/UnsupportedOperationException";
 199                 message = "class redefinition failed: attempted to change the schema (add/remove fields)";
 200                 break;
 201 
 202         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
 203                 throwableClassName = "java/lang/UnsupportedOperationException";
 204                 message = "class redefinition failed: attempted to change superclass or interfaces";
 205                 break;
 206 
 207         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
 208                 throwableClassName = "java/lang/UnsupportedOperationException";
 209                 message = "class redefinition failed: attempted to delete a method";
 210                 break;
 211 
 212         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
 213                 throwableClassName = "java/lang/UnsupportedOperationException";
 214                 message = "class redefinition failed: attempted to change the class modifiers";
 215                 break;
 216 
 217         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
 218                 throwableClassName = "java/lang/UnsupportedOperationException";
 219                 message = "class redefinition failed: attempted to change method modifiers";
 220                 break;
 221 
 222         case JVMTI_ERROR_UNSUPPORTED_VERSION:
 223                 throwableClassName = "java/lang/UnsupportedClassVersionError";
 224                 break;
 225 
 226         case JVMTI_ERROR_NAMES_DONT_MATCH:
 227                 throwableClassName = "java/lang/NoClassDefFoundError";
 228                 message = "class names don't match";
 229                 break;
 230 
 231         case JVMTI_ERROR_INVALID_CLASS_FORMAT:
 232                 throwableClassName = "java/lang/ClassFormatError";
 233                 break;
 234 
 235         case JVMTI_ERROR_UNMODIFIABLE_CLASS:
 236                 throwableClassName = "java/lang/instrument/UnmodifiableClassException";
 237                 break;
 238 
 239         case JVMTI_ERROR_INVALID_CLASS:
 240                 throwableClassName = "java/lang/InternalError";
 241                 message = "class redefinition failed: invalid class";
 242                 break;
 243 
 244         case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
 245                 throwableClassName = "java/lang/UnsupportedOperationException";
 246                 message = "unsupported operation";
 247                 break;
 248 
 249         case JVMTI_ERROR_INTERNAL:
 250         default:
 251                 throwableClassName = "java/lang/InternalError";
 252                 break;
 253         }
 254 
 255     if ( message != NULL ) {
 256         jboolean errorOutstanding;
 257 
 258         messageString = (*jnienv)->NewStringUTF(jnienv, message);
 259         errorOutstanding = checkForAndClearThrowable(jnienv);
 260         jplis_assert_msg(!errorOutstanding, "can't create exception java string");
 261     }
 262     return createThrowable( jnienv,
 263                             throwableClassName,
 264                             messageString);
 265 
 266 }
 267 
 268 
 269 /**
 270  *  Calls toString() on the given message which is the same call made by
 271  *  Exception when passed a throwable to its constructor
 272  */
 273 jstring
 274 getMessageFromThrowable(    JNIEnv*     jnienv,
 275                             jthrowable  exception) {
 276     jclass      exceptionClass      = NULL;
 277     jmethodID   method              = NULL;
 278     jstring     message             = NULL;
 279     jboolean    errorOutstanding    = JNI_FALSE;
 280 
 281     jplis_assert(isSafeForJNICalls(jnienv));
 282 
 283     /* call getMessage on exception */
 284     exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception);
 285     errorOutstanding = checkForAndClearThrowable(jnienv);
 286     jplis_assert(!errorOutstanding);
 287 
 288     if (!errorOutstanding) {
 289         method = (*jnienv)->GetMethodID(jnienv,
 290                                         exceptionClass,
 291                                         "toString",
 292                                         "()Ljava/lang/String;");
 293         errorOutstanding = checkForAndClearThrowable(jnienv);
 294         jplis_assert(!errorOutstanding);
 295     }
 296 
 297     if (!errorOutstanding) {
 298         message = (*jnienv)->CallObjectMethod(jnienv, exception, method);
 299         errorOutstanding = checkForAndClearThrowable(jnienv);
 300         jplis_assert(!errorOutstanding);
 301     }
 302 
 303     jplis_assert(isSafeForJNICalls(jnienv));
 304 
 305     return message;
 306 }
 307 
 308 
 309 /**
 310  *  Returns whether the exception given is an unchecked exception:
 311  *  a subclass of Error or RuntimeException
 312  */
 313 jboolean
 314 isUnchecked(    JNIEnv*     jnienv,
 315                 jthrowable  exception) {
 316     jboolean result = JNI_FALSE;
 317 
 318     jplis_assert(isSafeForJNICalls(jnienv));
 319     result =    (exception == NULL) ||
 320                 isInstanceofClassName(jnienv, exception, "java/lang/Error") ||
 321                 isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException");
 322     jplis_assert(isSafeForJNICalls(jnienv));
 323     return result;
 324 }
 325 
 326 /*
 327  *  Returns the current throwable, if any. Clears the throwable state.
 328  *  Clients can use this to preserve the current throwable state on the stack.
 329  */
 330 jthrowable
 331 preserveThrowable(JNIEnv * jnienv) {
 332     jthrowable result = (*jnienv)->ExceptionOccurred(jnienv);
 333     if ( result != NULL ) {
 334         (*jnienv)->ExceptionClear(jnienv);
 335     }
 336     return result;
 337 }
 338 
 339 /*
 340  *  Installs the supplied throwable into the JNIEnv if the throwable is not null.
 341  *  Clients can use this to preserve the current throwable state on the stack.
 342  */
 343 void
 344 restoreThrowable(   JNIEnv *    jnienv,
 345                     jthrowable  preservedException) {
 346     throwThrowable( jnienv,
 347                     preservedException);
 348     return;
 349 }
 350 
 351 void
 352 throwThrowable(     JNIEnv *    jnienv,
 353                     jthrowable  exception) {
 354     if ( exception != NULL ) {
 355         jint result = (*jnienv)->Throw(jnienv, exception);
 356         jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw");
 357     }
 358     return;
 359 }
 360 
 361 
 362 /*
 363  *  Always clears the JNIEnv throwable state. Returns true if an exception was present
 364  *  before the clearing operation.
 365  */
 366 jboolean
 367 checkForAndClearThrowable(  JNIEnv *    jnienv) {
 368     jboolean result = (*jnienv)->ExceptionCheck(jnienv);
 369     if ( result ) {
 370         (*jnienv)->ExceptionClear(jnienv);
 371     }
 372     return result;
 373 }
 374 
 375 /* creates a java.lang.InternalError and installs it into the JNIEnv */
 376 void
 377 createAndThrowInternalError(JNIEnv * jnienv) {
 378     jthrowable internalError = createInternalError( jnienv, NULL);
 379     throwThrowable(jnienv, forceFallback(internalError));
 380 }
 381 
 382 void
 383 createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
 384     jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
 385     throwThrowable(jnienv, forceFallback(throwable));
 386 }
 387 
 388 void
 389 mapThrownThrowableIfNecessary(  JNIEnv *                jnienv,
 390                                 CheckedExceptionMapper  mapper) {
 391     jthrowable  originalThrowable   = NULL;
 392     jthrowable  resultThrowable     = NULL;
 393 
 394     originalThrowable = preserveThrowable(jnienv);
 395 
 396     /* the throwable is now cleared, so JNI calls are safe */
 397     if ( originalThrowable != NULL ) {
 398         /* if there is an exception: we can just throw it if it is unchecked. If checked,
 399          * we need to map it (mapper is conditional, will vary by usage, hence the callback)
 400          */
 401         if ( isUnchecked(jnienv, originalThrowable) ) {
 402             resultThrowable = originalThrowable;
 403         }
 404         else {
 405             resultThrowable = (*mapper) (jnienv, originalThrowable);
 406         }
 407     }
 408 
 409     /* re-establish the correct throwable */
 410     if ( resultThrowable != NULL ) {
 411         throwThrowable(jnienv, forceFallback(resultThrowable));
 412     }
 413 
 414 }