blob: c9db6e9e19a815ccd789823dfacd3353b9a28f97 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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_METHOD_MODIFIERS_CHANGED:
218 throwableClassName = "java/lang/UnsupportedOperationException";
219 message = "class redefinition failed: attempted to change method modifiers";
220 break;
221
222 case JVMTI_ERROR_UNSUPPORTED_VERSION:
223 throwableClassName = "java/lang/UnsupportedClassVersionError";
224 break;
225
226 case JVMTI_ERROR_NAMES_DONT_MATCH:
227 throwableClassName = "java/lang/NoClassDefFoundError";
228 message = "class names don't match";
229 break;
230
231 case JVMTI_ERROR_INVALID_CLASS_FORMAT:
232 throwableClassName = "java/lang/ClassFormatError";
233 break;
234
235 case JVMTI_ERROR_UNMODIFIABLE_CLASS:
236 throwableClassName = "java/lang/instrument/UnmodifiableClassException";
237 break;
238
239 case JVMTI_ERROR_INVALID_CLASS:
240 throwableClassName = "java/lang/InternalError";
241 message = "class redefinition failed: invalid class";
242 break;
243
244 case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
245 throwableClassName = "java/lang/UnsupportedOperationException";
246 message = "unsupported operation";
247 break;
248
249 case JVMTI_ERROR_INTERNAL:
250 default:
251 throwableClassName = "java/lang/InternalError";
252 break;
253 }
254
255 if ( message != NULL ) {
256 jboolean errorOutstanding;
257
258 messageString = (*jnienv)->NewStringUTF(jnienv, message);
259 errorOutstanding = checkForAndClearThrowable(jnienv);
260 jplis_assert_msg(!errorOutstanding, "can't create exception java string");
261 }
262 return createThrowable( jnienv,
263 throwableClassName,
264 messageString);
265
266}
267
268
269/**
270 * Calls toString() on the given message which is the same call made by
271 * Exception when passed a throwable to its constructor
272 */
273jstring
274getMessageFromThrowable( JNIEnv* jnienv,
275 jthrowable exception) {
276 jclass exceptionClass = NULL;
277 jmethodID method = NULL;
278 jstring message = NULL;
279 jboolean errorOutstanding = JNI_FALSE;
280
281 jplis_assert(isSafeForJNICalls(jnienv));
282
283 /* call getMessage on exception */
284 exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception);
285 errorOutstanding = checkForAndClearThrowable(jnienv);
286 jplis_assert(!errorOutstanding);
287
288 if (!errorOutstanding) {
289 method = (*jnienv)->GetMethodID(jnienv,
290 exceptionClass,
291 "toString",
292 "()Ljava/lang/String;");
293 errorOutstanding = checkForAndClearThrowable(jnienv);
294 jplis_assert(!errorOutstanding);
295 }
296
297 if (!errorOutstanding) {
298 message = (*jnienv)->CallObjectMethod(jnienv, exception, method);
299 errorOutstanding = checkForAndClearThrowable(jnienv);
300 jplis_assert(!errorOutstanding);
301 }
302
303 jplis_assert(isSafeForJNICalls(jnienv));
304
305 return message;
306}
307
308
309/**
310 * Returns whether the exception given is an unchecked exception:
311 * a subclass of Error or RuntimeException
312 */
313jboolean
314isUnchecked( JNIEnv* jnienv,
315 jthrowable exception) {
316 jboolean result = JNI_FALSE;
317
318 jplis_assert(isSafeForJNICalls(jnienv));
319 result = (exception == NULL) ||
320 isInstanceofClassName(jnienv, exception, "java/lang/Error") ||
321 isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException");
322 jplis_assert(isSafeForJNICalls(jnienv));
323 return result;
324}
325
326/*
327 * Returns the current throwable, if any. Clears the throwable state.
328 * Clients can use this to preserve the current throwable state on the stack.
329 */
330jthrowable
331preserveThrowable(JNIEnv * jnienv) {
332 jthrowable result = (*jnienv)->ExceptionOccurred(jnienv);
333 if ( result != NULL ) {
334 (*jnienv)->ExceptionClear(jnienv);
335 }
336 return result;
337}
338
339/*
340 * Installs the supplied throwable into the JNIEnv if the throwable is not null.
341 * Clients can use this to preserve the current throwable state on the stack.
342 */
343void
344restoreThrowable( JNIEnv * jnienv,
345 jthrowable preservedException) {
346 throwThrowable( jnienv,
347 preservedException);
348 return;
349}
350
351void
352throwThrowable( JNIEnv * jnienv,
353 jthrowable exception) {
354 if ( exception != NULL ) {
355 jint result = (*jnienv)->Throw(jnienv, exception);
356 jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw");
357 }
358 return;
359}
360
361
362/*
363 * Always clears the JNIEnv throwable state. Returns true if an exception was present
364 * before the clearing operation.
365 */
366jboolean
367checkForAndClearThrowable( JNIEnv * jnienv) {
368 jboolean result = (*jnienv)->ExceptionCheck(jnienv);
369 if ( result ) {
370 (*jnienv)->ExceptionClear(jnienv);
371 }
372 return result;
373}
374
375/* creates a java.lang.InternalError and installs it into the JNIEnv */
376void
377createAndThrowInternalError(JNIEnv * jnienv) {
378 jthrowable internalError = createInternalError( jnienv, NULL);
379 throwThrowable(jnienv, forceFallback(internalError));
380}
381
382void
383createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
384 jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
385 throwThrowable(jnienv, forceFallback(throwable));
386}
387
388void
389mapThrownThrowableIfNecessary( JNIEnv * jnienv,
390 CheckedExceptionMapper mapper) {
391 jthrowable originalThrowable = NULL;
392 jthrowable resultThrowable = NULL;
393
394 originalThrowable = preserveThrowable(jnienv);
395
396 /* the throwable is now cleared, so JNI calls are safe */
397 if ( originalThrowable != NULL ) {
398 /* if there is an exception: we can just throw it if it is unchecked. If checked,
399 * we need to map it (mapper is conditional, will vary by usage, hence the callback)
400 */
401 if ( isUnchecked(jnienv, originalThrowable) ) {
402 resultThrowable = originalThrowable;
403 }
404 else {
405 resultThrowable = (*mapper) (jnienv, originalThrowable);
406 }
407 }
408
409 /* re-establish the correct throwable */
410 if ( resultThrowable != NULL ) {
411 throwThrowable(jnienv, forceFallback(resultThrowable));
412 }
413
414}