Skip to content
This repository was archived by the owner on Jul 12, 2024. It is now read-only.

Commit aac88e6

Browse files
committed
Use the legacy try instruction to implement TryCatch.
This will allow to use the `JSTag` behavior for catching JS exceptions, while waiting for Node.js to catch up with V8 12.5. Luckily, we only need that for `TryCatch` and not for `TryFinally`.
1 parent 7adcb8d commit aac88e6

File tree

4 files changed

+59
-21
lines changed

4 files changed

+59
-21
lines changed

wasm/src/main/scala/converters/WasmTextWriter.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ class WasmTextWriter {
318318

319319
private def writeInstr(instr: WasmInstr)(implicit b: WatBuilder): Unit = {
320320
instr match {
321-
case END | ELSE => b.deindent()
322-
case _ => ()
321+
case END | ELSE | _: CATCH | CATCH_ALL => b.deindent()
322+
case _ => ()
323323
}
324324
b.newLine()
325325
b.appendElement(instr.mnemonic)
@@ -333,8 +333,8 @@ class WasmTextWriter {
333333
writeInstrImmediates(instr)
334334

335335
instr match {
336-
case _: StructuredLabeledInstr | ELSE => b.indent()
337-
case _ => ()
336+
case _: StructuredLabeledInstr | ELSE | _: CATCH | CATCH_ALL => b.indent()
337+
case _ => ()
338338
}
339339
}
340340

wasm/src/main/scala/ir2wasm/WasmExpressionBuilder.scala

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,23 @@ import _root_.wasm4s.Defaults
2121
import EmbeddedConstants._
2222

2323
object 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

wasm/src/main/scala/wasm4s/Instructions.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ object WasmInstr {
259259
extends WasmInstr("try_table", 0x1F)
260260
with StructuredLabeledInstr
261261

262+
// Legacy exception system
263+
case class TRY(i: BlockType, label: Option[WasmLabelName] = None)
264+
extends WasmBlockTypeLabeledInstr("try", 0x06, i)
265+
case class CATCH(i: WasmTagName) extends WasmTagInstr("catch", 0x07, i)
266+
case object CATCH_ALL extends WasmSimpleInstr("catch_all", 0x19)
267+
// case class DELEGATE(i: WasmLabelName) extends WasmLabelInstr("delegate", 0x18, i)
268+
case class RETHROW(i: WasmLabelName)
269+
extends WasmLabelInstr("rethrow", 0x09, i)
270+
with StackPolymorphicInstr
271+
262272
// Parametric instructions
263273
// https://webassembly.github.io/spec/core/syntax/instructions.html#parametric-instructions
264274
case object DROP extends WasmSimpleInstr("drop", 0x1A)

wasm/src/main/scala/wasm4s/WasmFunctionContext.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ class WasmFunctionContext private (
388388
while (nestingLevel >= 0 && iter.hasNext) {
389389
val deadCodeInstr = iter.next()
390390
deadCodeInstr match {
391-
case END | ELSE if nestingLevel == 0 =>
391+
case END | ELSE | _: CATCH | CATCH_ALL if nestingLevel == 0 =>
392392
/* We have reached the end of the original block of dead code.
393393
* Actually emit this END or ELSE and then drop `nestingLevel`
394394
* below 0 to end the dead code processing loop.

0 commit comments

Comments
 (0)