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
42 static 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 */
51 jthrowable
52 forceFallback(jthrowable potentialException);
53
54
55 jthrowable
forceFallback(jthrowable potentialException)56 forceFallback(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 */
68 jboolean
initializeFallbackError(JNIEnv * jnienv)69 initializeFallbackError(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 */
80 jthrowable
mapAllCheckedToInternalErrorMapper(JNIEnv * jnienv,jthrowable throwableToMap)81 mapAllCheckedToInternalErrorMapper( 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
98 jboolean
checkForThrowable(JNIEnv * jnienv)99 checkForThrowable( JNIEnv* jnienv) {
100 return (*jnienv)->ExceptionCheck(jnienv);
101 }
102
103 jboolean
isSafeForJNICalls(JNIEnv * jnienv)104 isSafeForJNICalls( JNIEnv * jnienv) {
105 return !(*jnienv)->ExceptionCheck(jnienv);
106 }
107
108
109 void
logThrowable(JNIEnv * jnienv)110 logThrowable( 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 */
122 jthrowable
createThrowable(JNIEnv * jnienv,const char * className,jstring message)123 createThrowable( 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
158 jthrowable
createInternalError(JNIEnv * jnienv,jstring message)159 createInternalError(JNIEnv * jnienv, jstring message) {
160 return createThrowable( jnienv,
161 "java/lang/InternalError",
162 message);
163 }
164
165 jthrowable
createThrowableFromJVMTIErrorCode(JNIEnv * jnienv,jvmtiError errorCode)166 createThrowableFromJVMTIErrorCode(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 */
278 jstring
getMessageFromThrowable(JNIEnv * jnienv,jthrowable exception)279 getMessageFromThrowable( 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 */
318 jboolean
isUnchecked(JNIEnv * jnienv,jthrowable exception)319 isUnchecked( 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 */
335 jthrowable
preserveThrowable(JNIEnv * jnienv)336 preserveThrowable(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 */
348 void
restoreThrowable(JNIEnv * jnienv,jthrowable preservedException)349 restoreThrowable( JNIEnv * jnienv,
350 jthrowable preservedException) {
351 throwThrowable( jnienv,
352 preservedException);
353 return;
354 }
355
356 void
throwThrowable(JNIEnv * jnienv,jthrowable exception)357 throwThrowable( 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 */
371 jboolean
checkForAndClearThrowable(JNIEnv * jnienv)372 checkForAndClearThrowable( 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 */
381 void
createAndThrowInternalError(JNIEnv * jnienv)382 createAndThrowInternalError(JNIEnv * jnienv) {
383 jthrowable internalError = createInternalError( jnienv, NULL);
384 throwThrowable(jnienv, forceFallback(internalError));
385 }
386
387 void
createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv,jvmtiError errorCode)388 createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
389 jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
390 throwThrowable(jnienv, forceFallback(throwable));
391 }
392
393 void
mapThrownThrowableIfNecessary(JNIEnv * jnienv,CheckedExceptionMapper mapper)394 mapThrownThrowableIfNecessary( 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