Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 5e7081d

Browse files
[interpreter] Parse and convert EH opcodes
Add support for EH opcodes in the parser, AST, encoder, decoder and formatter, and add spec tests. This can already be used to convert the tests to JS, but not run them with the interpreter yet since validation and execution are still missing.
1 parent a5e87e4 commit 5e7081d

File tree

14 files changed

+635
-6
lines changed

14 files changed

+635
-6
lines changed

interpreter/binary/decode.ml

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,29 @@ let rec instr s =
246246
end
247247

248248
| 0x05 -> error s pos "misplaced ELSE opcode"
249-
| 0x06| 0x07 | 0x08 | 0x09 | 0x0a as b -> illegal s pos b
249+
| 0x06 ->
250+
let bt = block_type s in
251+
let es = instr_block s in
252+
let ct = catch_list s in
253+
let ca = if peek s = Some 0x19 then begin
254+
ignore (u8 s);
255+
Some (instr_block s)
256+
end else
257+
None
258+
in
259+
if ct <> [] || ca <> None then begin
260+
end_ s;
261+
try_catch bt es ct ca
262+
end else
263+
begin match op s with
264+
| 0x0b -> try_catch bt es [] None
265+
| 0x18 -> try_delegate bt es (at var s)
266+
| b -> illegal s pos b
267+
end
268+
| 0x07 -> error s pos "misplaced CATCH opcode"
269+
| 0x08 -> throw (at var s)
270+
| 0x09 -> rethrow (at var s)
271+
| 0x0a as b -> illegal s pos b
250272
| 0x0b -> error s pos "misplaced END opcode"
251273

252274
| 0x0c -> br (at var s)
@@ -263,7 +285,10 @@ let rec instr s =
263285
let x = at var s in
264286
call_indirect x y
265287

266-
| 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 as b -> illegal s pos b
288+
| 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 as b -> illegal s pos b
289+
290+
| 0x18 -> error s pos "misplaced DELEGATE opcode"
291+
| 0x19 -> error s pos "misplaced CATCH_ALL opcode"
267292

268293
| 0x1a -> drop
269294
| 0x1b -> select None
@@ -499,11 +524,19 @@ let rec instr s =
499524
and instr_block s = List.rev (instr_block' s [])
500525
and instr_block' s es =
501526
match peek s with
502-
| None | Some (0x05 | 0x0b) -> es
527+
| None | Some (0x05 | 0x07 | 0x0a | 0x0b | 0x18 | 0x19) -> es
503528
| _ ->
504529
let pos = pos s in
505530
let e' = instr s in
506531
instr_block' s (Source.(e' @@ region s pos pos) :: es)
532+
and catch_list s =
533+
if peek s = Some 0x07 then begin
534+
ignore (u8 s);
535+
let tag = at var s in
536+
let instrs = instr_block s in
537+
(tag, instrs) :: catch_list s
538+
end else
539+
[]
507540

508541
let const s =
509542
let c = at instr_block s in

interpreter/binary/encode.ml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,28 @@ let encode m =
156156
op 0x04; block_type bt; list instr es1;
157157
if es2 <> [] then op 0x05;
158158
list instr es2; end_ ()
159-
159+
| TryCatch (bt, es, ct, ca) ->
160+
op 0x06; block_type bt; list instr es;
161+
let catch (tag, es) =
162+
op 0x07; var tag; list instr es
163+
in
164+
list catch ct;
165+
begin match ca with
166+
| None -> ()
167+
| Some es -> op 0x19; list instr es
168+
end;
169+
end_ ()
170+
| TryDelegate (bt, es, x) ->
171+
op 0x06; block_type bt; list instr es;
172+
op 0x18; var x
160173
| Br x -> op 0x0c; var x
161174
| BrIf x -> op 0x0d; var x
162175
| BrTable (xs, x) -> op 0x0e; vec var xs; var x
163176
| Return -> op 0x0f
164177
| Call x -> op 0x10; var x
165178
| CallIndirect (x, y) -> op 0x11; var y; var x
179+
| Throw x -> op 0x08; var x
180+
| Rethrow x -> op 0x09; var x
166181

