16
16
17
17
#include "include/nativehelper/JNIHelp.h"
18
18
19
+ #include <stdarg.h>
19
20
#include <stdbool.h>
20
21
#include <stdio.h>
21
22
#include <stdlib.h>
@@ -233,6 +234,84 @@ static void DiscardPendingException(JNIEnv* env, const char* className) {
233
234
(* env )-> DeleteLocalRef (env , exception );
234
235
}
235
236
237
+ static int ThrowException (JNIEnv * env , const char * className , const char * ctorSig , ...) {
238
+ int status = -1 ;
239
+ jclass exceptionClass = NULL ;
240
+
241
+ va_list args ;
242
+ va_start (args , ctorSig );
243
+
244
+ DiscardPendingException (env , className );
245
+
246
+ {
247
+ /* We want to clean up local references before returning from this function, so,
248
+ * regardless of return status, the end block must run. Have the work done in a
249
+ * nested block to avoid using any uninitialized variables in the end block. */
250
+ exceptionClass = (* env )-> FindClass (env , className );
251
+ if (exceptionClass == NULL ) {
252
+ ALOGE ("Unable to find exception class %s" , className );
253
+ /* an exception, most likely ClassNotFoundException, will now be pending */
254
+ goto end ;
255
+ }
256
+
257
+ jmethodID init = (* env )-> GetMethodID (env , exceptionClass , "<init>" , ctorSig );
258
+ if (init == NULL ) {
259
+ ALOGE ("Failed to find constructor for '%s' '%s'" , className , ctorSig );
260
+ goto end ;
261
+ }
262
+
263
+ jobject instance = (* env )-> NewObjectV (env , exceptionClass , init , args );
264
+ if (instance == NULL ) {
265
+ ALOGE ("Failed to construct '%s'" , className );
266
+ goto end ;
267
+ }
268
+
269
+ if ((* env )-> Throw (env , (jthrowable )instance ) != JNI_OK ) {
270
+ ALOGE ("Failed to throw '%s'" , className );
271
+ /* an exception, most likely OOM, will now be pending */
272
+ goto end ;
273
+ }
274
+
275
+ /* everything worked fine, just update status to success and clean up */
276
+ status = 0 ;
277
+ }
278
+
279
+ end :
280
+ va_end (args );
281
+ if (exceptionClass != NULL ) {
282
+ (* env )-> DeleteLocalRef (env , exceptionClass );
283
+ }
284
+ return status ;
285
+ }
286
+
287
+ static jstring CreateExceptionMsg (JNIEnv * env , const char * msg ) {
288
+ jstring detailMessage = (* env )-> NewStringUTF (env , msg );
289
+ if (detailMessage == NULL ) {
290
+ /* Not really much we can do here. We're probably dead in the water,
291
+ but let's try to stumble on... */
292
+ (* env )-> ExceptionClear (env );
293
+ }
294
+ return detailMessage ;
295
+ }
296
+
297
+ /* Helper macro to deal with conversion of the exception message from a C string
298
+ * to jstring.
299
+ *
300
+ * This is useful because most exceptions have a message as the first parameter
301
+ * and delegating the conversion to all the callers of ThrowException results in
302
+ * code duplication. However, since we try to allow variable number of arguments
303
+ * for the exception constructor we'd either need to do the conversion inside
304
+ * the macro, or manipulate the va_list to replace the C string to a jstring.
305
+ * This seems like the cleaner solution.
306
+ */
307
+ #define THROW_EXCEPTION_WITH_MESSAGE (env , className , ctorSig , msg , ...) ({ \
308
+ jstring _detailMessage = CreateExceptionMsg(env, msg); \
309
+ int _status = ThrowException(env, className, ctorSig, _detailMessage, ## __VA_ARGS__); \
310
+ if (_detailMessage != NULL) { \
311
+ (*env)->DeleteLocalRef(env, _detailMessage); \
312
+ } \
313
+ _status; })
314
+
236
315
//
237
316
// JNIHelp external API
238
317
//
@@ -277,24 +356,7 @@ void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable thro
277
356
}
278
357
279
358
int jniThrowException (JNIEnv * env , const char * className , const char * message ) {
280
- DiscardPendingException (env , className );
281
-
282
- jclass exceptionClass = (* env )-> FindClass (env , className );
283
- if (exceptionClass == NULL ) {
284
- ALOGE ("Unable to find exception class %s" , className );
285
- /* ClassNotFoundException now pending */
286
- return -1 ;
287
- }
288
-
289
- int status = 0 ;
290
- if ((* env )-> ThrowNew (env , exceptionClass , message ) != JNI_OK ) {
291
- ALOGE ("Failed throwing '%s' '%s'" , className , message );
292
- /* an exception, most likely OOM, will now be pending */
293
- status = -1 ;
294
- }
295
- (* env )-> DeleteLocalRef (env , exceptionClass );
296
-
297
- return status ;
359
+ return THROW_EXCEPTION_WITH_MESSAGE (env , className , "(Ljava/lang/String;)V" , message );
298
360
}
299
361
300
362
int jniThrowExceptionFmt (JNIEnv * env , const char * className , const char * fmt , va_list args ) {
@@ -311,12 +373,17 @@ int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
311
373
return jniThrowException (env , "java/lang/RuntimeException" , msg );
312
374
}
313
375
314
- int jniThrowIOException (JNIEnv * env , int errnum ) {
376
+ int jniThrowIOException (JNIEnv * env , int errno_value ) {
315
377
char buffer [80 ];
316
- const char * message = platformStrError (errnum , buffer , sizeof (buffer ));
378
+ const char * message = platformStrError (errno_value , buffer , sizeof (buffer ));
317
379
return jniThrowException (env , "java/io/IOException" , message );
318
380
}
319
381
382
+ int jniThrowErrnoException (JNIEnv * env , const char * functionName , int errno_value ) {
383
+ return THROW_EXCEPTION_WITH_MESSAGE (env , "android/system/ErrnoException" ,
384
+ "(Ljava/lang/String;I)V" , functionName , errno_value );
385
+ }
386
+
320
387
jstring jniCreateString (JNIEnv * env , const jchar * unicodeChars , jsize len ) {
321
388
return (* env )-> NewString (env , unicodeChars , len );
322
389
}
0 commit comments