Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: avoid throwing uncaught exceptions in java #26

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
7 changes: 5 additions & 2 deletions test-app/runtime/src/main/cpp/runtime/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Runtime::Runtime(JNIEnv *jEnv, jobject runtime, int id)
m_objectManager = new ObjectManager(m_runtime);
m_loopTimer = new MessageLoopTimer();
id_to_runtime_cache.Insert(id, this);
pendingError = nullptr;

js_method_cache = new JSMethodCache(this);

Expand Down Expand Up @@ -604,7 +605,7 @@ jobject Runtime::ConvertJsValueToJavaObject(JEnv &jEnv, napi_value value, int cl
void
Runtime::PassExceptionToJsNative(JNIEnv *jEnv, jobject obj, jthrowable exception, jstring message,
jstring fullStackTrace, jstring jsStackTrace,
jboolean isDiscarded) {
jboolean isDiscarded, jboolean isPendingError) {
napi_env napiEnv = env;

std::string errMsg = ArgConverter::jstringToString(message);
Expand Down Expand Up @@ -642,7 +643,9 @@ Runtime::PassExceptionToJsNative(JNIEnv *jEnv, jobject obj, jthrowable exception

// Pass err to JS
NativeScriptException::CallJsFuncWithErr(env, errObj, isDiscarded);

if (isPendingError) {
pendingError = napi_util::make_ref(env, errObj);
}
}

void
Expand Down
19 changes: 18 additions & 1 deletion test-app/runtime/src/main/cpp/runtime/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ namespace tns {

void
PassExceptionToJsNative(JNIEnv *env, jobject obj, jthrowable exception, jstring message,
jstring fullStackTrace, jstring jsStackTrace, jboolean isDiscarded);
jstring fullStackTrace, jstring jsStackTrace, jboolean isDiscarded, jboolean isPendingError);

void PassUncaughtExceptionFromWorkerToMainHandler(napi_value message, napi_value stackTrace,
napi_value filename, int lineno);
Expand All @@ -123,7 +123,24 @@ namespace tns {

bool is_destroying = false;

napi_value getPendingError() {
if (!pendingError) return nullptr;
napi_value error = napi_util::get_ref_value(env, pendingError);
napi_delete_reference(env, pendingError);
pendingError = nullptr;
return error;
}

void clearPendingError() {
if (!pendingError) return;
napi_delete_reference(env, pendingError);
pendingError = nullptr;
}

private:
napi_ref pendingError;


Runtime(JNIEnv *env, jobject runtime, int id);

static napi_value GlobalAccessorCallback(napi_env env, napi_callback_info info);
Expand Down
4 changes: 2 additions & 2 deletions test-app/runtime/src/main/cpp/runtime/com_tns_Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,14 @@ extern "C" JNIEXPORT void Java_com_tns_Runtime_unlock(JNIEnv* env, jobject obj,
}
}

extern "C" JNIEXPORT void Java_com_tns_Runtime_passExceptionToJsNative(JNIEnv* jEnv, jobject obj, jint runtimeId, jthrowable exception, jstring message, jstring fullStackTrace, jstring jsStackTrace, jboolean isDiscarded) {
extern "C" JNIEXPORT void Java_com_tns_Runtime_passExceptionToJsNative(JNIEnv* jEnv, jobject obj, jint runtimeId, jthrowable exception, jstring message, jstring fullStackTrace, jstring jsStackTrace, jboolean isDiscarded, jboolean isPendingError) {
auto runtime = TryGetRuntime(runtimeId);
if (runtime == nullptr) return;

NapiScope scope(runtime->GetNapiEnv());

try {
runtime->PassExceptionToJsNative(jEnv, obj, exception, message, fullStackTrace, jsStackTrace, isDiscarded);
runtime->PassExceptionToJsNative(jEnv, obj, exception, message, fullStackTrace, jsStackTrace, isDiscarded, isPendingError);
} catch (NativeScriptException& e) {
e.ReThrowToJava(runtime->GetNapiEnv());
} catch (std::exception e) {
Expand Down
10 changes: 9 additions & 1 deletion test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1986,13 +1986,21 @@ napi_value MetadataNode::MethodCallback(napi_env env, napi_callback_info info) {
}
}


if (argc == 0 && methodName == PROP_KEY_VALUEOF) {
return jsThis;
} else {
Runtime::GetRuntime(env)->clearPendingError();
bool isFromInterface = initialCallbackData->node->IsNodeTypeInterface();
return CallbackHandlers::CallJavaMethod(env, jsThis, *className, methodName, entry,
napi_value result = CallbackHandlers::CallJavaMethod(env, jsThis, *className, methodName, entry,
isFromInterface, first.isStatic, info,
argc, argv.data());
napi_value error;
error = Runtime::GetRuntime(env)->getPendingError();
if (error) {
throw NativeScriptException(env, error);
}
return result;
}

} catch (NativeScriptException &e) {
Expand Down
128 changes: 68 additions & 60 deletions test-app/runtime/src/main/java/com/tns/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.atomic.AtomicInteger;

import dalvik.annotation.optimization.FastNative;

public class Runtime {
private native void initNativeScript(int runtimeId, String filesPath, String nativeLibDir, boolean verboseLoggingEnabled, boolean isDebuggable, String packageName,
Object[] v8Options, String callingDir, int maxLogcatObjectSize, boolean forceLog);
Expand All @@ -61,7 +59,7 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat

private native void unlock(int runtimeId);

private native void passExceptionToJsNative(int runtimeId, Throwable ex, String message, String fullStackTrace, String jsStackTrace, boolean isDiscarded);
private native void passExceptionToJsNative(int runtimeId, Throwable ex, String message, String fullStackTrace, String jsStackTrace, boolean isDiscarded, boolean isPendingError);

private static native int getCurrentRuntimeId();

Expand All @@ -82,21 +80,21 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat
private static native void ResetDateTimeConfigurationCache(int runtimeId);

void passUncaughtExceptionToJs(Throwable ex, String message, String fullStackTrace, String jsStackTrace) {
passExceptionToJsNative(getRuntimeId(), ex, message, fullStackTrace, jsStackTrace, false);
passExceptionToJsNative(getRuntimeId(), ex, message, fullStackTrace, jsStackTrace, false, false);
}

void passDiscardedExceptionToJs(Throwable ex, String prefix) {
void passExceptionToJS(Throwable ex, boolean isPendingError, boolean isDiscarded) {
//String message = prefix + ex.getMessage();
// we'd better not prefix the error with something like - Error on "main" thread for reportSupressedException
// as it doesn't seem very useful for the users
passExceptionToJsNative(getRuntimeId(), ex, ex.getMessage(), Runtime.getStackTraceErrorMessage(ex), Runtime.getJSStackTrace(ex), true);
passExceptionToJsNative(getRuntimeId(), ex, ex.getMessage(), Runtime.getStackTraceErrorMessage(ex), Runtime.getJSStackTrace(ex), isDiscarded, isPendingError);
}

public static void passSuppressedExceptionToJs(Throwable ex, String methodName) {
com.tns.Runtime runtime = com.tns.Runtime.getCurrentRuntime();
if (runtime != null) {
String errorMessage = "Error on \"" + Thread.currentThread().getName() + "\" thread for " + methodName + "\n";
runtime.passDiscardedExceptionToJs(ex, "");
runtime.passExceptionToJS(ex, false , false);
}
}

Expand Down Expand Up @@ -668,78 +666,91 @@ private void init(Logger logger, String appName, String nativeLibDir, File rootD

@RuntimeCallable
public void enableVerboseLogging() {

logger.setEnabled(true);
ProxyGenerator.IsLogEnabled = true;
}


@RuntimeCallable
public void disableVerboseLogging() {
// logger.setEnabled(false);
// ProxyGenerator.IsLogEnabled = false;
logger.setEnabled(false);
ProxyGenerator.IsLogEnabled = false;
}

public void run() throws NativeScriptException {
public void run() {
ManualInstrumentation.Frame frame = ManualInstrumentation.start("Runtime.run");
try {
String mainModule = Module.bootstrapApp();
runModule(new File(mainModule));
} catch (NativeScriptException e){
passExceptionToJS(e, false, false);
} finally {
frame.close();
}
}

public void runModule(File jsFile) throws NativeScriptException {
String filePath = jsFile.getPath();
runModule(getRuntimeId(), filePath);
public void runModule(File jsFile) {
try {
String filePath = jsFile.getPath();
runModule(getRuntimeId(), filePath);
} catch (NativeScriptException ex) {
passExceptionToJS(ex, false, false);
}
}

public Object runScript(File jsFile) throws NativeScriptException {
return this.runScript(jsFile, true);
public Object runScript(File jsFile) {
try {
return this.runScript(jsFile, true);
} catch (NativeScriptException ex) {
passExceptionToJS(ex, false, false);
return null;
}
}

public Object runScript(File jsFile, final boolean waitForResultOnMainThread) throws NativeScriptException {
public Object runScript(File jsFile, final boolean waitForResultOnMainThread) {
Object result = null;
try {
if (jsFile.exists() && jsFile.isFile()) {
final String filePath = jsFile.getAbsolutePath();

if (jsFile.exists() && jsFile.isFile()) {
final String filePath = jsFile.getAbsolutePath();

boolean isWorkThread = threadScheduler.getThread().equals(Thread.currentThread());
boolean isWorkThread = threadScheduler.getThread().equals(Thread.currentThread());

if (isWorkThread) {
result = runScript(getRuntimeId(), filePath);
} else {
final Object[] arr = new Object[2];

Runnable r = new Runnable() {
@Override
public void run() {
synchronized (this) {
try {
arr[0] = runScript(getRuntimeId(), filePath);
} finally {
this.notify();
arr[1] = Boolean.TRUE;
if (isWorkThread) {
result = runScript(getRuntimeId(), filePath);
} else {
final Object[] arr = new Object[2];

Runnable r = new Runnable() {
@Override
public void run() {
synchronized (this) {
try {
arr[0] = runScript(getRuntimeId(), filePath);
} finally {
this.notify();
arr[1] = Boolean.TRUE;
}
}
}
}
};
};

boolean success = threadScheduler.post(r);
boolean success = threadScheduler.post(r);

if (success) {
synchronized (r) {
try {
if (arr[1] == null && waitForResultOnMainThread) {
r.wait();
if (success) {
synchronized (r) {
try {
if (arr[1] == null && waitForResultOnMainThread) {
r.wait();
}
} catch (InterruptedException e) {
result = e;
}
} catch (InterruptedException e) {
result = e;
}
}
}
}
} catch (NativeScriptException ex) {
passExceptionToJS(ex, false, false);
}

return result;
Expand Down Expand Up @@ -1116,9 +1127,6 @@ private Object getJavaObjectByID(int javaObjectID) throws Exception {
}
}

// Log.d(DEFAULT_LOG_TAG,
// "Platform.getJavaObjectByID found strong object with id:" +
// javaObjectID);
return instance;
}

Expand Down Expand Up @@ -1344,13 +1352,13 @@ private Object dispatchCallJSMethodNative(final int javaObjectID, Class<?> claz,
try {
ret = callJSMethodNative(getRuntimeId(), javaObjectID, claz, methodName, returnType, isConstructor, packagedArgs);
} catch (NativeScriptException e) {
if (discardUncaughtJsExceptions) {
// if (discardUncaughtJsExceptions) {
String errorMessage = "Error on \"" + Thread.currentThread().getName() + "\" thread for callJSMethodNative\n";
android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!");
passDiscardedExceptionToJs(e, errorMessage);
} else {
throw e;
}
// android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!");
passExceptionToJS(e, true, true);
// } else {
// throw e;
// }
}
} else {
final Object[] arr = new Object[2];
Expand All @@ -1365,13 +1373,13 @@ public void run() {
final Object[] packagedArgs = packageArgs(tmpArgs);
arr[0] = callJSMethodNative(getRuntimeId(), javaObjectID, claz, methodName, returnType, isCtor, packagedArgs);
} catch (NativeScriptException e) {
if (discardUncaughtJsExceptions) {
// if (discardUncaughtJsExceptions) {
String errorMessage = "Error on \"" + Thread.currentThread().getName() + "\" thread for callJSMethodNative\n";
passDiscardedExceptionToJs(e, errorMessage);
android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!");
} else {
throw e;
}
passExceptionToJS(e, true, false);
// android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!");
// } else {
// throw e;
// }
} finally {
this.notify();
arr[1] = Boolean.TRUE;
Expand Down