Path: blob/master/src/java.instrument/share/native/libinstrument/JavaExceptions.c
41149 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 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 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* Copyright 2003 Wily Technology, Inc.27*/2829#include <jni.h>30#include <jvmti.h>3132#include "JPLISAssert.h"33#include "Utilities.h"34#include "JavaExceptions.h"3536/**37* This module contains utility routines for manipulating Java throwables38* and JNIEnv throwable state from native code.39*/4041static jthrowable sFallbackInternalError = NULL;4243/*44* Local forward declarations.45*/4647/* insist on having a throwable. If we already have one, return it.48* If not, map to fallback49*/50jthrowable51forceFallback(jthrowable potentialException);525354jthrowable55forceFallback(jthrowable potentialException) {56if ( potentialException == NULL ) {57return sFallbackInternalError;58}59else {60return potentialException;61}62}6364/**65* Returns true if it properly sets up a fallback exception66*/67jboolean68initializeFallbackError(JNIEnv* jnienv) {69jplis_assert(isSafeForJNICalls(jnienv));70sFallbackInternalError = createInternalError(jnienv, NULL);71jplis_assert(isSafeForJNICalls(jnienv));72return (sFallbackInternalError != NULL);73}747576/*77* Map everything to InternalError.78*/79jthrowable80mapAllCheckedToInternalErrorMapper( JNIEnv * jnienv,81jthrowable throwableToMap) {82jthrowable mappedThrowable = NULL;83jstring message = NULL;8485jplis_assert(throwableToMap != NULL);86jplis_assert(isSafeForJNICalls(jnienv));87jplis_assert(!isUnchecked(jnienv, throwableToMap));8889message = getMessageFromThrowable(jnienv, throwableToMap);90mappedThrowable = createInternalError(jnienv, message);9192jplis_assert(isSafeForJNICalls(jnienv));93return mappedThrowable;94}959697jboolean98checkForThrowable( JNIEnv* jnienv) {99return (*jnienv)->ExceptionCheck(jnienv);100}101102jboolean103isSafeForJNICalls( JNIEnv * jnienv) {104return !(*jnienv)->ExceptionCheck(jnienv);105}106107108void109logThrowable( JNIEnv * jnienv) {110if ( checkForThrowable(jnienv) ) {111(*jnienv)->ExceptionDescribe(jnienv);112}113}114115116117/**118* Creates an exception or error with the fully qualified classname (ie java/lang/Error)119* and message passed to its constructor120*/121jthrowable122createThrowable( JNIEnv * jnienv,123const char * className,124jstring message) {125jthrowable exception = NULL;126jmethodID constructor = NULL;127jclass exceptionClass = NULL;128jboolean errorOutstanding = JNI_FALSE;129130jplis_assert(className != NULL);131jplis_assert(isSafeForJNICalls(jnienv));132133/* create new VMError with message from exception */134exceptionClass = (*jnienv)->FindClass(jnienv, className);135errorOutstanding = checkForAndClearThrowable(jnienv);136jplis_assert(!errorOutstanding);137138if (!errorOutstanding) {139constructor = (*jnienv)->GetMethodID( jnienv,140exceptionClass,141"<init>",142"(Ljava/lang/String;)V");143errorOutstanding = checkForAndClearThrowable(jnienv);144jplis_assert(!errorOutstanding);145}146147if (!errorOutstanding) {148exception = (*jnienv)->NewObject(jnienv, exceptionClass, constructor, message);149errorOutstanding = checkForAndClearThrowable(jnienv);150jplis_assert(!errorOutstanding);151}152153jplis_assert(isSafeForJNICalls(jnienv));154return exception;155}156157jthrowable158createInternalError(JNIEnv * jnienv, jstring message) {159return createThrowable( jnienv,160"java/lang/InternalError",161message);162}163164jthrowable165createThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {166const char * throwableClassName = NULL;167const char * message = NULL;168jstring messageString = NULL;169170switch ( errorCode ) {171case JVMTI_ERROR_NULL_POINTER:172throwableClassName = "java/lang/NullPointerException";173break;174175case JVMTI_ERROR_ILLEGAL_ARGUMENT:176throwableClassName = "java/lang/IllegalArgumentException";177break;178179case JVMTI_ERROR_OUT_OF_MEMORY:180throwableClassName = "java/lang/OutOfMemoryError";181break;182183case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:184throwableClassName = "java/lang/ClassCircularityError";185break;186187case JVMTI_ERROR_FAILS_VERIFICATION:188throwableClassName = "java/lang/VerifyError";189break;190191case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:192throwableClassName = "java/lang/UnsupportedOperationException";193message = "class redefinition failed: attempted to add a method";194break;195196case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:197throwableClassName = "java/lang/UnsupportedOperationException";198message = "class redefinition failed: attempted to change the schema (add/remove fields)";199break;200201case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:202throwableClassName = "java/lang/UnsupportedOperationException";203message = "class redefinition failed: attempted to change superclass or interfaces";204break;205206case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:207throwableClassName = "java/lang/UnsupportedOperationException";208message = "class redefinition failed: attempted to delete a method";209break;210211case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:212throwableClassName = "java/lang/UnsupportedOperationException";213message = "class redefinition failed: attempted to change the class modifiers";214break;215216case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED:217throwableClassName = "java/lang/UnsupportedOperationException";218message = "class redefinition failed: attempted to change the class NestHost, NestMembers, Record, or PermittedSubclasses attribute";219break;220221case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:222throwableClassName = "java/lang/UnsupportedOperationException";223message = "class redefinition failed: attempted to change method modifiers";224break;225226case JVMTI_ERROR_UNSUPPORTED_VERSION:227throwableClassName = "java/lang/UnsupportedClassVersionError";228break;229230case JVMTI_ERROR_NAMES_DONT_MATCH:231throwableClassName = "java/lang/NoClassDefFoundError";232message = "class names don't match";233break;234235case JVMTI_ERROR_INVALID_CLASS_FORMAT:236throwableClassName = "java/lang/ClassFormatError";237break;238239case JVMTI_ERROR_UNMODIFIABLE_CLASS:240throwableClassName = "java/lang/instrument/UnmodifiableClassException";241break;242243case JVMTI_ERROR_INVALID_CLASS:244throwableClassName = "java/lang/InternalError";245message = "class redefinition failed: invalid class";246break;247248case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:249throwableClassName = "java/lang/UnsupportedOperationException";250message = "unsupported operation";251break;252253case JVMTI_ERROR_INTERNAL:254default:255throwableClassName = "java/lang/InternalError";256break;257}258259if ( message != NULL ) {260jboolean errorOutstanding;261262messageString = (*jnienv)->NewStringUTF(jnienv, message);263errorOutstanding = checkForAndClearThrowable(jnienv);264jplis_assert_msg(!errorOutstanding, "can't create exception java string");265}266return createThrowable( jnienv,267throwableClassName,268messageString);269270}271272273/**274* Calls toString() on the given message which is the same call made by275* Exception when passed a throwable to its constructor276*/277jstring278getMessageFromThrowable( JNIEnv* jnienv,279jthrowable exception) {280jclass exceptionClass = NULL;281jmethodID method = NULL;282jstring message = NULL;283jboolean errorOutstanding = JNI_FALSE;284285jplis_assert(isSafeForJNICalls(jnienv));286287/* call getMessage on exception */288exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception);289errorOutstanding = checkForAndClearThrowable(jnienv);290jplis_assert(!errorOutstanding);291292if (!errorOutstanding) {293method = (*jnienv)->GetMethodID(jnienv,294exceptionClass,295"toString",296"()Ljava/lang/String;");297errorOutstanding = checkForAndClearThrowable(jnienv);298jplis_assert(!errorOutstanding);299}300301if (!errorOutstanding) {302message = (*jnienv)->CallObjectMethod(jnienv, exception, method);303errorOutstanding = checkForAndClearThrowable(jnienv);304jplis_assert(!errorOutstanding);305}306307jplis_assert(isSafeForJNICalls(jnienv));308309return message;310}311312313/**314* Returns whether the exception given is an unchecked exception:315* a subclass of Error or RuntimeException316*/317jboolean318isUnchecked( JNIEnv* jnienv,319jthrowable exception) {320jboolean result = JNI_FALSE;321322jplis_assert(isSafeForJNICalls(jnienv));323result = (exception == NULL) ||324isInstanceofClassName(jnienv, exception, "java/lang/Error") ||325isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException");326jplis_assert(isSafeForJNICalls(jnienv));327return result;328}329330/*331* Returns the current throwable, if any. Clears the throwable state.332* Clients can use this to preserve the current throwable state on the stack.333*/334jthrowable335preserveThrowable(JNIEnv * jnienv) {336jthrowable result = (*jnienv)->ExceptionOccurred(jnienv);337if ( result != NULL ) {338(*jnienv)->ExceptionClear(jnienv);339}340return result;341}342343/*344* Installs the supplied throwable into the JNIEnv if the throwable is not null.345* Clients can use this to preserve the current throwable state on the stack.346*/347void348restoreThrowable( JNIEnv * jnienv,349jthrowable preservedException) {350throwThrowable( jnienv,351preservedException);352return;353}354355void356throwThrowable( JNIEnv * jnienv,357jthrowable exception) {358if ( exception != NULL ) {359jint result = (*jnienv)->Throw(jnienv, exception);360jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw");361}362return;363}364365366/*367* Always clears the JNIEnv throwable state. Returns true if an exception was present368* before the clearing operation.369*/370jboolean371checkForAndClearThrowable( JNIEnv * jnienv) {372jboolean result = (*jnienv)->ExceptionCheck(jnienv);373if ( result ) {374(*jnienv)->ExceptionClear(jnienv);375}376return result;377}378379/* creates a java.lang.InternalError and installs it into the JNIEnv */380void381createAndThrowInternalError(JNIEnv * jnienv) {382jthrowable internalError = createInternalError( jnienv, NULL);383throwThrowable(jnienv, forceFallback(internalError));384}385386void387createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {388jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);389throwThrowable(jnienv, forceFallback(throwable));390}391392void393mapThrownThrowableIfNecessary( JNIEnv * jnienv,394CheckedExceptionMapper mapper) {395jthrowable originalThrowable = NULL;396jthrowable resultThrowable = NULL;397398originalThrowable = preserveThrowable(jnienv);399400/* the throwable is now cleared, so JNI calls are safe */401if ( originalThrowable != NULL ) {402/* if there is an exception: we can just throw it if it is unchecked. If checked,403* we need to map it (mapper is conditional, will vary by usage, hence the callback)404*/405if ( isUnchecked(jnienv, originalThrowable) ) {406resultThrowable = originalThrowable;407}408else {409resultThrowable = (*mapper) (jnienv, originalThrowable);410}411}412413/* re-establish the correct throwable */414if ( resultThrowable != NULL ) {415throwThrowable(jnienv, forceFallback(resultThrowable));416}417418}419420421