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 42static 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 */ 51jthrowable 52forceFallback(jthrowable potentialException); 53 54 55jthrowable 56forceFallback(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 */ 68jboolean 69initializeFallbackError(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 */ 80jthrowable 81mapAllCheckedToInternalErrorMapper( 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 98jboolean 99checkForThrowable( JNIEnv* jnienv) { 100 return (*jnienv)->ExceptionCheck(jnienv); 101} 102 103jboolean 104isSafeForJNICalls( JNIEnv * jnienv) { 105 return !(*jnienv)->ExceptionCheck(jnienv); 106} 107 108 109void 110logThrowable( 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 */ 122jthrowable 123createThrowable( 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 158jthrowable 159createInternalError(JNIEnv * jnienv, jstring message) { 160 return createThrowable( jnienv, 161 "java/lang/InternalError", 162 message); 163} 164 165jthrowable 166createThrowableFromJVMTIErrorCode(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 */ 273jstring 274getMessageFromThrowable( 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 */ 313jboolean 314isUnchecked( 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 */ 330jthrowable 331preserveThrowable(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 */ 343void 344restoreThrowable( JNIEnv * jnienv, 345 jthrowable preservedException) { 346 throwThrowable( jnienv, 347 preservedException); 348 return; 349} 350 351void 352throwThrowable( 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 */ 366jboolean 367checkForAndClearThrowable( 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 */ 376void 377createAndThrowInternalError(JNIEnv * jnienv) { 378 jthrowable internalError = createInternalError( jnienv, NULL); 379 throwThrowable(jnienv, forceFallback(internalError)); 380} 381 382void 383createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) { 384 jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode); 385 throwThrowable(jnienv, forceFallback(throwable)); 386} 387 388void 389mapThrownThrowableIfNecessary( 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} 415