123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280openWax_langopenAst(* Recover a [dispatch] from the conventional dense-switch shape that compilers
emit (and that [Ast_utils.lower_dispatch] reproduces): a stack of void blocks,
one per case, with a [br_table] in the innermost and each case body just after
its block. The outermost block's case body is the code *following* that block,
so recovery folds the block together with the statements after it:
'c_0: { 'c_1: { … 'c_k: { br_table […] idx } b_k } … b_1 } b_0 (b_0 = following stmts)
⇒ dispatch idx [ … ] { 'c_k: { b_k } … 'c_1: { b_1 } 'c_0: { b_0 } }
Arms come out in fall-through order — innermost case first — which is the
reverse of the block nesting, so a case's body falls through into the *next*
arm listed, not the previous one. So decompiled WAT/WASM jump tables (and
round-tripped Wax dispatches) read as the high-level form, every case an arm,
rather than a pile of blocks.
Folding is the exact inverse of the lowering — re-lowering reproduces the
original blocks byte-for-byte — so it always preserves runtime semantics; the
matcher just confirms the shape.
Cost. [descend] follows only the leading-child chain and stops at the first
element that breaks it; the chain nodes are the case blocks, none of which
heads another chain, so each node is walked by at most one descent and the
pass is linear in the AST. *)letis_void(t:functype)=t.params=[||]&&t.results=[||](* Peel the case-block chain inside the outermost block, collecting the inner
cases (with their bodies), plus the [br_table]'s full label list and index. *)letrecdescendblock=matchblockwith|[{desc=Br_table(br_labels,index);_}]->Some([],br_labels,index)|{desc=Block{label=Somec;typ;block={desc=inner;_}};_}::bodywhenis_voidtyp->(matchdescendinnerwith|Some(arms,br_labels,index)->Some((c,body)::arms,br_labels,index)|None->None)|_->None(* Recurse into children structurally (no folding here — folding needs the
statement list, see [rewrite_list]). *)letrecrewrite_instr(i:locationinstr):locationinstr={iwithdesc=rewrite_desci.desc}(* Fold a statement list, recovering a [dispatch] from a switch-wrapper block
together with the statements that follow it (the outermost case's body). *)andrewrite_list=function|[]->[]|i::rest->(matchtry_foldirestwith|Somedispatch->[dispatch]|None->rewrite_instri::rewrite_listrest)andtry_fold(i:locationinstr)(trailing:locationinstrlist):locationinstroption=matchi.descwith|Block{label=Somec0;typ;block}whenis_voidtyp->(matchdescendblock.descwith|Some(inner_arms,br_labels,index)->(matchList.revbr_labelswith|default::rev_cases->letcases=List.revrev_casesin(* Arms in fall-through order: innermost case first, the outermost
block [c0] (whose body is the trailing code) last. *)letarms=List.rev((c0,trailing)::inner_arms)inletarm_names=List.map(fun((l:label),_)->l.desc)armsinletbr_names=List.map(fun(l:label)->l.desc)br_labelsin(* Case labels become distinct, name-keyed arms; and the outermost
block must itself be a [br_table] target (it is, in a real
switch — index 0 or the default lands there), which keeps us from
folding an unrelated enclosing block and swallowing the code
after it. *)letdistinct=List.length(List.sort_uniqcomparearm_names)=List.lengtharm_namesinifdistinct&&List.memc0.descbr_namesthenSome{iwithdesc=Dispatch{index=rewrite_instrindex;cases;default;arms=List.map(fun(l,b)->(l,no_loc(rewrite_listb)))arms;};}elseNone|[]->None)|None->None)|_->Noneandrewrite_desc(desc:locationinstr_desc):locationinstr_desc=matchdescwith|Block{label;typ;block}->Block{label;typ;block={blockwithdesc=rewrite_listblock.desc}}|Loop{label;typ;block}->Loop{label;typ;block={blockwithdesc=rewrite_listblock.desc}}|While{label;cond;step;block}->While{label;cond=rewrite_instrcond;step=Option.maprewrite_instrstep;block={blockwithdesc=rewrite_listblock.desc};}|If{label;typ;cond;if_block;else_block}->If{label;typ;cond=rewrite_instrcond;if_block={if_blockwithdesc=rewrite_listif_block.desc};else_block=Option.map(funb->{bwithdesc=rewrite_listb.desc})else_block;}|TryTable{label;typ;catches;block}->TryTable{label;typ;catches;block={blockwithdesc=rewrite_listblock.desc};}|TryCatch{label;typ;block;arms}->TryCatch{label;typ;block={blockwithdesc=rewrite_listblock.desc};arms=List.map(funa->{awitharm_body={a.arm_bodywithdesc=rewrite_lista.arm_body.desc};})arms;}|Try{label;typ;block;catches;catch_all}->Try{label;typ;block={blockwithdesc=rewrite_listblock.desc};catches=List.map(fun(t,l)->(t,{lwithdesc=rewrite_listl.desc}))catches;catch_all=Option.map(funb->{bwithdesc=rewrite_listb.desc})catch_all;}|Dispatch{index;cases;default;arms}->Dispatch{index=rewrite_instrindex;cases;default;arms=List.map(fun(l,b)->(l,{bwithdesc=rewrite_listb.desc}))arms;}|Match{scrutinee;arms;default}->Match{scrutinee=rewrite_instrscrutinee;arms=List.map(fun(p,b)->(p,{bwithdesc=rewrite_listb.desc}))arms;default={defaultwithdesc=rewrite_listdefault.desc};}|If_annotation{cond;then_body;else_body}->If_annotation{cond;then_body={then_bodywithdesc=rewrite_listthen_body.desc};else_body=Option.map(funb->{bwithdesc=rewrite_listb.desc})else_body;}|Set(x,op,e)->Set(x,op,rewrite_instre)|Tee(x,e)->Tee(x,rewrite_instre)|Labelled(l,e)->Labelled(l,rewrite_instre)|Call(t,args)->Call(rewrite_instrt,List.maprewrite_instrargs)|TailCall(t,args)->TailCall(rewrite_instrt,List.maprewrite_instrargs)|Cast(e,t)->Cast(rewrite_instre,t)|CastDesc(e,t,d)->CastDesc(rewrite_instre,t,rewrite_instrd)|Test(e,t)->Test(rewrite_instre,t)|NonNulle->NonNull(rewrite_instre)|Struct(idx,fs)->Struct(idx,List.map(fun(n,e)->(n,Option.maprewrite_instre))fs)|StructDesc(d,fs)->StructDesc(rewrite_instrd,List.map(fun(n,e)->(n,Option.maprewrite_instre))fs)|StructDefaultDescd->StructDefaultDesc(rewrite_instrd)|StructGet(e,x)->StructGet(rewrite_instre,x)|GetDescriptore->GetDescriptor(rewrite_instre)|StructSet(e,x,v)->StructSet(rewrite_instre,x,rewrite_instrv)|Array(idx,a,b)->Array(idx,rewrite_instra,rewrite_instrb)|ArrayDefault(idx,e)->ArrayDefault(idx,rewrite_instre)|ArrayFixed(idx,l)->ArrayFixed(idx,List.maprewrite_instrl)|ArraySegment(idx,d,a,b)->ArraySegment(idx,d,rewrite_instra,rewrite_instrb)|ArrayGet(a,b)->ArrayGet(rewrite_instra,rewrite_instrb)|ArraySet(a,b,c)->ArraySet(rewrite_instra,rewrite_instrb,rewrite_instrc)|BinOp(op,a,b)->BinOp(op,rewrite_instra,rewrite_instrb)|UnOp(op,e)->UnOp(op,rewrite_instre)|Let(bs,e)->Let(bs,Option.maprewrite_instre)|Br(l,e)->Br(l,Option.maprewrite_instre)|Br_if(l,e)->Br_if(l,rewrite_instre)|Hinted(h,e)->Hinted(h,rewrite_instre)|On(e,h)->On(rewrite_instre,h)|Br_table(ls,e)->Br_table(ls,rewrite_instre)|Br_on_null(l,e)->Br_on_null(l,rewrite_instre)|Br_on_non_null(l,e)->Br_on_non_null(l,rewrite_instre)|Br_on_cast(l,t,e)->Br_on_cast(l,t,rewrite_instre)|Br_on_cast_fail(l,t,e)->Br_on_cast_fail(l,t,rewrite_instre)|Br_on_cast_desc_eq(l,t,e,d)->Br_on_cast_desc_eq(l,t,rewrite_instre,rewrite_instrd)|Br_on_cast_desc_eq_fail(l,t,e,d)->Br_on_cast_desc_eq_fail(l,t,rewrite_instre,rewrite_instrd)|Throw(idx,e)->Throw(idx,List.maprewrite_instre)|ThrowRefe->ThrowRef(rewrite_instre)|ContNew(ct,e)->ContNew(ct,rewrite_instre)|ContBind(src,dst,l)->ContBind(src,dst,List.maprewrite_instrl)|Suspend(tag,l)->Suspend(tag,List.maprewrite_instrl)|Resume(ct,h,l)->Resume(ct,h,List.maprewrite_instrl)|ResumeThrow(ct,tag,h,l)->ResumeThrow(ct,tag,h,List.maprewrite_instrl)|ResumeThrowRef(ct,h,l)->ResumeThrowRef(ct,h,List.maprewrite_instrl)|Switch(ct,tag,l)->Switch(ct,tag,List.maprewrite_instrl)|Returne->Return(Option.maprewrite_instre)|Sequencel->Sequence(List.maprewrite_instrl)|Select(a,b,c)->Select(rewrite_instra,rewrite_instrb,rewrite_instrc)|(Unreachable|Nop|Hole|Null|Get_|Path_|Char_|String_|Int_|Float_|StructDefault_)asx->xletrecfield_desc(f:locationmodulefield)=letmap_fields=List.map(funa->{awithdesc=field_desca.desc})inmatchfwith|Func({body=label,instrs;_}asr)->Func{rwithbody=(label,rewrite_listinstrs)}|Conditional({then_fields;else_fields;_}asr)->Conditional{rwiththen_fields={then_fieldswithdesc=map_fieldsthen_fields.desc};else_fields=Option.map(funb->{bwithdesc=map_fieldsb.desc})else_fields;}|(Type_|Module_annotation_|Import_|Import_group_|Global_|Tag_|Memory_|Data_|Table_|Elem_)asf->fletmodule_(m:locationmodule_):locationmodule_=List.map(funa->{awithdesc=field_desca.desc})m