Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 82 additions & 10 deletions test-app/runtime/src/main/cpp/com_tns_Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,65 @@
#include "NativeScriptException.h"
#include "CallbackHandlers.h"
#include <sstream>
#include <android/log.h>

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<char*>("generateNewObjectIdCritical"), const_cast<char*>("(I)I"), reinterpret_cast<void*>(generateNewObjectIdCritical_impl)},
{const_cast<char*>("getCurrentRuntimeIdCritical"), const_cast<char*>("()I"), reinterpret_cast<void*>(getCurrentRuntimeIdCritical_impl)},
{const_cast<char*>("getPointerSizeCritical"), const_cast<char*>("()I"), reinterpret_cast<void*>(getPointerSizeCritical_impl)},
};
// @FastNative: standard JNI ABI (JNIEnv*, jobject/jclass, ...).
static const JNINativeMethod fastMethods[] = {
{const_cast<char*>("notifyGcFast"), const_cast<char*>("(I)Z"), reinterpret_cast<void*>(notifyGcFast_impl)},
{const_cast<char*>("setManualInstrumentationModeFast"), const_cast<char*>("(Ljava/lang/String;)V"), reinterpret_cast<void*>(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<void**>(&env), JNI_VERSION_1_6) == JNI_OK && env != nullptr) {
RegisterOptimizedNatives(env);
}
} catch (NativeScriptException& e) {
e.ReThrowToJava();
} catch (std::exception e) {
Expand All @@ -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 (...) {
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
48 changes: 43 additions & 5 deletions test-app/runtime/src/main/java/com/tns/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand Down
Loading