167182
| Drop -> op 0x1a
168183
| Select None -> op 0x1b

interpreter/syntax/ast.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ and instr' =
114114
| Unary of unop (* unary numeric operator *)
115115
| Binary of binop (* binary numeric operator *)
116116
| Convert of cvtop (* conversion *)
117+
| TryCatch of block_type * instr list * (* try *)
118+
(var * instr list) list * (* catch exception with tag *)
119+
instr list option (* catch_all *)
120+
| TryDelegate of block_type * instr list * (* try *)
121+
var (* delegate to outer handler *)
122+
| Throw of var (* throw exception *)
123+
| Rethrow of var (* rethrow exception *)
117124

118125

119126
(* Globals & Functions *)

interpreter/syntax/free.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ let rec instr (e : instr) =
8787
memories zero
8888
| MemoryInit x -> memories zero ++ datas (var x)
8989
| DataDrop x -> datas (var x)
90+
| TryCatch (bt, es, ct, ca) ->
91+
let catch (tag, es) = events (var tag) ++ block es in
92+
let catch_all = function
93+
| None -> empty
94+
| Some es -> block es in
95+
block es ++ (list catch ct) ++ catch_all ca
96+
| TryDelegate (bt, es, x) -> block es ++ events (var x)
97+
| Throw x -> events (var x)
98+
| Rethrow x -> labels (var x)
9099

91100
and block (es : instr list) =
92101
let free = list instr es in {free with labels = shift free.labels}

interpreter/syntax/operators.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ let select t = Select t
1919
let block bt es = Block (bt, es)
2020
let loop bt es = Loop (bt, es)
2121
let if_ bt es1 es2 = If (bt, es1, es2)
22+
let try_catch bt es ct ca = TryCatch (bt, es, ct, ca)
23+
let try_delegate bt es x = TryDelegate (bt, es, x)
2224
let br x = Br x
2325
let br_if x = BrIf x
2426
let br_table xs x = BrTable (xs, x)
2527

2628
let return = Return
2729
let call x = Call x
2830
let call_indirect x y = CallIndirect (x, y)
31+
let throw x = Throw x
32+
let rethrow x = Rethrow x
2933

3034
let local_get x = LocalGet x
3135
let local_set x = LocalSet x

interpreter/text/arrange.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ let rec instr e =
279279
| Unary op -> unop op, []
280280
| Binary op -> binop op, []
281281
| Convert op -> cvtop op, []
282+
| TryCatch (bt, es, ct, ca) ->
283+
let catch (tag, es) = Node ("catch " ^ var tag, list instr es) in
284+
let catch_all = match ca with
285+
| Some es -> [Node ("catch_all", list instr es)]
286+
| None -> [] in
287+
let handler = list catch ct @ catch_all in
288+
"try", block_type bt @ [Node ("do", list instr es)] @ handler
289+
| TryDelegate (bt, es, x) ->
290+
let delegate = [Node ("delegate " ^ var x, [])] in
291+
"try", block_type bt @ [Node ("do", list instr es)] @ delegate
292+
| Throw x -> "throw " ^ var x, []
293+
| Rethrow x -> "rethrow " ^ var x, []
282294
in Node (head, inner)
283295

284296
let const head c =

interpreter/text/lexer.mll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ rule token = parse
348348
| "i32.reinterpret_f32" { CONVERT i32_reinterpret_f32 }
349349
| "i64.reinterpret_f64" { CONVERT i64_reinterpret_f64 }
350350

351+
| "try" { TRY }
352+
| "do" { DO }
353+
| "catch" { CATCH }
354+
| "catch_all" { CATCH_ALL }
355+
| "delegate" { DELEGATE }
356+
| "throw" { THROW }
357+
| "rethrow" { RETHROW }
358+
351359
| "type" { TYPE }
352360
| "func" { FUNC }
353361
| "start" { START }

interpreter/text/parser.mly

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ let func_type (c : context) x =
112112
try (Lib.List32.nth c.types.list x.it).it
113113
with Failure _ -> error x.at ("unknown type " ^ Int32.to_string x.it)
114114

