diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index e47e5cb18..32d9b66f8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -221,6 +221,12 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { removeStringEntryFunction.setExportName("reportGarbageCollectedString"); } + var exceptionMessageRef = new MethodReference(Throwable.class, "getMessage", Throwable.class); + if (controller.getDependencyInfo().getMethod(exceptionMessageRef) != null) { + var exceptionMessageFunction = declarationsGenerator.functions().forInstanceMethod(exceptionMessageRef); + exceptionMessageFunction.setExportName("exceptionMessage"); + } + moduleGenerator.generate(); customGenerators.contributeToModule(module); adjustModuleMemory(module); diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java index 7434eb9e9..20ddf69d7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java @@ -19,6 +19,8 @@ import java.util.Arrays; import java.util.List; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; +import org.teavm.dependency.AbstractDependencyListener; +import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.model.MethodReference; @@ -105,6 +107,17 @@ public class WasmGCDependencies { analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "throwCloneNotSupportedException", void.class)).use(); analyzer.linkMethod(new MethodReference(NullPointerException.class, "", void.class)).use(); + + analyzer.addDependencyListener(new AbstractDependencyListener() { + @Override + public void classReached(DependencyAgent agent, String className) { + if (className.equals(Throwable.class.getName())) { + agent.linkMethod(new MethodReference(Throwable.class, "getMessage", String.class)) + .propagate(0, Throwable.class) + .use(); + } + } + }); } private void contributeInitializerUtils() { diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js b/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js index 798cd57f4..86bf6de6d 100644 --- a/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js +++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js @@ -38,6 +38,24 @@ TeaVM.wasm = function() { imports.teavmMath = Math; } + let javaExceptionSymbol = Symbol("javaException"); + class JavaError extends Error { + constructor(javaException) { + super(); + this[javaExceptionSymbol] = javaException; + } + get message() { + let exceptionMessage = exports.exceptionMessage; + if (typeof exceptionMessage === "function") { + let message = exceptionMessage(this[javaExceptionSymbol]); + if (message != null) { + return stringToJava(message); + } + } + return "(could not fetch message)"; + } + } + function dateImports(imports) { imports.teavmDate = { currentTimeMillis: () => new Date().getTime(), @@ -119,7 +137,6 @@ TeaVM.wasm = function() { let javaObjectSymbol = Symbol("javaObject"); let functionsSymbol = Symbol("functions"); let functionOriginSymbol = Symbol("functionOrigin"); - let javaExceptionSymbol = Symbol("javaException"); let jsWrappers = new WeakMap(); let javaWrappers = new WeakMap(); @@ -179,9 +196,8 @@ TeaVM.wasm = function() { return wrapper; } } - let wrapper = new Error(); + let wrapper = new JavaError(javaException); javaExceptionWrappers.set(javaException, new WeakRef(wrapper)); - wrapper[javaExceptionSymbol] = javaException; return wrapper; } } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyDelegate.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyDelegate.java index 52f3d907c..5b6d9ca62 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyDelegate.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSBodyDelegate.java @@ -15,5 +15,5 @@ */ package org.teavm.jso.impl; -@interface JSBodyDelegate { +public @interface JSBodyDelegate { } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSDependencies.java b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSDependencies.java index 80f80f6c8..642b5725a 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSDependencies.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/wasmgc/WasmGCJSDependencies.java @@ -22,6 +22,7 @@ import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.MethodDependency; import org.teavm.jso.JSObject; import org.teavm.jso.impl.JS; +import org.teavm.jso.impl.JSBodyDelegate; import org.teavm.jso.impl.JSWrapper; import org.teavm.model.MethodReference; @@ -33,15 +34,30 @@ class WasmGCJSDependencies extends AbstractDependencyListener { @Override public void methodReached(DependencyAgent agent, MethodDependency method) { - if (method.getMethod().getOwnerName().equals(JS.class.getName())) { - if (method.getMethod().getName().equals("jsArrayItem")) { - method.getVariable(1).getArrayItem().connect(method.getResult()); + var methodReader = method.getMethod(); + if (methodReader.getOwnerName().equals(JS.class.getName())) { + switch (methodReader.getName()) { + case "jsArrayItem": + method.getVariable(1).getArrayItem().connect(method.getResult()); + break; + case "invoke": + case "apply": + case "construct": + case "get": + case "getPure": + case "set": + case "setPure": + case "global": + method.getThrown().propagate(agent.getType(WasmGCExceptionWrapper.class.getName())); + break; } - } else if (method.getMethod().getOwnerName().equals(JSWrapper.class.getName())) { - if (method.getMethod().getName().equals("wrap")) { + } else if (methodReader.getOwnerName().equals(JSWrapper.class.getName())) { + if (methodReader.getName().equals("wrap")) { agent.linkMethod(new MethodReference(JSWrapper.class, "createWrapper", JSObject.class, Object.class)) .use(); } + } else if (methodReader.getAnnotations().get(JSBodyDelegate.class.getName()) != null) { + method.getThrown().propagate(agent.getType(WasmGCExceptionWrapper.class.getName())); } } 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 f0d8e6dd3..1ca222fca 100644 --- a/tests/src/test/java/org/teavm/jso/test/ExceptionsTest.java +++ b/tests/src/test/java/org/teavm/jso/test/ExceptionsTest.java @@ -27,7 +27,6 @@ import org.teavm.jso.core.JSError; import org.teavm.junit.EachTestCompiledSeparately; import org.teavm.junit.OnlyPlatform; import org.teavm.junit.SkipJVM; -import org.teavm.junit.SkipPlatform; import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TestPlatform; @@ -103,7 +102,6 @@ public class ExceptionsTest { } @Test - @SkipPlatform(TestPlatform.WEBASSEMBLY_GC) public void catchNativeExceptionAsRuntimeException() { StringBuilder sb = new StringBuilder(); try {