@@ -21,6 +21,23 @@ import _root_.wasm4s.Defaults
2121import EmbeddedConstants ._
2222
2323object WasmExpressionBuilder {
24+
25+ /** Whether to use the legacy `try` instruction to implement `TryCatch`.
26+ *
27+ * Support for catching JS exceptions was only added to `try_table` in V8 12.5 from April 2024.
28+ * While waiting for Node.js to catch up with V8, we use `try` to implement our `TryCatch`.
29+ *
30+ * We use this "fixed configuration option" to keep the code that implements `TryCatch` using
31+ * `try_table` in the codebase, as code that is actually compiled, so that refactorings apply to
32+ * it as well. It also makes it easier to manually experiment with the new `try_table` encoding,
33+ * which will become available in Chrome v125.
34+ *
35+ * Note that we use `try_table` regardless to implement `TryFinally`. Its `catch_all_ref` handler
36+ * is perfectly happy to catch and rethrow JavaScript exception in Node.js 22. Duplicating that
37+ * implementation for `try` would be a nightmare, given how complex it is already.
38+ */
39+ private final val UseLegacyExceptionsForTryCatch = true
40+
2441 def generateIRBody (tree : IRTrees .Tree , resultType : IRTypes .Type )(implicit
2542 ctx : TypeDefinableWasmContext ,
2643 fctx : WasmFunctionContext
@@ -1690,26 +1707,39 @@ private class WasmExpressionBuilder private (
16901707 private def genTryCatch (t : IRTrees .TryCatch ): IRTypes .Type = {
16911708 val resultType = TypeTransformer .transformResultType(t.tpe)(ctx)
16921709
1693- fctx.block(resultType) { doneLabel =>
1694- fctx.block(Types .WasmRefType .anyref) { catchLabel =>
1695- /* We used to have `resultType` as result of the try_table, wich the
1696- * `BR(doneLabel)` outside of the try_table. Unfortunately it seems
1697- * V8 cannot handle try_table with a result type that is `(ref ...)`.
1698- * The current encoding with `anyref` as result type (to match the
1699- * enclosing block) and the `br` *inside* the `try_table` works.
1700- */
1701- fctx.tryTable(Types .WasmRefType .anyref)(
1702- List (CatchClause .Catch (ctx.exceptionTagName, catchLabel))
1703- ) {
1704- genTree(t.block, t.tpe)
1705- instrs += BR (doneLabel)
1706- }
1707- } // end block $catch
1710+ if (UseLegacyExceptionsForTryCatch ) {
1711+ instrs += TRY (fctx.sigToBlockType(WasmFunctionSignature (Nil , resultType)))
1712+ genTree(t.block, t.tpe)
1713+ instrs += CATCH (ctx.exceptionTagName)
17081714 fctx.withNewLocal(t.errVar.name, Types .WasmRefType .anyref) { exceptionLocal =>
1715+ instrs += ANY_CONVERT_EXTERN
17091716 instrs += LOCAL_SET (exceptionLocal)
17101717 genTree(t.handler, t.tpe)
17111718 }
1712- } // end block $done
1719+ instrs += END
1720+ } else {
1721+ fctx.block(resultType) { doneLabel =>
1722+ fctx.block(Types .WasmRefType .externref) { catchLabel =>
1723+ /* We used to have `resultType` as result of the try_table, with the
1724+ * `BR(doneLabel)` outside of the try_table. Unfortunately it seems
1725+ * V8 cannot handle try_table with a result type that is `(ref ...)`.
1726+ * The current encoding with `externref` as result type (to match the
1727+ * enclosing block) and the `br` *inside* the `try_table` works.
1728+ */
1729+ fctx.tryTable(Types .WasmRefType .externref)(
1730+ List (CatchClause .Catch (ctx.exceptionTagName, catchLabel))
1731+ ) {
1732+ genTree(t.block, t.tpe)
1733+ instrs += BR (doneLabel)
1734+ }
1735+ } // end block $catch
1736+ fctx.withNewLocal(t.errVar.name, Types .WasmRefType .anyref) { exceptionLocal =>
1737+ instrs += ANY_CONVERT_EXTERN
1738+ instrs += LOCAL_SET (exceptionLocal)
1739+ genTree(t.handler, t.tpe)
1740+ }
1741+ } // end block $done
1742+ }
17131743
17141744 if (t.tpe == IRTypes .NothingType )
17151745 instrs += UNREACHABLE
@@ -1762,6 +1792,7 @@ private class WasmExpressionBuilder private (
17621792
17631793 private def genThrow (tree : IRTrees .Throw ): IRTypes .Type = {
17641794 genTree(tree.expr, IRTypes .AnyType )
1795+ instrs += EXTERN_CONVERT_ANY
17651796 instrs += THROW (ctx.exceptionTagName)
17661797
17671798 IRTypes .NothingType
0 commit comments