| /* | |
| * Copyright (c) 2020, 2024, 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. | |
| */ | |
| #ifndef __JNIUTILITIES_H | |
| #define __JNIUTILITIES_H | |
| #include "jni.h" | |
| #include "jni_util.h" | |
| #import <Cocoa/Cocoa.h> | |
| /******** LOGGING SUPPORT *********/ | |
| #define LOG_NULL(dst_var, name) \ | |
| if (dst_var == NULL) { \ | |
| NSLog(@"Bad JNI lookup %s\n", name); \ | |
| NSLog(@"%@",[NSThread callStackSymbols]); \ | |
| if ([NSThread isMainThread] == NO) { \ | |
| if (!(*env)->ExceptionCheck(env)) { \ | |
| JNU_ThrowInternalError(env, "Bad JNI Lookup"); \ | |
| } \ | |
| } else { \ | |
| if ((*env)->ExceptionCheck(env)) { \ | |
| (*env)->ExceptionDescribe(env); \ | |
| } \ | |
| } \ | |
| [NSException raise:NSGenericException format:@"JNI Lookup Exception"]; \ | |
| } | |
| /******** GET CLASS SUPPORT *********/ | |
| #define GET_CLASS(dst_var, cls) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->FindClass(env, cls); \ | |
| if (dst_var != NULL) dst_var = (*env)->NewGlobalRef(env, dst_var); \ | |
| } \ | |
| LOG_NULL(dst_var, cls); \ | |
| CHECK_NULL(dst_var); | |
| #define DECLARE_CLASS(dst_var, cls) \ | |
| static jclass dst_var = NULL; \ | |
| GET_CLASS(dst_var, cls); | |
| #define GET_CLASS_RETURN(dst_var, cls, ret) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->FindClass(env, cls); \ | |
| if (dst_var != NULL) dst_var = (*env)->NewGlobalRef(env, dst_var); \ | |
| } \ | |
| LOG_NULL(dst_var, cls); \ | |
| CHECK_NULL_RETURN(dst_var, ret); | |
| #define DECLARE_CLASS_RETURN(dst_var, cls, ret) \ | |
| static jclass dst_var = NULL; \ | |
| GET_CLASS_RETURN(dst_var, cls, ret); | |
| /******** GET METHOD SUPPORT *********/ | |
| #define GET_METHOD(dst_var, cls, name, signature) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetMethodID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL(dst_var); | |
| #define DECLARE_METHOD(dst_var, cls, name, signature) \ | |
| static jmethodID dst_var = NULL; \ | |
| GET_METHOD(dst_var, cls, name, signature); | |
| #define GET_METHOD_RETURN(dst_var, cls, name, signature, ret) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetMethodID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL_RETURN(dst_var, ret); | |
| #define DECLARE_METHOD_RETURN(dst_var, cls, name, signature, ret) \ | |
| static jmethodID dst_var = NULL; \ | |
| GET_METHOD_RETURN(dst_var, cls, name, signature, ret); | |
| #define GET_STATIC_METHOD(dst_var, cls, name, signature) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetStaticMethodID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL(dst_var); | |
| #define DECLARE_STATIC_METHOD(dst_var, cls, name, signature) \ | |
| static jmethodID dst_var = NULL; \ | |
| GET_STATIC_METHOD(dst_var, cls, name, signature); | |
| #define GET_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetStaticMethodID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL_RETURN(dst_var, ret); | |
| #define DECLARE_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret) \ | |
| static jmethodID dst_var = NULL; \ | |
| GET_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret); | |
| /******** GET FIELD SUPPORT *********/ | |
| #define GET_FIELD(dst_var, cls, name, signature) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetFieldID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL(dst_var); | |
| #define DECLARE_FIELD(dst_var, cls, name, signature) \ | |
| static jfieldID dst_var = NULL; \ | |
| GET_FIELD(dst_var, cls, name, signature); | |
| #define GET_FIELD_RETURN(dst_var, cls, name, signature, ret) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetFieldID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL_RETURN(dst_var, ret); | |
| #define DECLARE_FIELD_RETURN(dst_var, cls, name, signature, ret) \ | |
| static jfieldID dst_var = NULL; \ | |
| GET_FIELD_RETURN(dst_var, cls, name, signature, ret); | |
| #define GET_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret) \ | |
| if (dst_var == NULL) { \ | |
| dst_var = (*env)->GetStaticFieldID(env, cls, name, signature); \ | |
| } \ | |
| LOG_NULL(dst_var, name); \ | |
| CHECK_NULL_RETURN(dst_var, ret); | |
| #define DECLARE_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret) \ | |
| static jfieldID dst_var = NULL; \ | |
| GET_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret); | |
| /********* EXCEPTION_HANDLING *********/ | |
| /* | |
| * Some explanation to set context of the bigger picture. | |
| * Before returning to Java from JNI, NSExceptions are caught - so long as | |
| * the body of the native method is wrapped in the ENTER/EXIT macros. | |
| * So if we want to directly return to Java from some nested Objective-C | |
| * function when detecting a Java exception, we just need to raise an | |
| * NSException. Then clear that right before returning to Java, | |
| * leaving the Java exception to be seen back in Java-land. | |
| * | |
| * But if the current thread is the Appkit thread we might as well clear | |
| * the Java Exception right now since there's nothing to receive it. | |
| * In such a case control will propagate back to the run loop which might | |
| * terminate the application. One drawback of that is that the location of | |
| * termination does not show where the NSException originated. | |
| * And for whatever reason, something swallows that exception. | |
| * So as a debugging aid, when on the AppKit thread we can provide a | |
| * way (via an env. var.) to log the location. | |
| * Additionally provide a similar way to prevent the NSException being | |
| * raised and instead just clear the Java Exception. | |
| * Together these provide alternate behaviours for more debugging info | |
| * or maybe a way for the app to continue running depending on the exact | |
| * nature of the problem that has been detected and how survivable it is. | |
| */ | |
| #define CHECK_EXCEPTION() \ | |
| if ((*env)->ExceptionCheck(env)) { \ | |
| if ([NSThread isMainThread] == YES) { \ | |
| if (getenv("JNU_APPKIT_TRACE")) { \ | |
| (*env)->ExceptionDescribe(env); \ | |
| NSLog(@"%@",[NSThread callStackSymbols]); \ | |
| } else { \ | |
| (*env)->ExceptionClear(env); \ | |
| } \ | |
| } \ | |
| if (getenv("JNU_NO_COCOA_EXCEPTION") == NULL) { \ | |
| [NSException raise:NSGenericException format:@"Java Exception"]; \ | |
| } else { \ | |
| (*env)->ExceptionClear(env); \ | |
| } \ | |
| }; | |
| #define CHECK_EXCEPTION_NULL_RETURN(x, y) \ | |
| CHECK_EXCEPTION(); \ | |
| if ((x) == NULL) { \ | |
| return y; \ | |
| }; | |
| /* Create a pool and initiate a try block to catch any exception */ | |
| #define JNI_COCOA_ENTER(env) \ | |
| NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \ | |
| @try { | |
| /* Don't allow NSExceptions to escape to Java. | |
| * If there is a Java exception that has been thrown that should escape. | |
| * And ensure we drain the auto-release pool. | |
| */ | |
| #define JNI_COCOA_EXIT(env) \ | |
| } \ | |
| @catch (NSException *e) { \ | |
| NSLog(@"%@", [e callStackSymbols]); \ | |
| } \ | |
| @finally { \ | |
| [pool drain]; \ | |
| }; | |
| /* Same as above but adds a clean up action. | |
| * Requires that whatever is being cleaned up is in scope. | |
| */ | |
| #define JNI_COCOA_EXIT_WITH_ACTION(env, action) \ | |
| } \ | |
| @catch (NSException *e) { \ | |
| { action; }; \ | |
| NSLog(@"%@", [e callStackSymbols]); \ | |
| } \ | |
| @finally { \ | |
| [pool drain]; \ | |
| }; | |
| /******** STRING CONVERSION SUPPORT *********/ | |
| JNIEXPORT NSString* JavaStringToNSString(JNIEnv *env, jstring jstr); | |
| JNIEXPORT jstring NSStringToJavaString(JNIEnv* env, NSString *str); | |
| JNIEXPORT NSString* NormalizedPathNSStringFromJavaString(JNIEnv *env, jstring pathStr); | |
| JNIEXPORT jstring NormalizedPathJavaStringFromNSString(JNIEnv* env, NSString *str); | |
| #endif /* __JNIUTILITIES_H */ |