| /* |
| * Copyright (c) 2020, 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)->ExceptionOccurred(env) == NULL) { \ |
| JNU_ThrowInternalError(env, "Bad JNI Lookup"); \ |
| } \ |
| } else { \ |
| if ((*env)->ExceptionOccurred(env) != NULL) { \ |
| (*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)->ExceptionOccurred(env) != NULL) { \ |
| 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; \ |
| }; |
| |
| #endif /* __JNIUTILITIES_H */ |