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
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_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 */
278jstring
279getMessageFromThrowable( 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 */
318jboolean
319isUnchecked( 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 */
335jthrowable
336preserveThrowable(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 */
348void
349restoreThrowable( JNIEnv * jnienv,
350 jthrowable preservedException) {
351 throwThrowable( jnienv,
352 preservedException);
353 return;
354}
355
356void
357throwThrowable( 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 */
371jboolean
372checkForAndClearThrowable( 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 */
381void
382createAndThrowInternalError(JNIEnv * jnienv) {
383 jthrowable internalError = createInternalError( jnienv, NULL);
384 throwThrowable(jnienv, forceFallback(internalError));
385}
386
387void
388createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
389 jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
390 throwThrowable(jnienv, forceFallback(throwable));
391}
392
393void
394mapThrownThrowableIfNecessary( 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}
420