115+
let handlers (c : context) h =
116+
List.map (fun (l, i) -> (l c event, i c)) h
115117

116118
let anon category space n =
117119
let i = space.count in
@@ -179,7 +181,8 @@ let inline_type_explicit (c : context) x ft at =
179181
%token NAT INT FLOAT STRING VAR
180182
%token NUM_TYPE FUNCREF EXTERNREF EXTERN MUT
181183
%token UNREACHABLE NOP DROP SELECT
182-
%token BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE
184+
%token BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE TRY DO CATCH CATCH_ALL
185+
%token DELEGATE
183186
%token CALL CALL_INDIRECT RETURN
184187
%token LOCAL_GET LOCAL_SET LOCAL_TEE GLOBAL_GET GLOBAL_SET
185188
%token TABLE_GET TABLE_SET
@@ -188,6 +191,7 @@ let inline_type_explicit (c : context) x ft at =
188191
%token LOAD STORE OFFSET_EQ_NAT ALIGN_EQ_NAT
189192
%token CONST UNARY BINARY TEST COMPARE CONVERT
190193
%token REF_NULL REF_FUNC REF_EXTERN REF_IS_NULL
194+
%token THROW RETHROW
191195
%token FUNC START TYPE PARAM RESULT LOCAL GLOBAL
192196
%token TABLE ELEM MEMORY EVENT DATA DECLARE OFFSET ITEM IMPORT EXPORT
193197
%token MODULE BIN QUOTE
@@ -358,6 +362,8 @@ plain_instr :
358362
br_table xs x }
359363
| RETURN { fun c -> return }
360364
| CALL var { fun c -> call ($2 c func) }
365+
| THROW var { fun c -> throw ($2 c event) }
366+
| RETHROW var { fun c -> rethrow ($2 c label) }
361367
| LOCAL_GET var { fun c -> local_get ($2 c local) }
362368
| LOCAL_SET var { fun c -> local_set ($2 c local) }
363369
| LOCAL_TEE var { fun c -> local_tee ($2 c local) }
@@ -398,7 +404,6 @@ plain_instr :
398404
| BINARY { fun c -> $1 }
399405
| CONVERT { fun c -> $1 }
400406

