Skip to content

Commit

Permalink
wasm gc: improve exception stack trace when exception is thrown from …
Browse files Browse the repository at this point in the history
…wasm and rethrown between Java and JS
  • Loading branch information
konsoletyper committed Oct 24, 2024
1 parent 24d672e commit 2e08640
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,14 @@ public Throwable fillInStackTrace() {
stackTrace = (TStackTraceElement[]) (Object) ExceptionHandling.fillStackTrace();
} else if (PlatformDetector.isWebAssemblyGC()) {
lazyStackTrace = takeWasmGCStack();
decorateException(this);
}
return this;
}

@Import(name = "decorateException")
private static native void decorateException(Object obj);

private void ensureStackTrace() {
if (PlatformDetector.isWebAssemblyGC()) {
if (lazyStackTrace != null) {
Expand Down
40 changes: 40 additions & 0 deletions core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,13 @@
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsics;
import org.teavm.backend.wasm.model.WasmCustomSection;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.optimization.WasmUsageCounter;
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
Expand Down Expand Up @@ -279,11 +284,46 @@ public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, Str

moduleGenerator.generate();
customGenerators.contributeToModule(module);
generateExceptionExports(declarationsGenerator);
adjustModuleMemory(module);

emitWasmFile(module, buildTarget, outputName, debugInfoBuilder);
}

private void generateExceptionExports(WasmGCDeclarationsGenerator declarationsGenerator) {
var nativeExceptionField = declarationsGenerator.classInfoProvider().getThrowableNativeOffset();
if (nativeExceptionField < 0) {
return;
}

var throwableType = declarationsGenerator.classInfoProvider().getClassInfo("java.lang.Throwable")
.getStructure();

var getFunction = new WasmFunction(declarationsGenerator.functionTypes.of(
WasmType.Reference.EXTERN, throwableType.getReference()
));
getFunction.setName("teavm.getJsException");
getFunction.setExportName("teavm.getJsException");
var getParam = new WasmLocal(throwableType.getReference(), "javaException");
getFunction.add(getParam);
var getField = new WasmStructGet(throwableType, new WasmGetLocal(getParam), nativeExceptionField);
getFunction.getBody().add(getField);
declarationsGenerator.module.functions.add(getFunction);

var setFunction = new WasmFunction(declarationsGenerator.functionTypes.of(null, throwableType.getReference(),
WasmType.Reference.EXTERN));
setFunction.setName("teavm.setJsException");
setFunction.setExportName("teavm.setJsException");
var setParam = new WasmLocal(throwableType.getReference(), "javaException");
var setValue = new WasmLocal(WasmType.Reference.EXTERN, "jsException");
setFunction.add(setParam);
setFunction.add(setValue);
var setField = new WasmStructSet(throwableType, new WasmGetLocal(setParam),
nativeExceptionField, new WasmGetLocal(setValue));
setFunction.getBody().add(setField);
declarationsGenerator.module.functions.add(setFunction);
}

private WasmGCClassConsumerContext createClassConsumerContext(
ClassReaderSource classes,
WasmGCDeclarationsGenerator generator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int arrayCopyOffset = -1;
private int cloneOffset = -1;
private int servicesOffset = -1;
private int throwableNativeOffset = -1;
private WasmStructure arrayVirtualTableStruct;
private WasmFunction arrayGetObjectFunction;
private WasmFunction arrayLengthObjectFunction;
Expand Down Expand Up @@ -482,6 +483,11 @@ public int getServicesOffset() {
return servicesOffset;
}

@Override
public int getThrowableNativeOffset() {
return throwableNativeOffset;
}

private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) {
classInfo.initializer = target -> {
int kind;
Expand Down Expand Up @@ -1221,6 +1227,11 @@ private void fillSimpleClassFields(List<WasmField> fields, String className) {
var field = new WasmField(WasmType.Reference.EXTERN.asStorage(), "nativeRef");
fields.add(field);
}
if (className.equals("java.lang.Throwable")) {
throwableNativeOffset = fields.size();
var field = new WasmField(WasmType.Reference.EXTERN.asStorage(), "nativeRef");
fields.add(field);
}
if (className.equals("java.lang.Class")) {
var cls = classSource.get("java.lang.Class");
classFlagsOffset = fields.size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public interface WasmGCClassInfoProvider {

int getServicesOffset();

int getThrowableNativeOffset();

default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name));
}
Expand Down
17 changes: 8 additions & 9 deletions core/src/main/js/wasm-gc-runtime/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class JavaError extends Error {
super();
this.#context = context;
this[javaExceptionSymbol] = javaException;
context.exports["teavm.setJsException"](javaException, this);
}
get message() {
let exceptionMessage = this.#context.exports["teavm.exceptionMessage"];
Expand Down Expand Up @@ -180,6 +181,9 @@ function coreImports(imports, context) {
return result;
}
};
},
decorateException(javaException) {
new JavaError(context, javaException);
}
};
}
Expand All @@ -195,7 +199,6 @@ function jsoImports(imports, context) {
let primitiveWrappers = new Map();
let primitiveFinalization = new FinalizationRegistry(token => primitiveWrappers.delete(token));
let hashCodes = new WeakMap();
let javaExceptionWrappers = new WeakMap();
let lastHashCode = 2463534242;
let nextHashCode = () => {
let x = lastHashCode;
Expand Down Expand Up @@ -235,21 +238,17 @@ function jsoImports(imports, context) {
function javaExceptionToJs(e) {
if (e instanceof WebAssembly.Exception) {
let tag = context.exports["teavm.javaException"];
let getJsException = context.exports["teavm.getJsException"];
if (e.is(tag)) {
let javaException = e.getArg(tag, 0);
let extracted = extractException(javaException);
if (extracted !== null) {
return extracted;
}
let wrapperRef = javaExceptionWrappers.get(javaException);
if (typeof wrapperRef != "undefined") {
let wrapper = wrapperRef.deref();
if (typeof wrapper !== "undefined") {
return wrapper;
}
let wrapper = getJsException(javaException);
if (typeof wrapper === "undefined") {
wrapper = new JavaError(context, javaException);
}
let wrapper = new JavaError(context, javaException);
javaExceptionWrappers.set(javaException, new WeakRef(wrapper));
return wrapper;
}
}
Expand Down

0 comments on commit 2e08640

Please sign in to comment.