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 79b1d7c10..57848fe83 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -251,6 +251,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { exceptionCons.use(); exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchMethodError.class.getName())); exceptionCons.getVariable(1).propagate(dependencyAnalyzer.getType("java.lang.String")); + + exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference( + RuntimeException.class, "", String.class, void.class), null); + exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName())); + exceptionCons.getVariable(1).propagate(dependencyAnalyzer.getType("java.lang.String")); + exceptionCons.use(); } @Override 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 0fcefa0d9..e88c58969 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 @@ -50,6 +50,7 @@ public class RuntimeRenderer { renderRuntimeNullCheck(); renderRuntimeIntern(); renderRuntimeThreads(); + renderRuntimeCreateException(); } catch (NamingException e) { throw new RenderingException("Error rendering runtime methods. See a cause for details", e); } catch (IOException e) { @@ -160,4 +161,13 @@ public class RuntimeRenderer { .append("(t);").softNewLine(); writer.outdent().append("}").newLine(); } + + private void renderRuntimeCreateException() throws IOException { + writer.append("function $rt_createException(message)").ws().append("{").indent().softNewLine(); + writer.append("return "); + writer.append(writer.getNaming().getNameForInit(new MethodReference(RuntimeException.class, + "", String.class, void.class))); + writer.append("(message);").softNewLine(); + writer.outdent().append("}").newLine(); + } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index a31152782..88d188a8d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -1400,24 +1400,46 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { visitStatements(protectedBody); writer.outdent().append("}").ws().append("catch").ws().append("($$e)") .ws().append("{").indent().softNewLine(); - writer.append("$$je").ws().append("=").ws().append("$$e.$javaException;").softNewLine(); + writer.append("$$je").ws().append("=").ws().append("$rt_wrapException($$e);").softNewLine(); + boolean first = true; + boolean defaultHandlerOccurred = false; for (TryCatchStatement catchClause : sequence) { - writer.append("if").ws().append("($$je"); - if (catchClause.getExceptionType() != null) { - writer.ws().append("&&").ws().append("$$je instanceof ") - .appendClass(catchClause.getExceptionType()); + if (!first) { + writer.ws().append("else"); } - writer.append(")").ws().append("{").indent().softNewLine(); + if (catchClause.getExceptionType() != null) { + if (!first) { + writer.append(" "); + } + writer.append("if").ws().append("($$je instanceof ").appendClass(catchClause.getExceptionType()); + writer.append(")").ws(); + } else { + defaultHandlerOccurred = true; + } + + if (catchClause.getExceptionType() != null || !first) { + writer.append("{").indent().softNewLine(); + } + if (catchClause.getExceptionVariable() != null) { writer.append(variableName(catchClause.getExceptionVariable())).ws().append("=").ws() .append("$$je;").softNewLine(); } visitStatements(catchClause.getHandler()); - writer.outdent().append("}").ws().append("else "); + + if (catchClause.getExceptionType() != null || !first) { + writer.outdent().append("}"); + } + + first = false; + } + if (!defaultHandlerOccurred) { + writer.ws().append("else").ws().append("{").indent().softNewLine(); + writer.append("throw $$je;").softNewLine(); + writer.outdent().append("}").softNewLine(); + } else { + writer.softNewLine(); } - writer.append("{").indent().softNewLine(); - writer.append("throw $$e;").softNewLine(); - writer.outdent().append("}").softNewLine(); writer.outdent().append("}").softNewLine(); } catch (IOException e) { throw new RenderingException("IO error occurred", e); 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 022021dda..f639d4851 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -614,10 +614,18 @@ function $rt_intBitsToFloat(n) { function $rt_javaException(e) { return e instanceof Error && typeof e.$javaException === 'object' ? e.$javaException : null; } - function $rt_jsException(e) { return typeof e.$jsException === 'object' ? e.$jsException : null; } +function $rt_wrapException(err) { + var ex = err.$javaException; + if (!ex) { + ex = $rt_createException($rt_str("(JavaScript) " + err.toString())); + err.$javaException = ex; + ex.$jsException = err; + } + return ex; +} function $dbg_class(obj) { var cls = obj.constructor; diff --git a/tests/src/test/java/org/teavm/jso/test/ExceptionsTest.java b/tests/src/test/java/org/teavm/jso/test/ExceptionsTest.java index c3de654bf..ed4622cf6 100644 --- a/tests/src/test/java/org/teavm/jso/test/ExceptionsTest.java +++ b/tests/src/test/java/org/teavm/jso/test/ExceptionsTest.java @@ -96,6 +96,18 @@ public class ExceptionsTest { assertEquals("foobar", sb.toString()); } + @Test + public void catchNativeExceptionAsRuntimeException() { + StringBuilder sb = new StringBuilder(); + try { + throwNativeException(); + } catch (RuntimeException e) { + sb.append(e.getMessage()); + } + + assertEquals("(JavaScript) Error: foo", sb.toString()); + } + @JSBody(params = "runnable", script = "runnable();") private static native void runJsCode(JSRunnable runnable);