From e848b7d5cb7aa89bbf5689c595d9d3d862260468 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Wed, 25 Mar 2026 12:02:58 -0500 Subject: [PATCH 1/3] Implement compact imports in the spec interpreter Adds binary and text parsing support for the compact import section proposal. Also fixes a small section-sizing bug in the binary tests. --- interpreter/binary/decode.ml | 31 +++++++++++-- interpreter/text/parser.mly | 85 +++++++++++++++++++++++++----------- 2 files changed, 87 insertions(+), 29 deletions(-) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index f817ff260..c2c31651e 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -1028,14 +1028,37 @@ let type_section s = (* Import section *) -let import s = +let imports s = + let left = pos s in let module_name = name s in let item_name = name s in - let xt = externtype s in - Import (module_name, item_name, xt) + if item_name = [] then + match peek s with + | Some 0x7f -> + skip 1 s; + vec (fun s -> + let l = pos s in + let nm = name s in + let xt = externtype s in + Import (module_name, nm, xt) @@ region s l (pos s) + ) s + | Some 0x7e -> + skip 1 s; + let xt = externtype s in + vec (fun s -> + let l = pos s in + let nm = name s in + Import (module_name, nm, xt) @@ region s l (pos s) + ) s + | _ -> + let xt = externtype s in + [Import (module_name, item_name, xt) @@ region s left (pos s)] + else + let xt = externtype s in + [Import (module_name, item_name, xt) @@ region s left (pos s)] let import_section s = - section Custom.Import (vec (at import)) [] s + section Custom.Import (fun s -> List.concat (vec imports s)) [] s (* Function section *) diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 1bcc33393..99c0e6866 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -1227,32 +1227,67 @@ table_fields : /* Imports & Exports */ externtype : - | LPAR FUNC bindidx_opt typeuse RPAR - { fun c -> ignore ($3 c anon_func bind_func); - fun () -> ExternFuncT (Idx ($4 c).it) } - | LPAR TAG bindidx_opt typeuse RPAR - { fun c -> ignore ($3 c anon_tag bind_tag); - fun () -> ExternTagT (TagT (Idx ($4 c).it)) } - | LPAR TAG bindidx_opt functype RPAR /* Sugar */ - { fun c -> ignore ($3 c anon_tag bind_tag); - fun () -> ExternTagT (TagT (Idx (inline_functype c ($4 c) $loc($4)).it)) } - | LPAR GLOBAL bindidx_opt globaltype RPAR - { fun c -> ignore ($3 c anon_global bind_global); - fun () -> ExternGlobalT ($4 c) } - | LPAR MEMORY bindidx_opt memorytype RPAR - { fun c -> ignore ($3 c anon_memory bind_memory); - fun () -> ExternMemoryT ($4 c) } - | LPAR TABLE bindidx_opt tabletype RPAR - { fun c -> ignore ($3 c anon_table bind_table); - fun () -> ExternTableT ($4 c) } - | LPAR FUNC bindidx_opt functype RPAR /* Sugar */ - { fun c -> ignore ($3 c anon_func bind_func); - fun () -> ExternFuncT (Idx (inline_functype c ($4 c) $loc($4)).it) } + | LPAR FUNC option(bindidx) typeuse RPAR + { fun c -> ($3, anon_func, bind_func, + fun () -> ExternFuncT (Idx ($4 c).it)) } + | LPAR TAG option(bindidx) typeuse RPAR + { fun c -> ($3, anon_tag, bind_tag, + fun () -> ExternTagT (TagT (Idx ($4 c).it))) } + | LPAR TAG option(bindidx) functype RPAR /* Sugar */ + { fun c -> ($3, anon_tag, bind_tag, + fun () -> ExternTagT (TagT (Idx (inline_functype c ($4 c) $loc($4)).it))) } + | LPAR GLOBAL option(bindidx) globaltype RPAR + { fun c -> ($3, anon_global, bind_global, + fun () -> ExternGlobalT ($4 c)) } + | LPAR MEMORY option(bindidx) memorytype RPAR + { fun c -> ($3, anon_memory, bind_memory, + fun () -> ExternMemoryT ($4 c)) } + | LPAR TABLE option(bindidx) tabletype RPAR + { fun c -> ($3, anon_table, bind_table, + fun () -> ExternTableT ($4 c)) } + | LPAR FUNC option(bindidx) functype RPAR /* Sugar */ + { fun c -> ($3, anon_func, bind_func, + fun () -> ExternFuncT (Idx (inline_functype c ($4 c) $loc($4)).it)) } + +compact_item1 : + | LPAR ITEM name externtype RPAR + { fun c -> let (id, anon, bind, df) = $4 c in + ignore (match id with None -> anon c $loc($4) | Some x -> bind c x); + fun () -> ($3, df ()) } + +compact_item1_list : + | compact_item1 + { fun c -> let f = $1 c in + fun () -> [f ()] } + | compact_item1 compact_item1_list + { fun c -> let f = $1 c in let fs = $2 c in + fun () -> f () :: fs () } + +compact_item2_list : + | LPAR ITEM name RPAR compact_item2_list + { let (items, xt_fn) = $5 in ($3 :: items, xt_fn) } + | externtype + { ([], $1) } import : | LPAR IMPORT name name externtype RPAR - { fun c -> let df = $5 c in - fun () -> Import ($3, $4, df ()) @@ $sloc } + { fun c -> let (id, anon, bind, df) = $5 c in + ignore (match id with None -> anon c $loc($5) | Some x -> bind c x); + fun () -> [Import ($3, $4, df ()) @@ $sloc] } + | LPAR IMPORT name compact_item1_list RPAR + { fun c -> let items = $4 c in + fun () -> + List.map (fun (item_name, xt) -> Import ($3, item_name, xt) @@ $sloc) + (items ()) } + | LPAR IMPORT name compact_item2_list RPAR + { fun c -> + let (items, xt_fn) = $4 in + let (id, anon, _bind, df) = xt_fn c in + (match id with Some x -> error x.at "identifier not allowed" | None -> ()); + List.iter (fun _ -> ignore (anon c $sloc)) items; + fun () -> + let xt = df () in + List.map (fun item_name -> Import ($3, item_name, xt) @@ $sloc) items } inline_import : | LPAR IMPORT name name RPAR { $3, $4 } @@ -1377,8 +1412,8 @@ module_fields1 : | import module_fields { fun c -> let imf = $1 c in let mff = $2 c in fun () -> let mf = mff () in - fun () -> let im = imf () in let m = mf () in - {m with imports = im :: m.imports} } + fun () -> let ims = imf () in let m = mf () in + {m with imports = ims @ m.imports} } | export module_fields { fun c -> let mff = $2 c in fun () -> let mf = mff () in From 29ed85e6694874dbb3afd57d26dbfc7afeb13375 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Wed, 29 Jul 2026 14:43:11 -0500 Subject: [PATCH 2/3] Updates per Rossberg's feedback --- interpreter/binary/decode.ml | 38 ++++++++++----------- interpreter/text/parser.mly | 64 ++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index c2c31651e..5f8023210 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -1032,28 +1032,24 @@ let imports s = let left = pos s in let module_name = name s in let item_name = name s in - if item_name = [] then - match peek s with - | Some 0x7f -> - skip 1 s; - vec (fun s -> - let l = pos s in - let nm = name s in - let xt = externtype s in - Import (module_name, nm, xt) @@ region s l (pos s) - ) s - | Some 0x7e -> - skip 1 s; - let xt = externtype s in - vec (fun s -> - let l = pos s in - let nm = name s in - Import (module_name, nm, xt) @@ region s l (pos s) - ) s - | _ -> + match peek s with + | Some 0x7f when item_name = [] -> + skip 1 s; + vec (fun s -> + let l = pos s in + let nm = name s in let xt = externtype s in - [Import (module_name, item_name, xt) @@ region s left (pos s)] - else + Import (module_name, nm, xt) @@ region s l (pos s) + ) s + | Some 0x7e when item_name = [] -> + skip 1 s; + let xt = externtype s in + vec (fun s -> + let l = pos s in + let nm = name s in + Import (module_name, nm, xt) @@ region s l (pos s) + ) s + | _ -> let xt = externtype s in [Import (module_name, item_name, xt) @@ region s left (pos s)] diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 99c0e6866..1204b8fe9 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -202,6 +202,9 @@ let bind_label (c : context) x = bind_rel "label" c.labels x let bind_field (c : context) x y = bind_abs "field" (Lib.List32.nth c.types.fields x) y +let bind_if b f = if b then f else + fun _c x -> error x.at "identifier not allowed" + let define_type (c : context) (ty : type_) = c.types.list <- c.types.list @ [ty] @@ -1227,32 +1230,31 @@ table_fields : /* Imports & Exports */ externtype : - | LPAR FUNC option(bindidx) typeuse RPAR - { fun c -> ($3, anon_func, bind_func, - fun () -> ExternFuncT (Idx ($4 c).it)) } - | LPAR TAG option(bindidx) typeuse RPAR - { fun c -> ($3, anon_tag, bind_tag, - fun () -> ExternTagT (TagT (Idx ($4 c).it))) } - | LPAR TAG option(bindidx) functype RPAR /* Sugar */ - { fun c -> ($3, anon_tag, bind_tag, - fun () -> ExternTagT (TagT (Idx (inline_functype c ($4 c) $loc($4)).it))) } - | LPAR GLOBAL option(bindidx) globaltype RPAR - { fun c -> ($3, anon_global, bind_global, - fun () -> ExternGlobalT ($4 c)) } - | LPAR MEMORY option(bindidx) memorytype RPAR - { fun c -> ($3, anon_memory, bind_memory, - fun () -> ExternMemoryT ($4 c)) } - | LPAR TABLE option(bindidx) tabletype RPAR - { fun c -> ($3, anon_table, bind_table, - fun () -> ExternTableT ($4 c)) } - | LPAR FUNC option(bindidx) functype RPAR /* Sugar */ - { fun c -> ($3, anon_func, bind_func, - fun () -> ExternFuncT (Idx (inline_functype c ($4 c) $loc($4)).it)) } + | LPAR FUNC bindidx_opt typeuse RPAR + { fun c b -> ignore ($3 c anon_func (bind_if b bind_func)); + fun () -> ExternFuncT (Idx ($4 c).it) } + | LPAR TAG bindidx_opt typeuse RPAR + { fun c b -> ignore ($3 c anon_tag (bind_if b bind_tag)); + fun () -> ExternTagT (TagT (Idx ($4 c).it)) } + | LPAR TAG bindidx_opt functype RPAR /* Sugar */ + { fun c b -> ignore ($3 c anon_tag (bind_if b bind_tag)); + fun () -> ExternTagT (TagT (Idx (inline_functype c ($4 c) $loc($4)).it)) } + | LPAR GLOBAL bindidx_opt globaltype RPAR + { fun c b -> ignore ($3 c anon_global (bind_if b bind_global)); + fun () -> ExternGlobalT ($4 c) } + | LPAR MEMORY bindidx_opt memorytype RPAR + { fun c b -> ignore ($3 c anon_memory (bind_if b bind_memory)); + fun () -> ExternMemoryT ($4 c) } + | LPAR TABLE bindidx_opt tabletype RPAR + { fun c b -> ignore ($3 c anon_table (bind_if b bind_table)); + fun () -> ExternTableT ($4 c) } + | LPAR FUNC bindidx_opt functype RPAR /* Sugar */ + { fun c b -> ignore ($3 c anon_func (bind_if b bind_func)); + fun () -> ExternFuncT (Idx (inline_functype c ($4 c) $loc($4)).it) } compact_item1 : | LPAR ITEM name externtype RPAR - { fun c -> let (id, anon, bind, df) = $4 c in - ignore (match id with None -> anon c $loc($4) | Some x -> bind c x); + { fun c -> let df = $4 c true in fun () -> ($3, df ()) } compact_item1_list : @@ -1265,14 +1267,13 @@ compact_item1_list : compact_item2_list : | LPAR ITEM name RPAR compact_item2_list - { let (items, xt_fn) = $5 in ($3 :: items, xt_fn) } + { let (item_names, xt_fn) = $5 in ($3 :: item_names, xt_fn) } | externtype { ([], $1) } import : | LPAR IMPORT name name externtype RPAR - { fun c -> let (id, anon, bind, df) = $5 c in - ignore (match id with None -> anon c $loc($5) | Some x -> bind c x); + { fun c -> let df = $5 c true in fun () -> [Import ($3, $4, df ()) @@ $sloc] } | LPAR IMPORT name compact_item1_list RPAR { fun c -> let items = $4 c in @@ -1281,13 +1282,12 @@ import : (items ()) } | LPAR IMPORT name compact_item2_list RPAR { fun c -> - let (items, xt_fn) = $4 in - let (id, anon, _bind, df) = xt_fn c in - (match id with Some x -> error x.at "identifier not allowed" | None -> ()); - List.iter (fun _ -> ignore (anon c $sloc)) items; + let (item_names, xt_fn) = $4 in + (* Apply the externtype once per item, to allocate one index each *) + let dfs = List.map (fun _ -> xt_fn c false) item_names in fun () -> - let xt = df () in - List.map (fun item_name -> Import ($3, item_name, xt) @@ $sloc) items } + List.map2 (fun item_name df -> Import ($3, item_name, df ()) @@ $sloc) + item_names dfs } inline_import : | LPAR IMPORT name name RPAR { $3, $4 } From 809b0db82f540bbd7ff6771af500b8ef2ed81a59 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Wed, 29 Jul 2026 14:43:46 -0500 Subject: [PATCH 3/3] Remove stray comment --- interpreter/text/parser.mly | 1 - 1 file changed, 1 deletion(-) diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 1204b8fe9..6ea942796 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -1283,7 +1283,6 @@ import : | LPAR IMPORT name compact_item2_list RPAR { fun c -> let (item_names, xt_fn) = $4 in - (* Apply the externtype once per item, to allocate one index each *) let dfs = List.map (fun _ -> xt_fn c false) item_names in fun () -> List.map2 (fun item_name df -> Import ($3, item_name, df ()) @@ $sloc)