1 /*
   2  * Copyright (c) 2020, 2021, 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 #ifndef __JNIUTILITIES_H
  27 #define __JNIUTILITIES_H
  28 
  29 #include "jni.h"
  30 #include "jni_util.h"
  31 
  32 #import <Cocoa/Cocoa.h>
  33 
  34 /********        LOGGING SUPPORT    *********/
  35 
  36 #define LOG_NULL(dst_var, name) \
  37    if (dst_var == NULL) { \
  38        NSLog(@"Bad JNI lookup %s\n", name); \
  39        NSLog(@"%@",[NSThread callStackSymbols]); \
  40        if ([NSThread isMainThread] == NO) { \
  41            if ((*env)->ExceptionOccurred(env) == NULL) { \
  42               JNU_ThrowInternalError(env, "Bad JNI Lookup"); \
  43            } \
  44        } else { \
  45               if ((*env)->ExceptionOccurred(env) != NULL) { \
  46                   (*env)->ExceptionDescribe(env); \
  47            } \
  48        } \
  49        [NSException raise:NSGenericException format:@"JNI Lookup Exception"];  \
  50     }
  51 
  52 /********        GET CLASS SUPPORT    *********/
  53 
  54 #define GET_CLASS(dst_var, cls) \
  55      if (dst_var == NULL) { \
  56          dst_var = (*env)->FindClass(env, cls); \
  57          if (dst_var != NULL) dst_var = (*env)->NewGlobalRef(env, dst_var); \
  58      } \
  59      LOG_NULL(dst_var, cls); \
  60      CHECK_NULL(dst_var);
  61 
  62 #define DECLARE_CLASS(dst_var, cls) \
  63     static jclass dst_var = NULL; \
  64     GET_CLASS(dst_var, cls);
  65 
  66 #define GET_CLASS_RETURN(dst_var, cls, ret) \
  67      if (dst_var == NULL) { \
  68          dst_var = (*env)->FindClass(env, cls); \
  69          if (dst_var != NULL) dst_var = (*env)->NewGlobalRef(env, dst_var); \
  70      } \
  71      LOG_NULL(dst_var, cls); \
  72      CHECK_NULL_RETURN(dst_var, ret);
  73 
  74 #define DECLARE_CLASS_RETURN(dst_var, cls, ret) \
  75     static jclass dst_var = NULL; \
  76     GET_CLASS_RETURN(dst_var, cls, ret);
  77 
  78 
  79 /********        GET METHOD SUPPORT    *********/
  80 
  81 #define GET_METHOD(dst_var, cls, name, signature) \
  82      if (dst_var == NULL) { \
  83          dst_var = (*env)->GetMethodID(env, cls, name, signature); \
  84      } \
  85      LOG_NULL(dst_var, name); \
  86      CHECK_NULL(dst_var);
  87 
  88 #define DECLARE_METHOD(dst_var, cls, name, signature) \
  89      static jmethodID dst_var = NULL; \
  90      GET_METHOD(dst_var, cls, name, signature);
  91 
  92 #define GET_METHOD_RETURN(dst_var, cls, name, signature, ret) \
  93      if (dst_var == NULL) { \
  94          dst_var = (*env)->GetMethodID(env, cls, name, signature); \
  95      } \
  96      LOG_NULL(dst_var, name); \
  97      CHECK_NULL_RETURN(dst_var, ret);
  98 
  99 #define DECLARE_METHOD_RETURN(dst_var, cls, name, signature, ret) \
 100      static jmethodID dst_var = NULL; \
 101      GET_METHOD_RETURN(dst_var, cls, name, signature, ret);
 102 
 103 #define GET_STATIC_METHOD(dst_var, cls, name, signature) \
 104      if (dst_var == NULL) { \
 105          dst_var = (*env)->GetStaticMethodID(env, cls, name, signature); \
 106      } \
 107      LOG_NULL(dst_var, name); \
 108      CHECK_NULL(dst_var);
 109 
 110 #define DECLARE_STATIC_METHOD(dst_var, cls, name, signature) \
 111      static jmethodID dst_var = NULL; \
 112      GET_STATIC_METHOD(dst_var, cls, name, signature);
 113 
 114 #define GET_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret) \
 115      if (dst_var == NULL) { \
 116          dst_var = (*env)->GetStaticMethodID(env, cls, name, signature); \
 117      } \
 118      LOG_NULL(dst_var, name); \
 119      CHECK_NULL_RETURN(dst_var, ret);
 120 
 121 #define DECLARE_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret) \
 122      static jmethodID dst_var = NULL; \
 123      GET_STATIC_METHOD_RETURN(dst_var, cls, name, signature, ret);
 124 
 125 /********        GET FIELD SUPPORT    *********/
 126 
 127 
 128 #define GET_FIELD(dst_var, cls, name, signature) \
 129      if (dst_var == NULL) { \
 130          dst_var = (*env)->GetFieldID(env, cls, name, signature); \
 131      } \
 132      LOG_NULL(dst_var, name); \
 133      CHECK_NULL(dst_var);
 134 
 135 #define DECLARE_FIELD(dst_var, cls, name, signature) \
 136      static jfieldID dst_var = NULL; \
 137      GET_FIELD(dst_var, cls, name, signature);
 138 
 139 #define GET_FIELD_RETURN(dst_var, cls, name, signature, ret) \
 140      if (dst_var == NULL) { \
 141          dst_var = (*env)->GetFieldID(env, cls, name, signature); \
 142      } \
 143      LOG_NULL(dst_var, name); \
 144      CHECK_NULL_RETURN(dst_var, ret);
 145 
 146 #define DECLARE_FIELD_RETURN(dst_var, cls, name, signature, ret) \
 147      static jfieldID dst_var = NULL; \
 148      GET_FIELD_RETURN(dst_var, cls, name, signature, ret);
 149 
 150 #define GET_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret) \
 151      if (dst_var == NULL) { \
 152          dst_var = (*env)->GetStaticFieldID(env, cls, name, signature); \
 153      } \
 154      LOG_NULL(dst_var, name); \
 155      CHECK_NULL_RETURN(dst_var, ret);
 156 
 157 #define DECLARE_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret) \
 158      static jfieldID dst_var = NULL; \
 159      GET_STATIC_FIELD_RETURN(dst_var, cls, name, signature, ret);
 160 
 161 /*********       EXCEPTION_HANDLING    *********/
 162 
 163 /*
 164  * Some explanation to set context of the bigger picture.
 165  * Before returning to Java from JNI, NSExceptions are caught - so long as
 166  * the body of the native method is wrapped in the ENTER/EXIT macros.
 167  * So if we want to directly return to Java from some nested Objective-C
 168  * function when detecting a Java exception, we just need to raise an
 169  * NSException. Then clear that right before returning to Java,
 170  * leaving the Java exception to be seen back in Java-land.
 171  *
 172  * But if the current thread is the Appkit thread we might as well clear
 173  * the Java Exception right now since there's nothing to receive it.
 174  * In such a case control will propagate back to the run loop which might
 175  * terminate the application. One drawback of that is that the location of
 176  * termination does not show where the NSException originated.
 177  * And for whatever reason, something swallows that exception.
 178  * So as a debugging aid, when on the AppKit thread we can provide a
 179  * way (via an env. var.) to log the location.
 180  * Additionally provide a similar way to prevent the NSException being
 181  * raised and instead just clear the Java Exception.
 182  * Together these provide alternate behaviours for more debugging info
 183  * or maybe a way for the app to continue running depending on the exact
 184  * nature of the problem that has been detected and how survivable it is.
 185  */
 186 #define CHECK_EXCEPTION() \
 187     if ((*env)->ExceptionOccurred(env) != NULL) { \
 188         if ([NSThread isMainThread] == YES) { \
 189             if (getenv("JNU_APPKIT_TRACE")) { \
 190                 (*env)->ExceptionDescribe(env); \
 191                 NSLog(@"%@",[NSThread callStackSymbols]); \
 192               } else { \
 193                   (*env)->ExceptionClear(env); \
 194               } \
 195          }  \
 196         if (getenv("JNU_NO_COCOA_EXCEPTION") == NULL) { \
 197             [NSException raise:NSGenericException format:@"Java Exception"]; \
 198         } else { \
 199             (*env)->ExceptionClear(env); \
 200         } \
 201     };
 202 
 203 #define CHECK_EXCEPTION_NULL_RETURN(x, y) \
 204     CHECK_EXCEPTION(); \
 205     if ((x) == NULL) { \
 206        return y; \
 207     };
 208 
 209 /* Create a pool and initiate a try block to catch any exception */
 210 #define JNI_COCOA_ENTER(env) \
 211  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \
 212  @try {
 213 
 214 /* Don't allow NSExceptions to escape to Java.
 215  * If there is a Java exception that has been thrown that should escape.
 216  * And ensure we drain the auto-release pool.
 217  */
 218 #define JNI_COCOA_EXIT(env) \
 219  } \
 220  @catch (NSException *e) { \
 221      NSLog(@"%@", [e callStackSymbols]); \
 222  } \
 223  @finally { \
 224     [pool drain]; \
 225  };
 226 
 227 /* Same as above but adds a clean up action.
 228  * Requires that whatever is being cleaned up is in scope.
 229  */
 230 #define JNI_COCOA_EXIT_WITH_ACTION(env, action) \
 231  } \
 232  @catch (NSException *e) { \
 233      { action; }; \
 234      NSLog(@"%@", [e callStackSymbols]); \
 235  } \
 236  @finally { \
 237     [pool drain]; \
 238  };
 239 
 240 /********        STRING CONVERSION SUPPORT    *********/
 241 
 242 JNIEXPORT NSString* JavaStringToNSString(JNIEnv *env, jstring jstr);
 243 
 244 JNIEXPORT jstring NSStringToJavaString(JNIEnv* env, NSString *str);
 245 
 246 JNIEXPORT NSString* NormalizedPathNSStringFromJavaString(JNIEnv *env, jstring pathStr);
 247 
 248 JNIEXPORT jstring NormalizedPathJavaStringFromNSString(JNIEnv* env, NSString *str);
 249 
 250 #endif /* __JNIUTILITIES_H */