Skip to content

Commit

Permalink
Add Branch Prediction Macros and Error checks in Android Initialisati…
Browse files Browse the repository at this point in the history
…on (#438)

* Add Branch Prediction Macros

* Rigorous Error Handling when using JNI
  • Loading branch information
hmelder authored Aug 20, 2024
1 parent 7cf59e4 commit 7474bd8
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 26 deletions.
112 changes: 86 additions & 26 deletions Source/NSProcessInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,31 @@ @implementation NSProcessInfo
static jobject _androidContext = NULL;
static NSString *_androidFilesDir = nil;
static NSString *_androidCacheDir = nil;


/* The following macro assumes that the function's return type is bool.
*/
#define GS_JNI_CHECK(env, obj) \
if (unlikely(JNI_TRUE == (*env)->ExceptionCheck(env))) { \
fprintf(stderr, "File: %s, Line: %d jenv: %p obj: %p Pending exception in JNI environment.\n", __FILE__, __LINE__, env, obj); \
(*env)->ExceptionDescribe(env); \
(*env)->ExceptionClear(env); \
abort(); \
} \
if (unlikely(obj == NULL)) { \
fprintf(stderr, "File: %s, Line: %d jenv: %p JNI returned NULL instead of a valid JNI object.\n", __FILE__, __LINE__, env); \
abort(); \
}
#define GS_JNI_CLS_CHECK(env, cls, name) \
if (unlikely(cls == NULL)) { \
fprintf(stderr, "File: %s, Line: %d jenv: %p JNI returned NULL instead of a valid JNI class object for '%s'.\n", __FILE__, __LINE__, env, name); \
abort(); \
}
#define GS_JNI_METH_CHECK(env, meth) \
if (unlikely(meth == NULL)) { \
fprintf(stderr, "File: %s, Line: %d jenv: %p JNI returned NULL instead of a valid JNI method object.\n", __FILE__, __LINE__, env); \
abort(); \
}
#endif

/*************************************************************************
Expand Down Expand Up @@ -1567,15 +1592,22 @@ - (void) performExpiringActivityWithReason: (NSString *)reason
GSInitializeProcessAndroid(JNIEnv *env, jobject context)
{
jclass contextCls = (*env)->GetObjectClass(env, context);
GS_JNI_CLS_CHECK(env, contextCls, "L/android/content/Context;");

// get package code path (path to APK)
jmethodID packageCodePathMethod = (*env)->GetMethodID(env, contextCls, "getPackageCodePath", "()Ljava/lang/String;");
GS_JNI_METH_CHECK(env, packageCodePathMethod);
jstring packageCodePathJava = (*env)->CallObjectMethod(env, context, packageCodePathMethod);
GS_JNI_CHECK(env, packageCodePathJava);

const char *packageCodePath = (*env)->GetStringUTFChars(env, packageCodePathJava, NULL);

// get package name
jmethodID packageNameMethod = (*env)->GetMethodID(env, contextCls, "getPackageName", "()Ljava/lang/String;");
GS_JNI_CHECK(env, packageNameMethod);
jstring packageNameJava = (*env)->CallObjectMethod(env, context, packageNameMethod);
GS_JNI_CHECK(env, packageNameJava);

const char *packageName = (*env)->GetStringUTFChars(env, packageNameJava, NULL);

// create fake executable path consisting of package code path (without .apk)
Expand All @@ -1591,10 +1623,18 @@ - (void) performExpiringActivityWithReason: (NSString *)reason

// get current locale
jclass localeCls = (*env)->FindClass(env, "java/util/Locale");
GS_JNI_CLS_CHECK(env, localeCls, "Ljava/util/Locale;");

jmethodID localeDefaultMethod = (*env)->GetStaticMethodID(env, localeCls, "getDefault", "()Ljava/util/Locale;");
GS_JNI_METH_CHECK(env, localeDefaultMethod);
jmethodID localeIdMethod = (*env)->GetMethodID(env, localeCls, "toLanguageTag", "()Ljava/lang/String;");
GS_JNI_METH_CHECK(env, localeIdMethod);

jobject localeObj = (*env)->CallStaticObjectMethod(env, localeCls, localeDefaultMethod);
GS_JNI_CHECK(env, localeObj);
jstring localeIdJava = (*env)->CallObjectMethod(env, localeObj, localeIdMethod);
GS_JNI_CHECK(env, localeIdJava);

const char *localeIdOrig = (*env)->GetStringUTFChars(env, localeIdJava, NULL);

// Android uses dashes as delimiters (e.g "en-US"), but we expect underscores
Expand All @@ -1609,46 +1649,55 @@ - (void) performExpiringActivityWithReason: (NSString *)reason
#if __ANDROID_API__ >= 24
// get locales ordered by user preference
jclass localeListCls = (*env)->FindClass(env, "android/os/LocaleList");
GS_JNI_CLS_CHECK(env, localeListCls, "L/android/os/LocaleList;");
jmethodID localeListGetDefaultMethod = (*env)->GetStaticMethodID(env, localeListCls, "getDefault", "()Landroid/os/LocaleList;");
jobject localeListObj = (*env)->CallStaticObjectMethod(env, localeListCls, localeListGetDefaultMethod);
GS_JNI_METH_CHECK(env, localeListGetDefaultMethod);

if (localeCls) {
// Retrieve string representation of the locale list
jmethodID localeListToLanguageTagsMethod = (*env)->GetMethodID(env, localeListCls, "toLanguageTags", "()Ljava/lang/String;");
jstring localeListJava = (*env)->CallObjectMethod(env, localeListObj, localeListToLanguageTagsMethod);

if (localeListJava) {
const char *localeListOrig = (*env)->GetStringUTFChars(env, localeIdJava, NULL);

// Some devices return with it enclosed in []'s so check if both exists before
// removing to ensure it is formatted correctly
if (localeListOrig[0] == '[' && localeListOrig[strlen(localeListOrig) - 1] == ']') {
localeList = strdup(localeListOrig + 1);
localeList[strlen(localeList) - 1] = '\0';
} else {
localeList = strdup(localeListOrig);
}
jobject localeListObj = (*env)->CallStaticObjectMethod(env, localeListCls, localeListGetDefaultMethod);
GS_JNI_CHECK(env, localeListObj);

// NOTE: This is an IETF BCP 47 language tag and may not correspond exactly tocorrespond ll-CC format
// e.g. gsw-u-sd-chzh is a valid BCP 47 language tag, but uses an ISO 639-3 subtag to classify the language.
// There is no easy fix to this, as we use ISO 639-2 subtags internally.
for (int i = 0; localeList[i]; i++) {
if (localeList[i] == '-') {
localeList[i] = '_';
}
}
// Retrieve string representation of the locale list
jmethodID localeListToLanguageTagsMethod = (*env)->GetMethodID(env, localeListCls, "toLanguageTags", "()Ljava/lang/String;");
GS_JNI_METH_CHECK(env, localeListToLanguageTagsMethod);
jstring localeListJava = (*env)->CallObjectMethod(env, localeListObj, localeListToLanguageTagsMethod);
GS_JNI_CHECK(env, localeListJava);

const char *localeListOrig = (*env)->GetStringUTFChars(env, localeIdJava, NULL);

// Some devices return with it enclosed in []'s so check if both exists before
// removing to ensure it is formatted correctly
if (localeListOrig[0] == '[' && localeListOrig[strlen(localeListOrig) - 1] == ']') {
localeList = strdup(localeListOrig + 1);
localeList[strlen(localeList) - 1] = '\0';
} else {
localeList = strdup(localeListOrig);
}

(*env)->ReleaseStringUTFChars(env, localeListJava, localeListOrig);
// NOTE: This is an IETF BCP 47 language tag and may not correspond exactly tocorrespond ll-CC format
// e.g. gsw-u-sd-chzh is a valid BCP 47 language tag, but uses an ISO 639-3 subtag to classify the language.
// There is no easy fix to this, as we use ISO 639-2 subtags internally.
for (int i = 0; localeList[i]; i++) {
if (localeList[i] == '-') {
localeList[i] = '_';
}
}

(*env)->ReleaseStringUTFChars(env, localeListJava, localeListOrig);
#endif


jclass timezoneCls = (*env)->FindClass(env, "java/util/TimeZone");
GS_JNI_CLS_CHECK(env, timezoneCls, "Ljava/util/TimeZone;");
jmethodID timezoneDefaultMethod = (*env)->GetStaticMethodID(env, timezoneCls, "getDefault", "()Ljava/util/TimeZone;");
GS_JNI_METH_CHECK(env, timezoneDefaultMethod);
jmethodID timezoneIdMethod = (*env)->GetMethodID(env, timezoneCls, "getID", "()Ljava/lang/String;");
GS_JNI_METH_CHECK(env, timezoneIdMethod);

jobject timezoneObj = (*env)->CallStaticObjectMethod(env, timezoneCls, timezoneDefaultMethod);
GS_JNI_CHECK(env, timezoneObj);
jstring timezoneIdJava = (*env)->CallObjectMethod(env, timezoneObj, timezoneIdMethod);
GS_JNI_CHECK(env, timezoneIdJava);

const char *timezoneId = (*env)->GetStringUTFChars(env, timezoneIdJava, NULL);

char *localeListValue = "";
Expand Down Expand Up @@ -1691,26 +1740,37 @@ - (void) performExpiringActivityWithReason: (NSString *)reason
[procLock unlock];

jclass contextCls = (*env)->GetObjectClass(env, context);
GS_JNI_CLS_CHECK(env, contextCls, "L/android/content/Context;");

// get File class and path method
jclass fileCls = (*env)->FindClass(env, "java/io/File");
GS_JNI_CLS_CHECK(env, fileCls, "Ljava/io/File;");
jmethodID getAbsolutePathMethod = (*env)->GetMethodID(env, fileCls, "getAbsolutePath", "()Ljava/lang/String;");
GS_JNI_METH_CHECK(env, getAbsolutePathMethod);

// get Android files dir
jmethodID filesDirMethod = (*env)->GetMethodID(env, contextCls, "getFilesDir", "()Ljava/io/File;");
GS_JNI_METH_CHECK(env, filesDirMethod);
jobject filesDirObj = (*env)->CallObjectMethod(env, context, filesDirMethod);
GS_JNI_CHECK(env, filesDirObj);
jstring filesDirJava = (*env)->CallObjectMethod(env, filesDirObj, getAbsolutePathMethod);
GS_JNI_CHECK(env, filesDirJava);
_androidFilesDir = _NSStringFromJString(env, filesDirJava);

// get Android cache dir
jmethodID cacheDirMethod = (*env)->GetMethodID(env, contextCls, "getCacheDir", "()Ljava/io/File;");
GS_JNI_METH_CHECK(env, cacheDirMethod);
jobject cacheDirObj = (*env)->CallObjectMethod(env, context, cacheDirMethod);
GS_JNI_CHECK(env, cacheDirObj);
jstring cacheDirJava = (*env)->CallObjectMethod(env, cacheDirObj, getAbsolutePathMethod);
GS_JNI_CHECK(env, cacheDirJava);
_androidCacheDir = _NSStringFromJString(env, cacheDirJava);

// get asset manager and initialize NSBundle
jmethodID assetManagerMethod = (*env)->GetMethodID(env, contextCls, "getAssets", "()Landroid/content/res/AssetManager;");
GS_JNI_METH_CHECK(env, assetManagerMethod);
jstring assetManagerJava = (*env)->CallObjectMethod(env, context, assetManagerMethod);
GS_JNI_CHECK(env, assetManagerJava);
[NSBundle setJavaAssetManager:assetManagerJava withJNIEnv:env];

// clean up our NSTemporaryDirectory() if it exists
Expand Down
10 changes: 10 additions & 0 deletions Source/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@

#include "config.h"

/* Branch prediction macros
*/
#if !defined(likely)
#define likely(x) __builtin_expect(!!(x), 1)
#endif

#if !defined(unlikely)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif

#if defined(HAVE_STRING_H)
/* For POSIX strerror_r() and others
*/
Expand Down

0 comments on commit 7474bd8

Please sign in to comment.