@@ -10,13 +10,19 @@ use wasm_encoder::{
1010 TypeSection , ValType ,
1111} ;
1212
13- /// A description of a Wasm module that makes a series of `externref` table
14- /// operations.
13+ /// Limits controlling the structure of a generated Wasm module.
1514#[ derive( Debug , Default , Serialize , Deserialize ) ]
16- pub struct TableOps {
15+ pub struct TableOpsLimits {
1716 pub ( crate ) num_params : u32 ,
1817 pub ( crate ) num_globals : u32 ,
1918 pub ( crate ) table_size : i32 ,
19+ }
20+
21+ /// A description of a Wasm module that makes a series of `externref` table
22+ /// operations.
23+ #[ derive( Debug , Default , Serialize , Deserialize ) ]
24+ pub struct TableOps {
25+ pub ( crate ) limits : TableOpsLimits ,
2026 pub ( crate ) ops : Vec < TableOp > ,
2127}
2228
@@ -37,7 +43,21 @@ impl TableOps {
3743 /// The "run" function does not terminate; you should run it with limited
3844 /// fuel. It also is not guaranteed to avoid traps: it may access
3945 /// out-of-bounds of the table.
40- pub fn to_wasm_binary ( & self ) -> Vec < u8 > {
46+ pub fn to_wasm_binary ( & mut self ) -> Vec < u8 > {
47+ // Clamp limits to generate opcodes within bounds
48+ self . limits . table_size = self
49+ . limits
50+ . table_size
51+ . clamp ( * TABLE_SIZE_RANGE . start ( ) , * TABLE_SIZE_RANGE . end ( ) ) ;
52+ self . limits . num_params = self
53+ . limits
54+ . num_params
55+ . clamp ( * NUM_PARAMS_RANGE . start ( ) , * NUM_PARAMS_RANGE . end ( ) ) ;
56+ self . limits . num_globals = self
57+ . limits
58+ . num_globals
59+ . clamp ( * NUM_GLOBALS_RANGE . start ( ) , * NUM_GLOBALS_RANGE . end ( ) ) ;
60+
4161 let mut module = Module :: new ( ) ;
4262
4363 // Encode the types for all functions that we are using.
@@ -56,8 +76,8 @@ impl TableOps {
5676 ) ;
5777
5878 // 1: "run"
59- let mut params: Vec < ValType > = Vec :: with_capacity ( self . num_params as usize ) ;
60- for _i in 0 ..self . num_params {
79+ let mut params: Vec < ValType > = Vec :: with_capacity ( self . limits . num_params as usize ) ;
80+ for _i in 0 ..self . limits . num_params {
6181 params. push ( ValType :: EXTERNREF ) ;
6282 }
6383 let results = vec ! [ ] ;
@@ -85,15 +105,15 @@ impl TableOps {
85105 let mut tables = TableSection :: new ( ) ;
86106 tables. table ( TableType {
87107 element_type : RefType :: EXTERNREF ,
88- minimum : self . table_size as u64 ,
108+ minimum : self . limits . table_size as u64 ,
89109 maximum : None ,
90110 table64 : false ,
91111 shared : false ,
92112 } ) ;
93113
94114 // Define our globals.
95115 let mut globals = GlobalSection :: new ( ) ;
96- for _ in 0 ..self . num_globals {
116+ for _ in 0 ..self . limits . num_globals {
97117 globals. global (
98118 wasm_encoder:: GlobalType {
99119 val_type : wasm_encoder:: ValType :: EXTERNREF ,
@@ -117,7 +137,7 @@ impl TableOps {
117137
118138 func. instruction ( & Instruction :: Loop ( wasm_encoder:: BlockType :: Empty ) ) ;
119139 for op in & self . ops {
120- op. insert ( & mut func, self . num_params ) ;
140+ op. insert ( & mut func, self . limits . num_params ) ;
121141 }
122142 func. instruction ( & Instruction :: Br ( 0 ) ) ;
123143 func. instruction ( & Instruction :: End ) ;
@@ -154,14 +174,14 @@ impl TableOps {
154174 /// Fixes the stack after mutating the `idx`th op.
155175 ///
156176 /// The abstract stack depth starting at the `idx`th opcode must be `stack`.
157- fn fixup ( & mut self , idx : usize , mut stack : usize ) {
177+ ///
178+ fn fixup ( & mut self ) {
158179 let mut new_ops = Vec :: with_capacity ( self . ops . len ( ) ) ;
159- new_ops. extend_from_slice ( & self . ops [ ..idx] ) ;
180+ let mut stack = 0 ;
181+
182+ for mut op in self . ops . iter ( ) . copied ( ) {
183+ op. fixup ( & self . limits ) ;
160184
161- // Iterate through all ops including and after `idx`, inserting a null
162- // ref for any missing operands when they want to pop more operands than
163- // exist on the stack.
164- new_ops. extend ( self . ops [ idx..] . iter ( ) . copied ( ) . flat_map ( |op| {
165185 let mut temp = SmallVec :: < [ _ ; 4 ] > :: new ( ) ;
166186
167187 while stack < op. operands_len ( ) {
@@ -172,11 +192,10 @@ impl TableOps {
172192 temp. push ( op) ;
173193 stack = stack - op. operands_len ( ) + op. results_len ( ) ;
174194
175- temp
176- } ) ) ;
195+ new_ops . extend ( temp) ;
196+ }
177197
178- // Now make sure that the stack is empty at the end of the ops by
179- // inserting drops as necessary.
198+ // Insert drops to balance the final stack state
180199 for _ in 0 ..stack {
181200 new_ops. push ( TableOp :: Drop ( ) ) ;
182201 }
@@ -205,7 +224,7 @@ impl Mutate<TableOps> for TableOpsMutator {
205224 let stack = ops. abstract_stack_depth ( idx) ;
206225 let ( op, _new_stack_size) = TableOp :: generate ( ctx, & ops, stack) ?;
207226 ops. ops . insert ( idx, op) ;
208- ops. fixup ( idx , stack ) ;
227+ ops. fixup ( ) ;
209228 }
210229 Ok ( ( ) )
211230 } ) ?;
@@ -218,9 +237,8 @@ impl Mutate<TableOps> for TableOpsMutator {
218237 . rng ( )
219238 . gen_index ( ops. ops . len ( ) )
220239 . expect ( "ops is not empty" ) ;
221- let stack = ops. abstract_stack_depth ( idx) ;
222240 ops. ops . remove ( idx) ;
223- ops. fixup ( idx , stack ) ;
241+ ops. fixup ( ) ;
224242 Ok ( ( ) )
225243 } ) ?;
226244 }
@@ -255,9 +273,11 @@ impl Generate<TableOps> for TableOpsMutator {
255273 let table_size = m:: range ( TABLE_SIZE_RANGE ) . generate ( ctx) ?;
256274
257275 let mut ops = TableOps {
258- num_params,
259- num_globals,
260- table_size,
276+ limits : TableOpsLimits {
277+ num_params,
278+ num_globals,
279+ table_size,
280+ } ,
261281 ops : vec ! [
262282 TableOp :: Null ( ) ,
263283 TableOp :: Drop ( ) ,
@@ -288,7 +308,7 @@ impl Generate<TableOps> for TableOpsMutator {
288308macro_rules! define_table_ops {
289309 (
290310 $(
291- $op: ident $( ( $( $limit: expr => $ty: ty) ,* ) ) ? : $params: expr => $results: expr ,
311+ $op: ident $( ( $( $limit_var : ident : $ limit: expr => $ty: ty) ,* ) ) ? : $params: expr => $results: expr ,
292312 ) *
293313 ) => {
294314 #[ derive( Copy , Clone , Debug , Serialize , Deserialize ) ]
@@ -335,7 +355,7 @@ macro_rules! define_table_ops {
335355 #[ allow( non_snake_case, reason = "macro-generated code" ) ]
336356 fn $op(
337357 _ctx: & mut mutatis:: Context ,
338- _ops : & TableOps ,
358+ _limits : & TableOpsLimits ,
339359 stack: usize ,
340360 ) -> mutatis:: Result <( TableOp , usize ) > {
341361 #[ allow( unused_comparisons, reason = "macro-generated code" ) ]
@@ -345,8 +365,8 @@ macro_rules! define_table_ops {
345365
346366 let op = TableOp :: $op(
347367 $( $( {
348- let limit_fn = $limit as fn ( & TableOps ) -> $ty;
349- let limit = ( limit_fn) ( _ops ) ;
368+ let limit_fn = $limit as fn ( & TableOpsLimits ) -> $ty;
369+ let limit = ( limit_fn) ( _limits ) ;
350370 debug_assert!( limit > 0 ) ;
351371 m:: range( 0 ..=limit - 1 ) . generate( _ctx) ?
352372 } ) * ) ?
@@ -357,21 +377,35 @@ macro_rules! define_table_ops {
357377 ) *
358378
359379 impl TableOp {
380+ fn fixup( & mut self , limits: & TableOpsLimits ) {
381+ match self {
382+ $(
383+ Self :: $op( $( $( $limit_var ) ,* ) ? ) => {
384+ $( $(
385+ let limit_fn = $limit as fn ( & TableOpsLimits ) -> $ty;
386+ let limit = ( limit_fn) ( limits) ;
387+ debug_assert!( limit > 0 ) ;
388+ * $limit_var = * $limit_var % limit;
389+ ) * ) ?
390+ }
391+ ) *
392+ }
393+ }
394+
360395 fn generate(
361396 ctx: & mut mutatis:: Context ,
362397 ops: & TableOps ,
363398 stack: usize ,
364399 ) -> mutatis:: Result <( TableOp , usize ) > {
365400 let mut valid_choices: Vec <
366- fn ( & mut mutatis :: Context , & TableOps , usize ) -> mutatis:: Result <( TableOp , usize ) >
401+ fn ( & mut Context , & TableOpsLimits , usize ) -> mutatis:: Result <( TableOp , usize ) >
367402 > = vec![ ] ;
368-
369403 $(
370404 #[ allow( unused_comparisons, reason = "macro-generated code" ) ]
371405 if stack >= $params $( $(
372406 && {
373- let limit_fn: fn ( & TableOps ) -> $ty = $limit ;
374- let limit = ( limit_fn) ( ops) ;
407+ let limit_fn = $limit as fn ( & TableOpsLimits ) -> $ty;
408+ let limit = ( limit_fn) ( & ops. limits ) ;
375409 limit > 0
376410 }
377411 ) * ) ? {
@@ -383,7 +417,7 @@ macro_rules! define_table_ops {
383417 . choose( & valid_choices)
384418 . expect( "should always have a valid op choice" ) ;
385419
386- ( f) ( ctx, ops, stack)
420+ ( f) ( ctx, & ops. limits , stack)
387421 }
388422 }
389423 } ;
@@ -396,14 +430,14 @@ define_table_ops! {
396430 TakeRefs : 3 => 0 ,
397431
398432 // Add one to make sure that out of bounds table accesses are possible, but still rare.
399- TableGet ( |ops| ops. table_size + 1 => i32 ) : 0 => 1 ,
400- TableSet ( |ops| ops. table_size + 1 => i32 ) : 1 => 0 ,
433+ TableGet ( elem_index : |ops| ops. table_size + 1 => i32 ) : 0 => 1 ,
434+ TableSet ( elem_index : |ops| ops. table_size + 1 => i32 ) : 1 => 0 ,
401435
402- GlobalGet ( |ops| ops. num_globals => u32 ) : 0 => 1 ,
403- GlobalSet ( |ops| ops. num_globals => u32 ) : 1 => 0 ,
436+ GlobalGet ( global_index : |ops| ops. num_globals => u32 ) : 0 => 1 ,
437+ GlobalSet ( global_index : |ops| ops. num_globals => u32 ) : 1 => 0 ,
404438
405- LocalGet ( |ops| ops. num_params => u32 ) : 0 => 1 ,
406- LocalSet ( |ops| ops. num_params => u32 ) : 1 => 0 ,
439+ LocalGet ( local_index : |ops| ops. num_params => u32 ) : 0 => 1 ,
440+ LocalSet ( local_index : |ops| ops. num_params => u32 ) : 1 => 0 ,
407441
408442 Drop : 1 => 0 ,
409443
@@ -465,19 +499,23 @@ mod tests {
465499 /// Creates empty TableOps
466500 fn empty_test_ops ( num_params : u32 , num_globals : u32 , table_size : i32 ) -> TableOps {
467501 TableOps {
468- num_params,
469- num_globals,
470- table_size,
502+ limits : TableOpsLimits {
503+ num_params,
504+ num_globals,
505+ table_size,
506+ } ,
471507 ops : vec ! [ ] ,
472508 }
473509 }
474510
475511 /// Creates TableOps with all default opcodes
476512 fn test_ops ( num_params : u32 , num_globals : u32 , table_size : i32 ) -> TableOps {
477513 TableOps {
478- num_params,
479- num_globals,
480- table_size,
514+ limits : TableOpsLimits {
515+ num_params,
516+ num_globals,
517+ table_size,
518+ } ,
481519 ops : vec ! [
482520 TableOp :: Null ( ) ,
483521 TableOp :: Drop ( ) ,
0 commit comments