diff --git a/test-app/runtime/src/main/cpp/com_tns_Runtime.cpp b/test-app/runtime/src/main/cpp/com_tns_Runtime.cpp index 5395536be..a05d869fb 100644 --- a/test-app/runtime/src/main/cpp/com_tns_Runtime.cpp +++ b/test-app/runtime/src/main/cpp/com_tns_Runtime.cpp @@ -3,13 +3,65 @@ #include "NativeScriptException.h" #include "CallbackHandlers.h" #include +#include using namespace std; using namespace tns; +// Forward declarations for natives that must be explicitly registered via +// RegisterNatives. Dynamic JNI lookup of @CriticalNative / @FastNative methods +// is unimplemented on Android 8-10 and buggy on Android 11, so for those +// versions the Java side dispatches to the *Legacy variants (auto-bound). +// On Android 12+ explicit registration also works, so we always register. +static jint generateNewObjectIdCritical_impl(jint runtimeId); +static jboolean notifyGcFast_impl(JNIEnv* env, jobject obj, jint runtimeId); +static jint getCurrentRuntimeIdCritical_impl(); +static jint getPointerSizeCritical_impl(); +static void setManualInstrumentationModeFast_impl(JNIEnv* env, jclass clazz, jstring mode); + +static void RegisterOptimizedNatives(JNIEnv* env) { + jclass cls = env->FindClass("com/tns/Runtime"); + if (cls == nullptr) { + __android_log_print(ANDROID_LOG_ERROR, "TNS.Native", + "Failed to find com/tns/Runtime for RegisterNatives"); + return; + } + + // @CriticalNative: signatures must omit JNIEnv* / jclass. + static const JNINativeMethod criticalMethods[] = { + {const_cast("generateNewObjectIdCritical"), const_cast("(I)I"), reinterpret_cast(generateNewObjectIdCritical_impl)}, + {const_cast("getCurrentRuntimeIdCritical"), const_cast("()I"), reinterpret_cast(getCurrentRuntimeIdCritical_impl)}, + {const_cast("getPointerSizeCritical"), const_cast("()I"), reinterpret_cast(getPointerSizeCritical_impl)}, + }; + // @FastNative: standard JNI ABI (JNIEnv*, jobject/jclass, ...). + static const JNINativeMethod fastMethods[] = { + {const_cast("notifyGcFast"), const_cast("(I)Z"), reinterpret_cast(notifyGcFast_impl)}, + {const_cast("setManualInstrumentationModeFast"), const_cast("(Ljava/lang/String;)V"), reinterpret_cast(setManualInstrumentationModeFast_impl)}, + }; + + if (env->RegisterNatives(cls, criticalMethods, + sizeof(criticalMethods) / sizeof(criticalMethods[0])) < 0) { + __android_log_print(ANDROID_LOG_ERROR, "TNS.Native", + "RegisterNatives failed for @CriticalNative methods"); + env->ExceptionClear(); + } + if (env->RegisterNatives(cls, fastMethods, + sizeof(fastMethods) / sizeof(fastMethods[0])) < 0) { + __android_log_print(ANDROID_LOG_ERROR, "TNS.Native", + "RegisterNatives failed for @FastNative methods"); + env->ExceptionClear(); + } + env->DeleteLocalRef(cls); +} + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { try { Runtime::Init(vm, reserved); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK && env != nullptr) { + RegisterOptimizedNatives(env); + } } catch (NativeScriptException& e) { e.ReThrowToJava(); } catch (std::exception e) { @@ -25,8 +77,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_VERSION_1_6; } -// @FastNative signature - optimized JNI, keeps JNIEnv* for jstring handling -extern "C" JNIEXPORT void Java_com_tns_Runtime_SetManualInstrumentationMode(JNIEnv* _env, jobject obj, jstring mode) { +static void setManualInstrumentationModeFast_impl(JNIEnv* env, jclass clazz, jstring mode) { try { Runtime::SetManualInstrumentationMode(mode); } catch (...) { @@ -35,6 +86,11 @@ extern "C" JNIEXPORT void Java_com_tns_Runtime_SetManualInstrumentationMode(JNIE } } +// Auto-bound legacy variant for Android < 8.0 (annotation absent / unsupported). +extern "C" JNIEXPORT void Java_com_tns_Runtime_setManualInstrumentationModeLegacy(JNIEnv* _env, jclass clazz, jstring mode) { + setManualInstrumentationModeFast_impl(_env, clazz, mode); +} + extern "C" JNIEXPORT void Java_com_tns_Runtime_initNativeScript(JNIEnv* _env, jobject obj, jint runtimeId, jstring filesPath, jstring nativeLibDir, jboolean verboseLoggingEnabled, jboolean isDebuggable, jstring packageName, jobjectArray args, jstring callingDir, jint maxLogcatObjectSize, jboolean forceLog) { try { Runtime::Init(_env, obj, runtimeId, filesPath, nativeLibDir, verboseLoggingEnabled, isDebuggable, packageName, args, callingDir, maxLogcatObjectSize, forceLog); @@ -215,8 +271,8 @@ extern "C" JNIEXPORT void Java_com_tns_Runtime_createJSInstanceNative(JNIEnv* _e } } -// @CriticalNative signature - no JNIEnv* or jobject parameters -extern "C" JNIEXPORT jint Java_com_tns_Runtime_generateNewObjectId(jint runtimeId) { +// @CriticalNative ABI: no JNIEnv* / jclass. +static jint generateNewObjectIdCritical_impl(jint runtimeId) { try { auto runtime = TryGetRuntime(runtimeId); if (runtime == nullptr) { @@ -238,8 +294,12 @@ extern "C" JNIEXPORT jint Java_com_tns_Runtime_generateNewObjectId(jint runtimeI return 0; } -// @FastNative signature - optimized JNI, keeps JNIEnv* for NotifyGC -extern "C" JNIEXPORT jboolean Java_com_tns_Runtime_notifyGc(JNIEnv* env, jobject obj, jint runtimeId) { +extern "C" JNIEXPORT jint Java_com_tns_Runtime_generateNewObjectIdLegacy(JNIEnv* env, jclass clazz, jint runtimeId) { + return generateNewObjectIdCritical_impl(runtimeId); +} + +// @FastNative ABI: standard JNI signature. +static jboolean notifyGcFast_impl(JNIEnv* env, jobject obj, jint runtimeId) { auto runtime = TryGetRuntime(runtimeId); if (runtime == nullptr) { return JNI_FALSE; @@ -249,6 +309,10 @@ extern "C" JNIEXPORT jboolean Java_com_tns_Runtime_notifyGc(JNIEnv* env, jobject return success; } +extern "C" JNIEXPORT jboolean Java_com_tns_Runtime_notifyGcLegacy(JNIEnv* env, jobject obj, jint runtimeId) { + return notifyGcFast_impl(env, obj, runtimeId); +} + extern "C" JNIEXPORT void Java_com_tns_Runtime_lock(JNIEnv* env, jobject obj, jint runtimeId) { auto runtime = TryGetRuntime(runtimeId); if (runtime != nullptr) { @@ -291,13 +355,17 @@ extern "C" JNIEXPORT void Java_com_tns_Runtime_passExceptionToJsNative(JNIEnv* e } } -// @CriticalNative signature - no JNIEnv* or jobject parameters -extern "C" JNIEXPORT jint Java_com_tns_Runtime_getPointerSize() { +// @CriticalNative ABI: no JNIEnv* / jclass. +static jint getPointerSizeCritical_impl() { return sizeof(void*); } -// @CriticalNative signature - no JNIEnv* or jobject parameters -extern "C" JNIEXPORT jint Java_com_tns_Runtime_getCurrentRuntimeId() { +extern "C" JNIEXPORT jint Java_com_tns_Runtime_getPointerSizeLegacy(JNIEnv* env, jclass clazz) { + return getPointerSizeCritical_impl(); +} + +// @CriticalNative ABI: no JNIEnv* / jclass. +static jint getCurrentRuntimeIdCritical_impl() { Isolate* isolate = Isolate::TryGetCurrent(); if (isolate == nullptr) { return -1; @@ -308,6 +376,10 @@ extern "C" JNIEXPORT jint Java_com_tns_Runtime_getCurrentRuntimeId() { return id; } +extern "C" JNIEXPORT jint Java_com_tns_Runtime_getCurrentRuntimeIdLegacy(JNIEnv* env, jclass clazz) { + return getCurrentRuntimeIdCritical_impl(); +} + extern "C" JNIEXPORT void Java_com_tns_Runtime_WorkerGlobalOnMessageCallback(JNIEnv* env, jobject obj, jint runtimeId, jstring msg) { // Worker Thread runtime auto runtime = TryGetRuntime(runtimeId); diff --git a/test-app/runtime/src/main/java/com/tns/Runtime.java b/test-app/runtime/src/main/java/com/tns/Runtime.java index 0e565526d..a76903653 100644 --- a/test-app/runtime/src/main/java/com/tns/Runtime.java +++ b/test-app/runtime/src/main/java/com/tns/Runtime.java @@ -53,11 +53,30 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat private native void createJSInstanceNative(int runtimeId, Object javaObject, int javaObjectID, String canonicalName); + // Dynamic JNI lookup for @CriticalNative / @FastNative is unimplemented on Android 8-10 + // and buggy on Android 11; it works reliably only on Android 12+ (API 31). + // For 8-11 the annotated methods must be bound via RegisterNatives in JNI_OnLoad. + // The 26-30 path below uses the registered annotated variants; everything older + // falls back to standard JNI auto-bind. + private static final boolean SUPPORTS_OPTIMIZED_NATIVE = android.os.Build.VERSION.SDK_INT >= 26; + @CriticalNative - private static native int generateNewObjectId(int runtimeId); + private static native int generateNewObjectIdCritical(int runtimeId); + private static native int generateNewObjectIdLegacy(int runtimeId); + + private static int generateNewObjectId(int runtimeId) { + return SUPPORTS_OPTIMIZED_NATIVE + ? generateNewObjectIdCritical(runtimeId) + : generateNewObjectIdLegacy(runtimeId); + } @FastNative - private native boolean notifyGc(int runtimeId); + private native boolean notifyGcFast(int runtimeId); + private native boolean notifyGcLegacy(int runtimeId); + + private boolean notifyGc(int runtimeId) { + return SUPPORTS_OPTIMIZED_NATIVE ? notifyGcFast(runtimeId) : notifyGcLegacy(runtimeId); + } private native void lock(int runtimeId); @@ -66,13 +85,32 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat private native void passExceptionToJsNative(int runtimeId, Throwable ex, String message, String fullStackTrace, String jsStackTrace, boolean isDiscarded); @CriticalNative - private static native int getCurrentRuntimeId(); + private static native int getCurrentRuntimeIdCritical(); + private static native int getCurrentRuntimeIdLegacy(); + + public static int getCurrentRuntimeId() { + return SUPPORTS_OPTIMIZED_NATIVE ? getCurrentRuntimeIdCritical() : getCurrentRuntimeIdLegacy(); + } @CriticalNative - public static native int getPointerSize(); + private static native int getPointerSizeCritical(); + private static native int getPointerSizeLegacy(); + + public static int getPointerSize() { + return SUPPORTS_OPTIMIZED_NATIVE ? getPointerSizeCritical() : getPointerSizeLegacy(); + } @FastNative - public static native void SetManualInstrumentationMode(String mode); + private static native void setManualInstrumentationModeFast(String mode); + private static native void setManualInstrumentationModeLegacy(String mode); + + public static void SetManualInstrumentationMode(String mode) { + if (SUPPORTS_OPTIMIZED_NATIVE) { + setManualInstrumentationModeFast(mode); + } else { + setManualInstrumentationModeLegacy(mode); + } + } private static native void WorkerGlobalOnMessageCallback(int runtimeId, String message);