wasm gc: fix issue with JS exceptions, trying to propagate Java exception message to JS error message

This commit is contained in:
Alexey Andreev 2024-10-04 16:56:47 +02:00
parent 753a028fc9
commit 7aec0763fa
6 changed files with 60 additions and 11 deletions

View File

@ -221,6 +221,12 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
removeStringEntryFunction.setExportName("reportGarbageCollectedString"); 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(); moduleGenerator.generate();
customGenerators.contributeToModule(module); customGenerators.contributeToModule(module);
adjustModuleMemory(module); adjustModuleMemory(module);

View File

@ -19,6 +19,8 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport; 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.dependency.DependencyAnalyzer;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -105,6 +107,17 @@ public class WasmGCDependencies {
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "throwCloneNotSupportedException", analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "throwCloneNotSupportedException",
void.class)).use(); void.class)).use();
analyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class)).use(); analyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", 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() { private void contributeInitializerUtils() {

View File

@ -38,6 +38,24 @@ TeaVM.wasm = function() {
imports.teavmMath = Math; 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) { function dateImports(imports) {
imports.teavmDate = { imports.teavmDate = {
currentTimeMillis: () => new Date().getTime(), currentTimeMillis: () => new Date().getTime(),
@ -119,7 +137,6 @@ TeaVM.wasm = function() {
let javaObjectSymbol = Symbol("javaObject"); let javaObjectSymbol = Symbol("javaObject");
let functionsSymbol = Symbol("functions"); let functionsSymbol = Symbol("functions");
let functionOriginSymbol = Symbol("functionOrigin"); let functionOriginSymbol = Symbol("functionOrigin");
let javaExceptionSymbol = Symbol("javaException");
let jsWrappers = new WeakMap(); let jsWrappers = new WeakMap();
let javaWrappers = new WeakMap(); let javaWrappers = new WeakMap();
@ -179,9 +196,8 @@ TeaVM.wasm = function() {
return wrapper; return wrapper;
} }
} }
let wrapper = new Error(); let wrapper = new JavaError(javaException);
javaExceptionWrappers.set(javaException, new WeakRef(wrapper)); javaExceptionWrappers.set(javaException, new WeakRef(wrapper));
wrapper[javaExceptionSymbol] = javaException;
return wrapper; return wrapper;
} }
} }

View File

@ -15,5 +15,5 @@
*/ */
package org.teavm.jso.impl; package org.teavm.jso.impl;
@interface JSBodyDelegate { public @interface JSBodyDelegate {
} }

View File

@ -22,6 +22,7 @@ import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.jso.JSObject; import org.teavm.jso.JSObject;
import org.teavm.jso.impl.JS; import org.teavm.jso.impl.JS;
import org.teavm.jso.impl.JSBodyDelegate;
import org.teavm.jso.impl.JSWrapper; import org.teavm.jso.impl.JSWrapper;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -33,15 +34,30 @@ class WasmGCJSDependencies extends AbstractDependencyListener {
@Override @Override
public void methodReached(DependencyAgent agent, MethodDependency method) { public void methodReached(DependencyAgent agent, MethodDependency method) {
if (method.getMethod().getOwnerName().equals(JS.class.getName())) { var methodReader = method.getMethod();
if (method.getMethod().getName().equals("jsArrayItem")) { if (methodReader.getOwnerName().equals(JS.class.getName())) {
method.getVariable(1).getArrayItem().connect(method.getResult()); 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())) { } else if (methodReader.getOwnerName().equals(JSWrapper.class.getName())) {
if (method.getMethod().getName().equals("wrap")) { if (methodReader.getName().equals("wrap")) {
agent.linkMethod(new MethodReference(JSWrapper.class, "createWrapper", JSObject.class, Object.class)) agent.linkMethod(new MethodReference(JSWrapper.class, "createWrapper", JSObject.class, Object.class))
.use(); .use();
} }
} else if (methodReader.getAnnotations().get(JSBodyDelegate.class.getName()) != null) {
method.getThrown().propagate(agent.getType(WasmGCExceptionWrapper.class.getName()));
} }
} }

View File

@ -27,7 +27,6 @@ import org.teavm.jso.core.JSError;
import org.teavm.junit.EachTestCompiledSeparately; import org.teavm.junit.EachTestCompiledSeparately;
import org.teavm.junit.OnlyPlatform; import org.teavm.junit.OnlyPlatform;
import org.teavm.junit.SkipJVM; import org.teavm.junit.SkipJVM;
import org.teavm.junit.SkipPlatform;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform; import org.teavm.junit.TestPlatform;
@ -103,7 +102,6 @@ public class ExceptionsTest {
} }
@Test @Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void catchNativeExceptionAsRuntimeException() { public void catchNativeExceptionAsRuntimeException() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
try { try {