diff --git a/test-app/runtime/src/main/cpp/runtime/Runtime.cpp b/test-app/runtime/src/main/cpp/runtime/Runtime.cpp index d27b568..fba5c5a 100644 --- a/test-app/runtime/src/main/cpp/runtime/Runtime.cpp +++ b/test-app/runtime/src/main/cpp/runtime/Runtime.cpp @@ -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); @@ -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); @@ -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 diff --git a/test-app/runtime/src/main/cpp/runtime/Runtime.h b/test-app/runtime/src/main/cpp/runtime/Runtime.h index cc6bb77..4c6f702 100644 --- a/test-app/runtime/src/main/cpp/runtime/Runtime.h +++ b/test-app/runtime/src/main/cpp/runtime/Runtime.h @@ -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); @@ -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); diff --git a/test-app/runtime/src/main/cpp/runtime/com_tns_Runtime.cpp b/test-app/runtime/src/main/cpp/runtime/com_tns_Runtime.cpp index 235eb97..c374f10 100644 --- a/test-app/runtime/src/main/cpp/runtime/com_tns_Runtime.cpp +++ b/test-app/runtime/src/main/cpp/runtime/com_tns_Runtime.cpp @@ -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) { diff --git a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp index d727afd..faa7d7d 100644 --- a/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp +++ b/test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp @@ -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) { 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 b286519..c2d1566 100644 --- a/test-app/runtime/src/main/java/com/tns/Runtime.java +++ b/test-app/runtime/src/main/java/com/tns/Runtime.java @@ -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); @@ -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(); @@ -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); } } @@ -668,7 +666,6 @@ private void init(Logger logger, String appName, String nativeLibDir, File rootD @RuntimeCallable public void enableVerboseLogging() { - logger.setEnabled(true); ProxyGenerator.IsLogEnabled = true; } @@ -676,70 +673,84 @@ public void enableVerboseLogging() { @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; @@ -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; } @@ -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]; @@ -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;