--- /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));
+ }
+
+}