src/java.instrument/share/native/libinstrument/JavaExceptions.c
changeset 47216 71c04702a3d5
parent 25859 3317bb8137f4
child 50735 2f2af62dfac7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.instrument/share/native/libinstrument/JavaExceptions.c	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Copyright 2003 Wily Technology, Inc.
+ */
+
+#include    <jni.h>
+#include    <jvmti.h>
+
+#include    "JPLISAssert.h"
+#include    "Utilities.h"
+#include    "JavaExceptions.h"
+
+/**
+ * This module contains utility routines for manipulating Java throwables
+ * and JNIEnv throwable state from native code.
+ */
+
+static jthrowable   sFallbackInternalError  = NULL;
+
+/*
+ * Local forward declarations.
+ */
+
+/* insist on having a throwable. If we already have one, return it.
+ * If not, map to fallback
+ */
+jthrowable
+forceFallback(jthrowable potentialException);
+
+
+jthrowable
+forceFallback(jthrowable potentialException) {
+    if ( potentialException == NULL ) {
+        return sFallbackInternalError;
+    }
+    else {
+        return potentialException;
+    }
+}
+
+/**
+ *  Returns true if it properly sets up a fallback exception
+ */
+jboolean
+initializeFallbackError(JNIEnv* jnienv) {
+    jplis_assert(isSafeForJNICalls(jnienv));
+    sFallbackInternalError = createInternalError(jnienv, NULL);
+    jplis_assert(isSafeForJNICalls(jnienv));
+    return (sFallbackInternalError != NULL);
+}
+
+
+/*
+ *  Map everything to InternalError.
+ */
+jthrowable
+mapAllCheckedToInternalErrorMapper( JNIEnv *    jnienv,
+                                    jthrowable  throwableToMap) {
+    jthrowable  mappedThrowable = NULL;
+    jstring     message         = NULL;
+
+    jplis_assert(throwableToMap != NULL);
+    jplis_assert(isSafeForJNICalls(jnienv));
+    jplis_assert(!isUnchecked(jnienv, throwableToMap));
+
+    message = getMessageFromThrowable(jnienv, throwableToMap);
+    mappedThrowable = createInternalError(jnienv, message);
+
+    jplis_assert(isSafeForJNICalls(jnienv));
+    return mappedThrowable;
+}
+
+
+jboolean
+checkForThrowable(  JNIEnv*     jnienv) {
+    return (*jnienv)->ExceptionCheck(jnienv);
+}
+
+jboolean
+isSafeForJNICalls(  JNIEnv * jnienv) {
+    return !(*jnienv)->ExceptionCheck(jnienv);
+}
+
+
+void
+logThrowable(   JNIEnv * jnienv) {
+    if ( checkForThrowable(jnienv) ) {
+        (*jnienv)->ExceptionDescribe(jnienv);
+    }
+}
+
+
+
+/**
+ *  Creates an exception or error with the fully qualified classname (ie java/lang/Error)
+ *  and message passed to its constructor
+ */
+jthrowable
+createThrowable(    JNIEnv *        jnienv,
+                    const char *    className,
+                    jstring         message) {
+    jthrowable  exception           = NULL;
+    jmethodID   constructor         = NULL;
+    jclass      exceptionClass      = NULL;
+    jboolean    errorOutstanding    = JNI_FALSE;
+
+    jplis_assert(className != NULL);
+    jplis_assert(isSafeForJNICalls(jnienv));
+
+    /* create new VMError with message from exception */
+    exceptionClass = (*jnienv)->FindClass(jnienv, className);
+    errorOutstanding = checkForAndClearThrowable(jnienv);
+    jplis_assert(!errorOutstanding);
+
+    if (!errorOutstanding) {
+        constructor = (*jnienv)->GetMethodID(   jnienv,
+                                                exceptionClass,
+                                                "<init>",
+                                                "(Ljava/lang/String;)V");
+        errorOutstanding = checkForAndClearThrowable(jnienv);
+        jplis_assert(!errorOutstanding);
+    }
+
+    if (!errorOutstanding) {
+        exception = (*jnienv)->NewObject(jnienv, exceptionClass, constructor, message);
+        errorOutstanding = checkForAndClearThrowable(jnienv);
+        jplis_assert(!errorOutstanding);
+    }
+
+    jplis_assert(isSafeForJNICalls(jnienv));
+    return exception;
+}
+
+jthrowable
+createInternalError(JNIEnv * jnienv, jstring message) {
+    return createThrowable( jnienv,
+                            "java/lang/InternalError",
+                            message);
+}
+
+jthrowable
+createThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
+    const char * throwableClassName = NULL;
+    const char * message            = NULL;
+    jstring messageString           = NULL;
+
+    switch ( errorCode ) {
+        case JVMTI_ERROR_NULL_POINTER:
+                throwableClassName = "java/lang/NullPointerException";
+                break;
+
+        case JVMTI_ERROR_ILLEGAL_ARGUMENT:
+                throwableClassName = "java/lang/IllegalArgumentException";
+                break;
+
+        case JVMTI_ERROR_OUT_OF_MEMORY:
+                throwableClassName = "java/lang/OutOfMemoryError";
+                break;
+
+        case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
+                throwableClassName = "java/lang/ClassCircularityError";
+                break;
+
+        case JVMTI_ERROR_FAILS_VERIFICATION:
+                throwableClassName = "java/lang/VerifyError";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "class redefinition failed: attempted to add a method";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "class redefinition failed: attempted to change the schema (add/remove fields)";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "class redefinition failed: attempted to change superclass or interfaces";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "class redefinition failed: attempted to delete a method";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "class redefinition failed: attempted to change the class modifiers";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "class redefinition failed: attempted to change method modifiers";
+                break;
+
+        case JVMTI_ERROR_UNSUPPORTED_VERSION:
+                throwableClassName = "java/lang/UnsupportedClassVersionError";
+                break;
+
+        case JVMTI_ERROR_NAMES_DONT_MATCH:
+                throwableClassName = "java/lang/NoClassDefFoundError";
+                message = "class names don't match";
+                break;
+
+        case JVMTI_ERROR_INVALID_CLASS_FORMAT:
+                throwableClassName = "java/lang/ClassFormatError";
+                break;
+
+        case JVMTI_ERROR_UNMODIFIABLE_CLASS:
+                throwableClassName = "java/lang/instrument/UnmodifiableClassException";
+                break;
+
+        case JVMTI_ERROR_INVALID_CLASS:
+                throwableClassName = "java/lang/InternalError";
+                message = "class redefinition failed: invalid class";
+                break;
+
+        case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
+                throwableClassName = "java/lang/UnsupportedOperationException";
+                message = "unsupported operation";
+                break;
+
+        case JVMTI_ERROR_INTERNAL:
+        default:
+                throwableClassName = "java/lang/InternalError";
+                break;
+        }
+
+    if ( message != NULL ) {
+        jboolean errorOutstanding;
+
+        messageString = (*jnienv)->NewStringUTF(jnienv, message);
+        errorOutstanding = checkForAndClearThrowable(jnienv);
+        jplis_assert_msg(!errorOutstanding, "can't create exception java string");
+    }
+    return createThrowable( jnienv,
+                            throwableClassName,
+                            messageString);
+
+}
+
+
+/**
+ *  Calls toString() on the given message which is the same call made by
+ *  Exception when passed a throwable to its constructor
+ */
+jstring
+getMessageFromThrowable(    JNIEnv*     jnienv,
+                            jthrowable  exception) {
+    jclass      exceptionClass      = NULL;
+    jmethodID   method              = NULL;
+    jstring     message             = NULL;
+    jboolean    errorOutstanding    = JNI_FALSE;
+
+    jplis_assert(isSafeForJNICalls(jnienv));
+
+    /* call getMessage on exception */
+    exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception);
+    errorOutstanding = checkForAndClearThrowable(jnienv);
+    jplis_assert(!errorOutstanding);
+
+    if (!errorOutstanding) {
+        method = (*jnienv)->GetMethodID(jnienv,
+                                        exceptionClass,
+                                        "toString",
+                                        "()Ljava/lang/String;");
+        errorOutstanding = checkForAndClearThrowable(jnienv);
+        jplis_assert(!errorOutstanding);
+    }
+
+    if (!errorOutstanding) {
+        message = (*jnienv)->CallObjectMethod(jnienv, exception, method);
+        errorOutstanding = checkForAndClearThrowable(jnienv);
+        jplis_assert(!errorOutstanding);
+    }
+
+    jplis_assert(isSafeForJNICalls(jnienv));
+
+    return message;
+}
+
+
+/**
+ *  Returns whether the exception given is an unchecked exception:
+ *  a subclass of Error or RuntimeException
+ */
+jboolean
+isUnchecked(    JNIEnv*     jnienv,
+                jthrowable  exception) {
+    jboolean result = JNI_FALSE;
+
+    jplis_assert(isSafeForJNICalls(jnienv));
+    result =    (exception == NULL) ||
+                isInstanceofClassName(jnienv, exception, "java/lang/Error") ||
+                isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException");
+    jplis_assert(isSafeForJNICalls(jnienv));
+    return result;
+}
+
+/*
+ *  Returns the current throwable, if any. Clears the throwable state.
+ *  Clients can use this to preserve the current throwable state on the stack.
+ */
+jthrowable
+preserveThrowable(JNIEnv * jnienv) {
+    jthrowable result = (*jnienv)->ExceptionOccurred(jnienv);
+    if ( result != NULL ) {
+        (*jnienv)->ExceptionClear(jnienv);
+    }
+    return result;
+}
+
+/*
+ *  Installs the supplied throwable into the JNIEnv if the throwable is not null.
+ *  Clients can use this to preserve the current throwable state on the stack.
+ */
+void
+restoreThrowable(   JNIEnv *    jnienv,
+                    jthrowable  preservedException) {
+    throwThrowable( jnienv,
+                    preservedException);
+    return;
+}
+
+void
+throwThrowable(     JNIEnv *    jnienv,
+                    jthrowable  exception) {
+    if ( exception != NULL ) {
+        jint result = (*jnienv)->Throw(jnienv, exception);
+        jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw");
+    }
+    return;
+}
+
+
+/*
+ *  Always clears the JNIEnv throwable state. Returns true if an exception was present
+ *  before the clearing operation.
+ */
+jboolean
+checkForAndClearThrowable(  JNIEnv *    jnienv) {
+    jboolean result = (*jnienv)->ExceptionCheck(jnienv);
+    if ( result ) {
+        (*jnienv)->ExceptionClear(jnienv);
+    }
+    return result;
+}
+
+/* creates a java.lang.InternalError and installs it into the JNIEnv */
+void
+createAndThrowInternalError(JNIEnv * jnienv) {
+    jthrowable internalError = createInternalError( jnienv, NULL);
+    throwThrowable(jnienv, forceFallback(internalError));
+}
+
+void
+createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
+    jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
+    throwThrowable(jnienv, forceFallback(throwable));
+}
+
+void
+mapThrownThrowableIfNecessary(  JNIEnv *                jnienv,
+                                CheckedExceptionMapper  mapper) {
+    jthrowable  originalThrowable   = NULL;
+    jthrowable  resultThrowable     = NULL;
+
+    originalThrowable = preserveThrowable(jnienv);
+
+    /* the throwable is now cleared, so JNI calls are safe */
+    if ( originalThrowable != NULL ) {
+        /* if there is an exception: we can just throw it if it is unchecked. If checked,
+         * we need to map it (mapper is conditional, will vary by usage, hence the callback)
+         */
+        if ( isUnchecked(jnienv, originalThrowable) ) {
+            resultThrowable = originalThrowable;
+        }
+        else {
+            resultThrowable = (*mapper) (jnienv, originalThrowable);
+        }
+    }
+
+    /* re-establish the correct throwable */
+    if ( resultThrowable != NULL ) {
+        throwThrowable(jnienv, forceFallback(resultThrowable));
+    }
+
+}