GeezerVison is showing github.com — runtime: avoid throwing uncaught exceptions in java · NativeScript/napi-android@351301e
File treeExpand file treeCollapse file tree Open diff view settings test-app/runtime/src/main
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: test-app/runtime/src/main/cpp/runtime/Runtime.cpp +5-2Lines changed: 5 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -85,6 +85,7 @@ Runtime::Runtime(JNIEnv *jEnv, jobject runtime, int id) | 85 | 85 | m_objectManager = new ObjectManager(m_runtime); | 86 | 86 | m_loopTimer = new MessageLoopTimer(); | 87 | 87 | id_to_runtime_cache.Insert(id, this); | | 88 | + pendingError = nullptr; | 88 | 89 |
| 89 | 90 | js_method_cache = new JSMethodCache(this); | 90 | 91 |
| @@ -604,7 +605,7 @@ jobject Runtime::ConvertJsValueToJavaObject(JEnv &jEnv, napi_value value, int cl | 604 | 605 | void | 605 | 606 | Runtime::PassExceptionToJsNative(JNIEnv *jEnv, jobject obj, jthrowable exception, jstring message, | 606 | 607 | jstring fullStackTrace, jstring jsStackTrace, | 607 | | - jboolean isDiscarded) { | | 608 | + jboolean isDiscarded, jboolean isPendingError) { | 608 | 609 | napi_env napiEnv = env; | 609 | 610 |
| 610 | 611 | std::string errMsg = ArgConverter::jstringToString(message); | @@ -642,7 +643,9 @@ Runtime::PassExceptionToJsNative(JNIEnv *jEnv, jobject obj, jthrowable exception | 642 | 643 |
| 643 | 644 | // Pass err to JS | 644 | 645 | NativeScriptException::CallJsFuncWithErr(env, errObj, isDiscarded); | 645 | | - | | 646 | + if (isPendingError) { | | 647 | + pendingError = napi_util::make_ref(env, errObj); | | 648 | + } | 646 | 649 | } | 647 | 650 |
| 648 | 651 | void | |
Collapse file Copy file name to clipboardExpand all lines: test-app/runtime/src/main/cpp/runtime/Runtime.h +18-1Lines changed: 18 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -112,7 +112,7 @@ namespace tns { | 112 | 112 |
| 113 | 113 | void | 114 | 114 | PassExceptionToJsNative(JNIEnv *env, jobject obj, jthrowable exception, jstring message, | 115 | | - jstring fullStackTrace, jstring jsStackTrace, jboolean isDiscarded); | | 115 | + jstring fullStackTrace, jstring jsStackTrace, jboolean isDiscarded, jboolean isPendingError); | 116 | 116 |
| 117 | 117 | void PassUncaughtExceptionFromWorkerToMainHandler(napi_value message, napi_value stackTrace, | 118 | 118 | napi_value filename, int lineno); | @@ -123,7 +123,24 @@ namespace tns { | 123 | 123 |
| 124 | 124 | bool is_destroying = false; | 125 | 125 |
| | 126 | + napi_value getPendingError() { | | 127 | + if (!pendingError) return nullptr; | | 128 | + napi_value error = napi_util::get_ref_value(env, pendingError); | | 129 | + napi_delete_reference(env, pendingError); | | 130 | + pendingError = nullptr; | | 131 | + return error; | | 132 | + } | | 133 | + | | 134 | + void clearPendingError() { | | 135 | + if (!pendingError) return; | | 136 | + napi_delete_reference(env, pendingError); | | 137 | + pendingError = nullptr; | | 138 | + } | | 139 | + | 126 | 140 | private: | | 141 | + napi_ref pendingError; | | 142 | + | | 143 | + | 127 | 144 | Runtime(JNIEnv *env, jobject runtime, int id); | 128 | 145 |
| 129 | 146 | static napi_value GlobalAccessorCallback(napi_env env, napi_callback_info info); | |
Collapse file Copy file name to clipboardExpand all lines: test-app/runtime/src/main/cpp/runtime/com_tns_Runtime.cpp +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -241,14 +241,14 @@ extern "C" JNIEXPORT void Java_com_tns_Runtime_unlock(JNIEnv* env, jobject obj, | 241 | 241 | } | 242 | 242 | } | 243 | 243 |
| 244 | | -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) { | | 244 | +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) { | 245 | 245 | auto runtime = TryGetRuntime(runtimeId); | 246 | 246 | if (runtime == nullptr) return; | 247 | 247 |
| 248 | 248 | NapiScope scope(runtime->GetNapiEnv()); | 249 | 249 |
| 250 | 250 | try { | 251 | | - runtime->PassExceptionToJsNative(jEnv, obj, exception, message, fullStackTrace, jsStackTrace, isDiscarded); | | 251 | + runtime->PassExceptionToJsNative(jEnv, obj, exception, message, fullStackTrace, jsStackTrace, isDiscarded, isPendingError); | 252 | 252 | } catch (NativeScriptException& e) { | 253 | 253 | e.ReThrowToJava(runtime->GetNapiEnv()); | 254 | 254 | } catch (std::exception e) { | |
Collapse file Copy file name to clipboardExpand all lines: test-app/runtime/src/main/cpp/runtime/metadata/MetadataNode.cpp +9-1Lines changed: 9 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -1986,13 +1986,21 @@ napi_value MetadataNode::MethodCallback(napi_env env, napi_callback_info info) { | 1986 | 1986 | } | 1987 | 1987 | } | 1988 | 1988 |
| | 1989 | + | 1989 | 1990 | if (argc == 0 && methodName == PROP_KEY_VALUEOF) { | 1990 | 1991 | return jsThis; | 1991 | 1992 | } else { | | 1993 | + Runtime::GetRuntime(env)->clearPendingError(); | 1992 | 1994 | bool isFromInterface = initialCallbackData->node->IsNodeTypeInterface(); | 1993 | | - return CallbackHandlers::CallJavaMethod(env, jsThis, *className, methodName, entry, | | 1995 | + napi_value result = CallbackHandlers::CallJavaMethod(env, jsThis, *className, methodName, entry, | 1994 | 1996 | isFromInterface, first.isStatic, info, | 1995 | 1997 | argc, argv.data()); | | 1998 | + napi_value error; | | 1999 | + error = Runtime::GetRuntime(env)->getPendingError(); | | 2000 | + if (error) { | | 2001 | + throw NativeScriptException(env, error); | | 2002 | + } | | 2003 | + return result; | 1996 | 2004 | } | 1997 | 2005 |
| 1998 | 2006 | } catch (NativeScriptException &e) { | |
Collapse file Copy file name to clipboardExpand all lines: test-app/runtime/src/main/java/com/tns/Runtime.java +68-60Lines changed: 68 additions & 60 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 37 | 37 | import java.util.concurrent.RunnableFuture; | 38 | 38 | import java.util.concurrent.atomic.AtomicInteger; | 39 | 39 |
| 40 | | -import dalvik.annotation.optimization.FastNative; | 41 | | - | 42 | 40 | public class Runtime { | 43 | 41 | private native void initNativeScript(int runtimeId, String filesPath, String nativeLibDir, boolean verboseLoggingEnabled, boolean isDebuggable, String packageName, | 44 | 42 | Object[] v8Options, String callingDir, int maxLogcatObjectSize, boolean forceLog); | @@ -61,7 +59,7 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat | 61 | 59 |
| 62 | 60 | private native void unlock(int runtimeId); | 63 | 61 |
| 64 | | - private native void passExceptionToJsNative(int runtimeId, Throwable ex, String message, String fullStackTrace, String jsStackTrace, boolean isDiscarded); | | 62 | + private native void passExceptionToJsNative(int runtimeId, Throwable ex, String message, String fullStackTrace, String jsStackTrace, boolean isDiscarded, boolean isPendingError); | 65 | 63 |
| 66 | 64 | private static native int getCurrentRuntimeId(); | 67 | 65 |
| @@ -82,21 +80,21 @@ private native void initNativeScript(int runtimeId, String filesPath, String nat | 82 | 80 | private static native void ResetDateTimeConfigurationCache(int runtimeId); | 83 | 81 |
| 84 | 82 | void passUncaughtExceptionToJs(Throwable ex, String message, String fullStackTrace, String jsStackTrace) { | 85 | | - passExceptionToJsNative(getRuntimeId(), ex, message, fullStackTrace, jsStackTrace, false); | | 83 | + passExceptionToJsNative(getRuntimeId(), ex, message, fullStackTrace, jsStackTrace, false, false); | 86 | 84 | } | 87 | 85 |
| 88 | | - void passDiscardedExceptionToJs(Throwable ex, String prefix) { | | 86 | + void passExceptionToJS(Throwable ex, boolean isPendingError, boolean isDiscarded) { | 89 | 87 | //String message = prefix + ex.getMessage(); | 90 | 88 | // we'd better not prefix the error with something like - Error on "main" thread for reportSupressedException | 91 | 89 | // as it doesn't seem very useful for the users | 92 | | - passExceptionToJsNative(getRuntimeId(), ex, ex.getMessage(), Runtime.getStackTraceErrorMessage(ex), Runtime.getJSStackTrace(ex), true); | | 90 | + passExceptionToJsNative(getRuntimeId(), ex, ex.getMessage(), Runtime.getStackTraceErrorMessage(ex), Runtime.getJSStackTrace(ex), isDiscarded, isPendingError); | 93 | 91 | } | 94 | 92 |
| 95 | 93 | public static void passSuppressedExceptionToJs(Throwable ex, String methodName) { | 96 | 94 | com.tns.Runtime runtime = com.tns.Runtime.getCurrentRuntime(); | 97 | 95 | if (runtime != null) { | 98 | 96 | String errorMessage = "Error on \"" + Thread.currentThread().getName() + "\" thread for " + methodName + "\n"; | 99 | | - runtime.passDiscardedExceptionToJs(ex, ""); | | 97 | + runtime.passExceptionToJS(ex, false , false); | 100 | 98 | } | 101 | 99 | } | 102 | 100 |
| @@ -668,78 +666,91 @@ private void init(Logger logger, String appName, String nativeLibDir, File rootD | 668 | 666 |
| 669 | 667 | @RuntimeCallable | 670 | 668 | public void enableVerboseLogging() { | 671 | | - | 672 | 669 | logger.setEnabled(true); | 673 | 670 | ProxyGenerator.IsLogEnabled = true; | 674 | 671 | } | 675 | 672 |
| 676 | 673 |
| 677 | 674 | @RuntimeCallable | 678 | 675 | public void disableVerboseLogging() { | 679 | | -// logger.setEnabled(false); | 680 | | -// ProxyGenerator.IsLogEnabled = false; | | 676 | + logger.setEnabled(false); | | 677 | + ProxyGenerator.IsLogEnabled = false; | 681 | 678 | } | 682 | 679 |
| 683 | | - public void run() throws NativeScriptException { | | 680 | + public void run() { | 684 | 681 | ManualInstrumentation.Frame frame = ManualInstrumentation.start("Runtime.run"); | 685 | 682 | try { | 686 | 683 | String mainModule = Module.bootstrapApp(); | 687 | 684 | runModule(new File(mainModule)); | | 685 | + } catch (NativeScriptException e){ | | 686 | + passExceptionToJS(e, false, false); | 688 | 687 | } finally { | 689 | 688 | frame.close(); | 690 | 689 | } | 691 | 690 | } | 692 | 691 |
| 693 | | - public void runModule(File jsFile) throws NativeScriptException { | 694 | | - String filePath = jsFile.getPath(); | 695 | | - runModule(getRuntimeId(), filePath); | | 692 | + public void runModule(File jsFile) { | | 693 | + try { | | 694 | + String filePath = jsFile.getPath(); | | 695 | + runModule(getRuntimeId(), filePath); | | 696 | + } catch (NativeScriptException ex) { | | 697 | + passExceptionToJS(ex, false, false); | | 698 | + } | 696 | 699 | } | 697 | 700 |
| 698 | | - public Object runScript(File jsFile) throws NativeScriptException { | 699 | | - return this.runScript(jsFile, true); | | 701 | + public Object runScript(File jsFile) { | | 702 | + try { | | 703 | + return this.runScript(jsFile, true); | | 704 | + } catch (NativeScriptException ex) { | | 705 | + passExceptionToJS(ex, false, false); | | 706 | + return null; | | 707 | + } | 700 | 708 | } | 701 | 709 |
| 702 | | - public Object runScript(File jsFile, final boolean waitForResultOnMainThread) throws NativeScriptException { | | 710 | + public Object runScript(File jsFile, final boolean waitForResultOnMainThread) { | 703 | 711 | Object result = null; | | 712 | + try { | | 713 | + if (jsFile.exists() && jsFile.isFile()) { | | 714 | + final String filePath = jsFile.getAbsolutePath(); | 704 | 715 |
| 705 | | - if (jsFile.exists() && jsFile.isFile()) { | 706 | | - final String filePath = jsFile.getAbsolutePath(); | 707 | | - | 708 | | - boolean isWorkThread = threadScheduler.getThread().equals(Thread.currentThread()); | | 716 | + boolean isWorkThread = threadScheduler.getThread().equals(Thread.currentThread()); | 709 | 717 |
| 710 | | - if (isWorkThread) { | 711 | | - result = runScript(getRuntimeId(), filePath); | 712 | | - } else { | 713 | | - final Object[] arr = new Object[2]; | 714 | | - | 715 | | - Runnable r = new Runnable() { | 716 | | - @Override | 717 | | - public void run() { | 718 | | - synchronized (this) { | 719 | | - try { | 720 | | - arr[0] = runScript(getRuntimeId(), filePath); | 721 | | - } finally { | 722 | | - this.notify(); | 723 | | - arr[1] = Boolean.TRUE; | | 718 | + if (isWorkThread) { | | 719 | + result = runScript(getRuntimeId(), filePath); | | 720 | + } else { | | 721 | + final Object[] arr = new Object[2]; | | 722 | + | | 723 | + Runnable r = new Runnable() { | | 724 | + @Override | | 725 | + public void run() { | | 726 | + synchronized (this) { | | 727 | + try { | | 728 | + arr[0] = runScript(getRuntimeId(), filePath); | | 729 | + } finally { | | 730 | + this.notify(); | | 731 | + arr[1] = Boolean.TRUE; | | 732 | + } | 724 | 733 | } | 725 | 734 | } | 726 | | - } | 727 | | - }; | | 735 | + }; | 728 | 736 |
| 729 | | - boolean success = threadScheduler.post(r); | | 737 | + boolean success = threadScheduler.post(r); | 730 | 738 |
| 731 | | - if (success) { | 732 | | - synchronized (r) { | 733 | | - try { | 734 | | - if (arr[1] == null && waitForResultOnMainThread) { | 735 | | - r.wait(); | | 739 | + if (success) { | | 740 | + synchronized (r) { | | 741 | + try { | | 742 | + if (arr[1] == null && waitForResultOnMainThread) { | | 743 | + r.wait(); | | 744 | + } | | 745 | + } catch (InterruptedException e) { | | 746 | + result = e; | 736 | 747 | } | 737 | | - } catch (InterruptedException e) { | 738 | | - result = e; | 739 | 748 | } | 740 | 749 | } | 741 | 750 | } | 742 | 751 | } | | 752 | + } catch (NativeScriptException ex) { | | 753 | + passExceptionToJS(ex, false, false); | 743 | 754 | } | 744 | 755 |
| 745 | 756 | return result; | @@ -1116,9 +1127,6 @@ private Object getJavaObjectByID(int javaObjectID) throws Exception { | 1116 | 1127 | } | 1117 | 1128 | } | 1118 | 1129 |
| 1119 | | - // Log.d(DEFAULT_LOG_TAG, | 1120 | | - // "Platform.getJavaObjectByID found strong object with id:" + | 1121 | | - // javaObjectID); | 1122 | 1130 | return instance; | 1123 | 1131 | } | 1124 | 1132 |
| @@ -1344,13 +1352,13 @@ private Object dispatchCallJSMethodNative(final int javaObjectID, Class<?> claz, | 1344 | 1352 | try { | 1345 | 1353 | ret = callJSMethodNative(getRuntimeId(), javaObjectID, claz, methodName, returnType, isConstructor, packagedArgs); | 1346 | 1354 | } catch (NativeScriptException e) { | 1347 | | - if (discardUncaughtJsExceptions) { | | 1355 | +// if (discardUncaughtJsExceptions) { | 1348 | 1356 | String errorMessage = "Error on \"" + Thread.currentThread().getName() + "\" thread for callJSMethodNative\n"; | 1349 | | - android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!"); | 1350 | | - passDiscardedExceptionToJs(e, errorMessage); | 1351 | | - } else { | 1352 | | - throw e; | 1353 | | - } | | 1357 | +// android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!"); | | 1358 | + passExceptionToJS(e, true, true); | | 1359 | +// } else { | | 1360 | +// throw e; | | 1361 | +// } | 1354 | 1362 | } | 1355 | 1363 | } else { | 1356 | 1364 | final Object[] arr = new Object[2]; | @@ -1365,13 +1373,13 @@ public void run() { | 1365 | 1373 | final Object[] packagedArgs = packageArgs(tmpArgs); | 1366 | 1374 | arr[0] = callJSMethodNative(getRuntimeId(), javaObjectID, claz, methodName, returnType, isCtor, packagedArgs); | 1367 | 1375 | } catch (NativeScriptException e) { | 1368 | | - if (discardUncaughtJsExceptions) { | | 1376 | +// if (discardUncaughtJsExceptions) { | 1369 | 1377 | String errorMessage = "Error on \"" + Thread.currentThread().getName() + "\" thread for callJSMethodNative\n"; | 1370 | | - passDiscardedExceptionToJs(e, errorMessage); | 1371 | | - android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!"); | 1372 | | - } else { | 1373 | | - throw e; | 1374 | | - } | | 1378 | + passExceptionToJS(e, true, false); | | 1379 | +// android.util.Log.w("Warning", "NativeScript discarding uncaught JS exception!"); | | 1380 | +// } else { | | 1381 | +// throw e; | | 1382 | +// } | 1375 | 1383 | } finally { | 1376 | 1384 | this.notify(); | 1377 | 1385 | arr[1] = Boolean.TRUE; | |
|