From 0f8d36a0807c9dc4d4475fdaab71542dbcc6028c Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 2 Mar 2023 20:21:41 +0100 Subject: [PATCH] JS: display Java exception message in uncaught exceptions in console --- .../backend/javascript/JavaScriptTarget.java | 3 ++ .../javascript/rendering/RuntimeRenderer.java | 13 +++++++ .../org/teavm/backend/javascript/runtime.js | 37 ++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index d17744ab1..0f969eeeb 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -295,6 +295,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { includeStackTraceMethods(dependencyAnalyzer); } + dependencyAnalyzer.linkMethod(new MethodReference(Throwable.class, "getMessage", String.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(Throwable.class, "getCause", Throwable.class)).use(); + dependencyAnalyzer.addDependencyListener(new AbstractDependencyListener() { @Override public void methodReached(DependencyAgent agent, MethodDependency method) { diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java index a56be6e2e..661fcad60 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RuntimeRenderer.java @@ -70,6 +70,7 @@ public class RuntimeRenderer { renderRuntimeUnwrapString(); renderRuntimeObjcls(); renderRuntimeThrowablecls(); + renderRuntimeThrowableMethods(); renderRuntimeNullCheck(); renderRuntimeIntern(); renderRuntimeThreads(); @@ -203,6 +204,18 @@ public class RuntimeRenderer { writer.append(";").softNewLine().outdent().append("}").newLine(); } + private void renderRuntimeThrowableMethods() throws IOException { + writer.append("function $rt_throwableMessage(t)").ws().append("{").indent().softNewLine(); + writer.append("return "); + writer.appendMethodBody(Throwable.class, "getMessage", String.class).append("(t);").softNewLine(); + writer.outdent().append("}").newLine(); + + writer.append("function $rt_throwableCause(t)").ws().append("{").indent().softNewLine(); + writer.append("return "); + writer.appendMethodBody(Throwable.class, "getCause", Throwable.class).append("(t);").softNewLine(); + writer.outdent().append("}").newLine(); + } + private void renderRuntimeThreads() throws IOException { ClassReader threadCls = classSource.get(THREAD_CLASS); MethodReader currentThreadMethod = threadCls != null ? threadCls.getMethod(CURRENT_THREAD_METHOD) : null; diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index e680f7bc1..c29ec4e57 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -284,7 +284,10 @@ var $rt_javaExceptionProp = Symbol("javaException") function $rt_exception(ex) { var err = ex.$jsException; if (!err) { - err = new Error("Java exception thrown"); + var javaCause = $rt_throwableCause(ex); + var jsCause = javaCause !== null ? javaCause.$jsException : undefined; + var cause = typeof jsCause === "object" ? { cause : jsCause } : undefined; + err = new JavaError("Java exception thrown", cause); if (typeof Error.captureStackTrace === "function") { Error.captureStackTrace(err); } @@ -694,6 +697,38 @@ function $rt_intBitsToFloat(n) { return $rt_numberConversionView.getFloat32(0); } +var JavaError; +if (typeof Reflect === 'object') { + var defaultMessage = Symbol("defaultMessage"); + JavaError = function JavaError(message, cause) { + var self = Reflect.construct(Error, [undefined, cause], JavaError); + Object.setPrototypeOf(self, JavaError.prototype); + self[defaultMessage] = message; + return self; + } + JavaError.prototype = Object.create(Error.prototype, { + constructor: { + configurable: true, + writable: true, + value: JavaError + }, + message: { + get: function() { + var javaException = this[$rt_javaExceptionProp]; + if (typeof javaException === 'object') { + var javaMessage = $rt_throwableMessage(javaException); + if (typeof javaMessage === "object") { + return javaMessage.toString(); + } + } + return this[defaultMessage]; + } + } + }); +} else { + JavaError = Error; +} + function $rt_javaException(e) { return e instanceof Error && typeof e[$rt_javaExceptionProp] === 'object' ? e[$rt_javaExceptionProp] : null; }