@@ -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,37 @@ 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 =>
17091715 instrs += LOCAL_SET (exceptionLocal)
17101716 genTree(t.handler, t.tpe)
17111717 }
1712- } // end block $done
1718+ instrs += END
1719+ } else {
1720+ fctx.block(resultType) { doneLabel =>
1721+ fctx.block(Types .WasmRefType .anyref) { catchLabel =>
1722+ /* We used to have `resultType` as result of the try_table, wich the
1723+ * `BR(doneLabel)` outside of the try_table. Unfortunately it seems
1724+ * V8 cannot handle try_table with a result type that is `(ref ...)`.
1725+ * The current encoding with `anyref` as result type (to match the
1726+ * enclosing block) and the `br` *inside* the `try_table` works.
1727+ */
1728+ fctx.tryTable(Types .WasmRefType .anyref)(
1729+ List (CatchClause .Catch (ctx.exceptionTagName, catchLabel))
1730+ ) {
1731+ genTree(t.block, t.tpe)
1732+ instrs += BR (doneLabel)
1733+ }
1734+ } // end block $catch
1735+ fctx.withNewLocal(t.errVar.name, Types .WasmRefType .anyref) { exceptionLocal =>
1736+ instrs += LOCAL_SET (exceptionLocal)
1737+ genTree(t.handler, t.tpe)
1738+ }
1739+ } // end block $done
1740+ }
17131741
17141742 if (t.tpe == IRTypes .NothingType )
17151743 instrs += UNREACHABLE
0 commit comments