@@ -57,6 +57,8 @@ pub struct Config {
5757 name_unnamed : bool ,
5858 fold_instructions : bool ,
5959 indent_text : String ,
60+ #[ cfg( feature = "validate" ) ]
61+ print_operand_stack : bool ,
6062}
6163
6264impl Default for Config {
@@ -67,6 +69,8 @@ impl Default for Config {
6769 name_unnamed : false ,
6870 fold_instructions : false ,
6971 indent_text : " " . to_string ( ) ,
72+ #[ cfg( feature = "validate" ) ]
73+ print_operand_stack : false ,
7074 }
7175 }
7276}
@@ -246,6 +250,29 @@ impl Config {
246250 self
247251 }
248252
253+ /// Print the operand stack types within function bodies,
254+ /// flagging newly pushed operands. E.g.:
255+ ///
256+ /// ```wasm
257+ /// (module
258+ /// (type (;0;) (func))
259+ /// (func (;0;) (type 0)
260+ /// i32.const 4
261+ /// ;; |i32
262+ /// i32.const 4
263+ /// ;; i32|i32
264+ /// i32.add
265+ /// ;; |i32
266+ /// drop
267+ /// ;; []
268+ /// )
269+ /// )
270+ #[ cfg( feature = "validate" ) ]
271+ pub fn print_operand_stack ( & mut self , enable : bool ) -> & mut Self {
272+ self . print_operand_stack = enable;
273+ self
274+ }
275+
249276 /// Select the string to use when indenting.
250277 ///
251278 /// The indent allowed here are arbitrary and unchecked. You should enter
@@ -415,6 +442,14 @@ impl Printer<'_, '_> {
415442 #[ cfg( feature = "component-model" ) ]
416443 let mut parsers = Vec :: new ( ) ;
417444
445+ #[ cfg( feature = "validate" ) ]
446+ let mut validator = Validator :: new ( ) ;
447+ #[ cfg( feature = "validate" ) ]
448+ let mut func_validators = Vec :: new ( ) ;
449+ #[ cfg( feature = "component-model" ) ]
450+ #[ cfg( feature = "validate" ) ]
451+ let mut validators: Vec < Validator > = Vec :: new ( ) ;
452+
418453 loop {
419454 let payload = match parser. parse ( bytes, true ) ? {
420455 Chunk :: NeedMoreData ( _) => unreachable ! ( ) ,
@@ -423,6 +458,19 @@ impl Printer<'_, '_> {
423458 payload
424459 }
425460 } ;
461+ #[ cfg( feature = "validate" ) ]
462+ if self . config . print_operand_stack {
463+ match validator. payload ( & payload) {
464+ Ok ( ValidPayload :: Func ( f, _) ) => {
465+ func_validators. push ( f. into_validator ( Default :: default ( ) ) ) ;
466+ }
467+ Ok ( _) => ( ) ,
468+ Err ( e) => {
469+ self . newline_unknown_pos ( ) ?;
470+ write ! ( self . result, ";; module or component is invalid: {e}" ) ?;
471+ }
472+ } ;
473+ }
426474 match payload {
427475 Payload :: Version { encoding, .. } => {
428476 if let Some ( e) = expected {
@@ -605,7 +653,12 @@ impl Printer<'_, '_> {
605653 Self :: ensure_module ( & states) ?;
606654 }
607655 Payload :: CodeSectionEntry ( body) => {
608- self . print_code_section_entry ( states. last_mut ( ) . unwrap ( ) , & body) ?;
656+ self . print_code_section_entry (
657+ states. last_mut ( ) . unwrap ( ) ,
658+ & body,
659+ #[ cfg( feature = "validate" ) ]
660+ func_validators. pop ( ) ,
661+ ) ?;
609662 self . update_custom_section_place ( & mut states, "after code" ) ;
610663 }
611664 Payload :: DataCountSection { .. } => {
@@ -628,6 +681,12 @@ impl Printer<'_, '_> {
628681 parsers. push ( parser) ;
629682 parser = inner;
630683 self . newline ( range. start ) ?;
684+
685+ #[ cfg( feature = "validate" ) ]
686+ if self . config . print_operand_stack {
687+ validators. push ( validator) ;
688+ validator = Validator :: new ( ) ;
689+ }
631690 }
632691 #[ cfg( feature = "component-model" ) ]
633692 Payload :: InstanceSection ( s) => {
@@ -646,6 +705,12 @@ impl Printer<'_, '_> {
646705 parsers. push ( parser) ;
647706 parser = inner;
648707 self . newline ( range. start ) ?;
708+
709+ #[ cfg( feature = "validate" ) ]
710+ if self . config . print_operand_stack {
711+ validators. push ( validator) ;
712+ validator = Validator :: new ( ) ;
713+ }
649714 }
650715 #[ cfg( feature = "component-model" ) ]
651716 Payload :: ComponentInstanceSection ( s) => {
@@ -699,6 +764,12 @@ impl Printer<'_, '_> {
699764 }
700765 }
701766 parser = parsers. pop ( ) . unwrap ( ) ;
767+
768+ #[ cfg( feature = "validate" ) ]
769+ if self . config . print_operand_stack {
770+ validator = validators. pop ( ) . unwrap ( ) ;
771+ }
772+
702773 continue ;
703774 }
704775 }
@@ -1328,6 +1399,7 @@ impl Printer<'_, '_> {
13281399 & mut self ,
13291400 state : & mut State ,
13301401 body : & FunctionBody < ' _ > ,
1402+ #[ cfg( feature = "validate" ) ] validator : Option < FuncValidator < ValidatorResources > > ,
13311403 ) -> Result < ( ) > {
13321404 self . newline ( body. get_binary_reader ( ) . original_position ( ) ) ?;
13331405 self . start_group ( "func " ) ?;
@@ -1355,7 +1427,7 @@ impl Printer<'_, '_> {
13551427 if self . config . print_skeleton {
13561428 self . result . write_str ( " ..." ) ?;
13571429 } else {
1358- self . print_func_body ( state, func_idx, params, & body, & hints) ?;
1430+ self . print_func_body ( state, func_idx, params, & body, & hints, validator ) ?;
13591431 }
13601432
13611433 self . end_group ( ) ?;
@@ -1370,6 +1442,7 @@ impl Printer<'_, '_> {
13701442 params : u32 ,
13711443 body : & FunctionBody < ' _ > ,
13721444 branch_hints : & [ ( usize , BranchHint ) ] ,
1445+ #[ cfg( feature = "validate" ) ] mut validator : Option < FuncValidator < ValidatorResources > > ,
13731446 ) -> Result < ( ) > {
13741447 let mut first = true ;
13751448 let mut local_idx = 0 ;
@@ -1400,6 +1473,15 @@ impl Printer<'_, '_> {
14001473 }
14011474 locals. finish ( self ) ?;
14021475
1476+ #[ cfg( feature = "validate" ) ]
1477+ if let Some ( ref mut f) = validator {
1478+ if let Err ( e) = f. read_locals ( & mut body. get_binary_reader ( ) ) {
1479+ validator = None ;
1480+ self . newline_unknown_pos ( ) ?;
1481+ write ! ( self . result, ";; locals are invalid: {e}" ) ?;
1482+ }
1483+ }
1484+
14031485 let nesting_start = self . nesting ;
14041486 let fold_instructions = self . config . fold_instructions ;
14051487 let mut operator_state = OperatorState :: new ( self , OperatorSeparator :: Newline ) ;
@@ -1408,11 +1490,24 @@ impl Printer<'_, '_> {
14081490 let mut folded_printer = PrintOperatorFolded :: new ( self , state, & mut operator_state) ;
14091491 folded_printer. set_offset ( func_start) ;
14101492 folded_printer. begin_function ( func_idx) ?;
1411- Self :: print_operators ( & mut reader, branch_hints, func_start, & mut folded_printer) ?;
1412- folded_printer. finalize ( ) ?;
1493+ Self :: print_operators (
1494+ & mut reader,
1495+ branch_hints,
1496+ func_start,
1497+ & mut folded_printer,
1498+ #[ cfg( feature = "validate" ) ]
1499+ validator,
1500+ ) ?;
14131501 } else {
14141502 let mut flat_printer = PrintOperator :: new ( self , state, & mut operator_state) ;
1415- Self :: print_operators ( & mut reader, branch_hints, func_start, & mut flat_printer) ?;
1503+ Self :: print_operators (
1504+ & mut reader,
1505+ branch_hints,
1506+ func_start,
1507+ & mut flat_printer,
1508+ #[ cfg( feature = "validate" ) ]
1509+ validator,
1510+ ) ?;
14161511 }
14171512
14181513 // If this was an invalid function body then the nesting may not
@@ -1433,12 +1528,31 @@ impl Printer<'_, '_> {
14331528 mut branch_hints : & [ ( usize , BranchHint ) ] ,
14341529 func_start : usize ,
14351530 op_printer : & mut O ,
1531+ #[ cfg( feature = "validate" ) ] mut validator : Option < FuncValidator < ValidatorResources > > ,
14361532 ) -> Result < ( ) > {
14371533 let mut ops = OperatorsReader :: new ( body. clone ( ) ) ;
14381534 while !ops. eof ( ) {
14391535 if ops. is_end_then_eof ( ) {
1536+ let mut annotation: Option < String > = None ;
1537+ #[ cfg( feature = "validate" ) ]
1538+ if let Some ( ref mut f) = validator {
1539+ let res = ops
1540+ . get_binary_reader ( )
1541+ . visit_operator ( & mut f. visitor ( ops. original_position ( ) ) ) ;
1542+ match res {
1543+ Ok ( Ok ( _) ) => ( ) ,
1544+ _ => {
1545+ annotation = Some (
1546+ String :: from ( "type mismatch at end of expression:" )
1547+ + & Self :: visualize_operand_stack ( & f, 0 ) ?,
1548+ )
1549+ }
1550+ }
1551+ }
1552+
14401553 ops. read ( ) ?; // final "end" opcode terminates instruction sequence
14411554 ops. finish ( ) ?;
1555+ op_printer. finalize ( annotation. as_deref ( ) ) ?;
14421556 return Ok ( ( ) ) ;
14431557 }
14441558
@@ -1450,14 +1564,87 @@ impl Printer<'_, '_> {
14501564 op_printer. branch_hint ( * hint_offset, hint. taken ) ?;
14511565 }
14521566 }
1453-
1567+ let mut annotation: Option < String > = None ;
1568+ #[ cfg( feature = "validate" ) ]
1569+ if let Some ( ref mut f) = validator {
1570+ let res = ops
1571+ . get_binary_reader ( )
1572+ . visit_operator ( & mut f. visitor ( ops. original_position ( ) ) ) ;
1573+ if let Ok ( Ok ( _) ) = res {
1574+ if let Ok ( op) = ops. clone ( ) . read ( ) {
1575+ let arity = op. operator_arity ( & f. visitor ( ops. original_position ( ) ) ) ;
1576+ if let Some ( ( _pop_count, push_count) ) = arity {
1577+ annotation = Some ( Self :: visualize_operand_stack ( & f, push_count) ?) ;
1578+ }
1579+ }
1580+ } else {
1581+ validator = None ;
1582+ annotation = Some ( String :: from ( "(invalid)" ) ) ;
1583+ }
1584+ }
14541585 op_printer. set_offset ( ops. original_position ( ) ) ;
1455- op_printer. visit_operator ( & mut ops) ?;
1586+ op_printer. visit_operator ( & mut ops, annotation . as_deref ( ) ) ?;
14561587 }
14571588 ops. finish ( ) ?; // for the error message
14581589 bail ! ( "unexpected end of operators" ) ;
14591590 }
14601591
1592+ fn visualize_operand_stack (
1593+ func : & FuncValidator < ValidatorResources > ,
1594+ push_count : u32 ,
1595+ ) -> Result < String > {
1596+ let mut ret = PrintFmtWrite ( String :: new ( ) ) ;
1597+
1598+ let stack_printer = Printer {
1599+ result : & mut ret,
1600+ config : & Config :: new ( ) ,
1601+ nesting : 0 ,
1602+ line : 0 ,
1603+ group_lines : Vec :: new ( ) ,
1604+ code_section_hints : Vec :: new ( ) ,
1605+ } ;
1606+
1607+ if let Some ( & Frame { height, .. } ) = func. get_control_frame ( 0 ) {
1608+ if height >= func. operand_stack_height ( ) as usize {
1609+ stack_printer. result . write_str ( "[]" ) ?;
1610+ } else {
1611+ for depth in height..func. operand_stack_height ( ) as usize {
1612+ stack_printer. result . write_str (
1613+ if depth as u32 + push_count == func. operand_stack_height ( ) {
1614+ "|"
1615+ } else {
1616+ " "
1617+ } ,
1618+ ) ?;
1619+ match func. get_operand_type ( depth) {
1620+ Some ( Some ( ty) ) => {
1621+ stack_printer. result . write_str ( & ty_to_str ( ty) ) ?;
1622+ }
1623+ Some ( None ) => {
1624+ stack_printer. result . write_str ( "(unknown)" ) ?;
1625+ }
1626+ None => {
1627+ stack_printer. result . write_str ( "(invalid)" ) ?;
1628+ }
1629+ }
1630+ }
1631+ }
1632+ }
1633+
1634+ return Ok ( ret. 0 ) ;
1635+
1636+ fn ty_to_str ( ty : ValType ) -> String {
1637+ match ty {
1638+ ValType :: I32 => String :: from ( "i32" ) ,
1639+ ValType :: I64 => String :: from ( "i64" ) ,
1640+ ValType :: F32 => String :: from ( "f32" ) ,
1641+ ValType :: F64 => String :: from ( "f64" ) ,
1642+ ValType :: V128 => String :: from ( "v128" ) ,
1643+ ValType :: Ref ( r) => format ! ( "{r}" ) ,
1644+ }
1645+ }
1646+ }
1647+
14611648 fn newline ( & mut self , offset : usize ) -> Result < ( ) > {
14621649 self . print_newline ( Some ( offset) )
14631650 }
@@ -1726,12 +1913,10 @@ impl Printer<'_, '_> {
17261913 if fold {
17271914 let mut folded_printer = PrintOperatorFolded :: new ( self , state, & mut operator_state) ;
17281915 folded_printer. begin_const_expr ( ) ;
1729- Self :: print_operators ( & mut reader, & [ ] , 0 , & mut folded_printer) ?;
1730- folded_printer. finalize ( ) ?;
1916+ Self :: print_operators ( & mut reader, & [ ] , 0 , & mut folded_printer, None ) ?;
17311917 } else {
17321918 let mut op_printer = PrintOperator :: new ( self , state, & mut operator_state) ;
1733- op_printer. suppress_label_comments ( ) ;
1734- Self :: print_operators ( & mut reader, & [ ] , 0 , & mut op_printer) ?;
1919+ Self :: print_operators ( & mut reader, & [ ] , 0 , & mut op_printer, None ) ?;
17351920 }
17361921
17371922 Ok ( ( ) )
0 commit comments