401-
402407
select_instr :
403408
| SELECT select_instr_results
404409
{ let at = at () in fun c -> let b, ts = $2 in
@@ -495,6 +500,12 @@ block_instr :
495500
| IF labeling_opt block ELSE labeling_end_opt instr_list END labeling_end_opt
496501
{ fun c -> let c' = $2 c ($5 @ $8) in
497502
let ts, es1 = $3 c' in if_ ts es1 ($6 c') }
503+
| TRY labeling_opt block handler_instr
504+
{ fun c -> let c' = $2 c [] in
505+
let ts, es = $3 c' in $4 ts es c' }
506+
| TRY labeling_opt block DELEGATE var
507+
{ fun c -> let c' = $2 c [] in
508+
let ts, es = $3 c' in try_delegate ts es ($5 c label) }
498509

499510
block :
500511
| type_use block_param_body
@@ -524,6 +535,44 @@ block_result_body :
524535
{ let FuncType (ins, out) = fst $5 in
525536
FuncType (ins, $3 @ out), snd $5 }
526537

538+
handler_instr :
539+
| catch_list_instr END
540+
{ fun bt es c -> try_catch bt es (handlers c $1) None }
541+
| catch_list_instr catch_all END
542+
{ fun bt es c -> try_catch bt es (handlers c $1) (Some ($2 c)) }
543+
| catch_all END
544+
{ fun bt es c -> try_catch bt es [] (Some ($1 c)) }
545+
| END { fun bt es c -> try_catch bt es [] None }
546+
547+
catch_list_instr :
548+
| catch catch_list_instr { $1 :: $2 }
549+
| catch { [$1] }
550+
551+
handler :
552+
| catch_list
553+
{ fun bt es _ c' ->
554+
let cs = (List.map (fun (l, i) -> (l c' event, i c')) $1) in
555+
try_catch bt es cs None }
556+
| catch_list LPAR catch_all RPAR
557+
{ fun bt es _ c' ->
558+
let cs = (List.map (fun (l, i) -> (l c' event, i c')) $1) in
559+
try_catch bt es cs (Some ($3 c')) }
560+
| LPAR catch_all RPAR
561+
{ fun bt es _ c' -> try_catch bt es [] (Some ($2 c')) }
562+
| LPAR DELEGATE var RPAR
563+
{ fun bt es c _ -> try_delegate bt es ($3 c label) }
564+
| /* empty */ { fun bt es c _ -> try_catch bt es [] None }
565+
566+
catch_list :
567+
| catch_list LPAR catch RPAR { $1 @ [$3] }
568+
| LPAR catch RPAR { [$2] }
569+
570+
catch :
571+
| CATCH var instr_list { ($2, $3) }
572+
573+
catch_all :
574+
| CATCH_ALL instr_list { $2 }
575+
527576

528577
expr : /* Sugar */
529578
| LPAR expr1 RPAR
@@ -545,6 +594,8 @@ expr1 : /* Sugar */
545594
| IF labeling_opt if_block
546595
{ fun c -> let c' = $2 c [] in
547596
let bt, (es, es1, es2) = $3 c c' in es, if_ bt es1 es2 }
597+
| TRY labeling_opt try_block
598+
{ fun c -> let c' = $2 c [] in [], $3 c c' }
548599

549600
select_expr_results :
550601
| LPAR RESULT value_type_list RPAR select_expr_results
@@ -614,6 +665,38 @@ if_ :
614665
| LPAR THEN instr_list RPAR /* Sugar */
615666
{ fun c c' -> [], $3 c', [] }
616667

668+
try_block :
669+
| type_use try_block_param_body
670+
{ let at = at () in
671+
fun c c' ->
672+
let bt = VarBlockType (inline_type_explicit c' ($1 c' type_) (fst $2) at) in
673+
snd $2 bt c c' }
674+
| try_block_param_body /* Sugar */
675+
{ let at = at () in
676+
fun c c' ->
677+
let bt =
678+
match fst $1 with
679+
| FuncType ([], []) -> ValBlockType None
680+
| FuncType ([], [t]) -> ValBlockType (Some t)
681+
| ft -> VarBlockType (inline_type c' ft at)
682+
in snd $1 bt c c' }
683+
684+
try_block_param_body :
685+
| try_block_result_body { $1 }
686+
| LPAR PARAM value_type_list RPAR try_block_param_body
687+
{ let FuncType (ins, out) = fst $5 in
688+
FuncType ($3 @ ins, out), snd $5 }
689+
690+
try_block_result_body :
691+
| try_ { FuncType ([], []), $1 }
692+
| LPAR RESULT value_type_list RPAR try_block_result_body
693+
{ let FuncType (ins, out) = fst $5 in
694+
FuncType (ins, $3 @ out), snd $5 }
695+
696+
try_ :
697+
| LPAR DO instr_list RPAR handler
698+
{ fun bt c c' -> $5 bt ($3 c') c c' }
699+
617700
instr_list :
618701
| /* empty */ { fun c -> [] }
619702
| select_instr { fun c -> [$1 c] }

interpreter/valid/valid.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,11 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type =
402402
let t1, t2 = type_cvtop e.at cvtop in
403403
[NumType t1] --> [NumType t2]
404404

405+
| TryCatch _ -> [] --> [] (* TODO *)
406+
| TryDelegate _ -> [] --> [] (* TODO *)
407+
| Throw _ -> [] --> [] (* TODO *)
408+
| Rethrow _ -> [] --> [] (* TODO *)
409+
405410
and check_seq (c : context) (s : infer_stack_type) (es : instr list)
406411
: infer_stack_type =
407412
match es with

test/core/exports.wast

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
(module (func) (memory 0) (export "a" (func 0)) (export "a" (memory 0)))
5858
"duplicate export name"
5959
)
60+
(assert_invalid
61+
(module (event $e0 (export "e0")) (event $e1 (export "e0")))
62+
"duplicate export name"
63+
)
6064

6165

6266
;; Globals

0 commit comments

Comments
 (0)