1 /* 2 * Copyright (c) 2003, 2018, 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_CLASS_ATTRIBUTE_CHANGED: 218 throwableClassName = "java/lang/UnsupportedOperationException"; 219 message = "class redefinition failed: attempted to change the class NestHost or NestMembers attribute"; 220 break; 221 222 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED: 223 throwableClassName = "java/lang/UnsupportedOperationException"; 224 message = "class redefinition failed: attempted to change method modifiers"; 225 break; 226 227 case JVMTI_ERROR_UNSUPPORTED_VERSION: 228 throwableClassName = "java/lang/UnsupportedClassVersionError"; 229 break; 230 231 case JVMTI_ERROR_NAMES_DONT_MATCH: 232 throwableClassName = "java/lang/NoClassDefFoundError"; 233 message = "class names don't match"; 234 break; 235 236 case JVMTI_ERROR_INVALID_CLASS_FORMAT: 237 throwableClassName = "java/lang/ClassFormatError"; 238 break; 239 240 case JVMTI_ERROR_UNMODIFIABLE_CLASS: 241 throwableClassName = "java/lang/instrument/UnmodifiableClassException"; 242 break; 243 244 case JVMTI_ERROR_INVALID_CLASS: 245 throwableClassName = "java/lang/InternalError"; 246 message = "class redefinition failed: invalid class"; 247 break; 248 249 case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED: 250 throwableClassName = "java/lang/UnsupportedOperationException"; 251 message = "unsupported operation"; 252 break; 253 254 case JVMTI_ERROR_INTERNAL: 255 default: 256 throwableClassName = "java/lang/InternalError"; 257 break; 258 } 259 260 if ( message != NULL ) { 261 jboolean errorOutstanding; 262 263 messageString = (*jnienv)->NewStringUTF(jnienv, message); 264 errorOutstanding = checkForAndClearThrowable(jnienv); 265 jplis_assert_msg(!errorOutstanding, "can't create exception java string"); 266 } 267 return createThrowable( jnienv, 268 throwableClassName, 269 messageString); 270 271 } 272 273 274 /** 275 * Calls toString() on the given message which is the same call made by 276 * Exception when passed a throwable to its constructor 277 */ 278 jstring 279 getMessageFromThrowable( JNIEnv* jnienv, 280 jthrowable exception) { 281 jclass exceptionClass = NULL; 282 jmethodID method = NULL; 283 jstring message = NULL; 284 jboolean errorOutstanding = JNI_FALSE; 285 286 jplis_assert(isSafeForJNICalls(jnienv)); 287 288 /* call getMessage on exception */ 289 exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception); 290 errorOutstanding = checkForAndClearThrowable(jnienv); 291 jplis_assert(!errorOutstanding); 292 293 if (!errorOutstanding) { 294 method = (*jnienv)->GetMethodID(jnienv, 295 exceptionClass, 296 "toString", 297 "()Ljava/lang/String;"); 298 errorOutstanding = checkForAndClearThrowable(jnienv); 299 jplis_assert(!errorOutstanding); 300 } 301 302 if (!errorOutstanding) { 303 message = (*jnienv)->CallObjectMethod(jnienv, exception, method); 304 errorOutstanding = checkForAndClearThrowable(jnienv); 305 jplis_assert(!errorOutstanding); 306 } 307 308 jplis_assert(isSafeForJNICalls(jnienv)); 309 310 return message; 311 } 312 313 314 /** 315 * Returns whether the exception given is an unchecked exception: 316 * a subclass of Error or RuntimeException 317 */ 318 jboolean 319 isUnchecked( JNIEnv* jnienv, 320 jthrowable exception) { 321 jboolean result = JNI_FALSE; 322 323 jplis_assert(isSafeForJNICalls(jnienv)); 324 result = (exception == NULL) || 325 isInstanceofClassName(jnienv, exception, "java/lang/Error") || 326 isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException"); 327 jplis_assert(isSafeForJNICalls(jnienv)); 328 return result; 329 } 330 331 /* 332 * Returns the current throwable, if any. Clears the throwable state. 333 * Clients can use this to preserve the current throwable state on the stack. 334 */ 335 jthrowable 336 preserveThrowable(JNIEnv * jnienv) { 337 jthrowable result = (*jnienv)->ExceptionOccurred(jnienv); 338 if ( result != NULL ) { 339 (*jnienv)->ExceptionClear(jnienv); 340 } 341 return result; 342 } 343 344 /* 345 * Installs the supplied throwable into the JNIEnv if the throwable is not null. 346 * Clients can use this to preserve the current throwable state on the stack. 347 */ 348 void 349 restoreThrowable( JNIEnv * jnienv, 350 jthrowable preservedException) { 351 throwThrowable( jnienv, 352 preservedException); 353 return; 354 } 355 356 void 357 throwThrowable( JNIEnv * jnienv, 358 jthrowable exception) { 359 if ( exception != NULL ) { 360 jint result = (*jnienv)->Throw(jnienv, exception); 361 jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw"); 362 } 363 return; 364 } 365 366 367 /* 368 * Always clears the JNIEnv throwable state. Returns true if an exception was present 369 * before the clearing operation. 370 */ 371 jboolean 372 checkForAndClearThrowable( JNIEnv * jnienv) { 373 jboolean result = (*jnienv)->ExceptionCheck(jnienv); 374 if ( result ) { 375 (*jnienv)->ExceptionClear(jnienv); 376 } 377 return result; 378 } 379 380 /* creates a java.lang.InternalError and installs it into the JNIEnv */ 381 void 382 createAndThrowInternalError(JNIEnv * jnienv) { 383 jthrowable internalError = createInternalError( jnienv, NULL); 384 throwThrowable(jnienv, forceFallback(internalError)); 385 } 386 387 void 388 createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) { 389 jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode); 390 throwThrowable(jnienv, forceFallback(throwable)); 391 } 392 393 void 394 mapThrownThrowableIfNecessary( JNIEnv * jnienv, 395 CheckedExceptionMapper mapper) { 396 jthrowable originalThrowable = NULL; 397 jthrowable resultThrowable = NULL; 398 399 originalThrowable = preserveThrowable(jnienv); 400 401 /* the throwable is now cleared, so JNI calls are safe */ 402 if ( originalThrowable != NULL ) { 403 /* if there is an exception: we can just throw it if it is unchecked. If checked, 404 * we need to map it (mapper is conditional, will vary by usage, hence the callback) 405 */ 406 if ( isUnchecked(jnienv, originalThrowable) ) { 407 resultThrowable = originalThrowable; 408 } 409 else { 410 resultThrowable = (*mapper) (jnienv, originalThrowable); 411 } 412 } 413 414 /* re-establish the correct throwable */ 415 if ( resultThrowable != NULL ) { 416 throwThrowable(jnienv, forceFallback(resultThrowable)); 417 } 418 419 }