@@ -57,6 +57,7 @@ impl<T: WasmModuleResources> FuncToValidate<T> {
5757///
5858/// This is a finalized validator which is ready to process a [`FunctionBody`].
5959/// This is created from the [`FuncToValidate::into_validator`] method.
60+ #[ derive( Clone ) ]
6061pub struct FuncValidator < T > {
6162 validator : OperatorValidator ,
6263 resources : T ,
@@ -121,6 +122,19 @@ impl<T: WasmModuleResources> FuncValidator<T> {
121122 reader. set_features ( self . validator . features ) ;
122123 }
123124 while !reader. eof ( ) {
125+ // In a `debug_check_try_op` build, verify that `rollback` successfully returns the
126+ // validator to its previous state after each (valid or invalid) operator.
127+ #[ cfg( all( debug_check_try_op, feature = "try-op" ) ) ]
128+ {
129+ let snapshot = self . validator . clone ( ) ;
130+ let op = reader. peek_operator ( & self . visitor ( reader. original_position ( ) ) ) ?;
131+ self . validator . begin_try_op ( ) ;
132+ let _ = self . op ( reader. original_position ( ) , & op) ;
133+ self . validator . rollback ( ) ;
134+ self . validator . pop_push_log . clear ( ) ;
135+ assert ! ( self . validator == snapshot) ;
136+ }
137+
124138 // In a debug build, verify that the validator's pops and pushes to and from
125139 // the operand stack match the operator's arity.
126140 #[ cfg( debug_assertions) ]
@@ -194,14 +208,30 @@ arity mismatch in validation
194208
195209 /// Validates the next operator in a function.
196210 ///
197- /// This functions is expected to be called once-per-operator in a
211+ /// This function is expected to be called once-per-operator in a
198212 /// WebAssembly function. Each operator's offset in the original binary and
199213 /// the operator itself are passed to this function to provide more useful
200- /// error messages.
214+ /// error messages. On error, the validator may be left in an undefined
215+ /// state and should not be reused.
201216 pub fn op ( & mut self , offset : usize , operator : & Operator < ' _ > ) -> Result < ( ) > {
202217 self . visitor ( offset) . visit_operator ( operator)
203218 }
204219
220+ /// Validates the next operator in a function, rolling back the validator
221+ /// to its previous state if this is unsuccesful. The validator may be reused
222+ /// even after an error.
223+ #[ cfg( feature = "try-op" ) ]
224+ pub fn try_op ( & mut self , offset : usize , operator : & Operator < ' _ > ) -> Result < ( ) > {
225+ self . validator . begin_try_op ( ) ;
226+ let res = self . op ( offset, operator) ;
227+ if res. is_ok ( ) {
228+ self . validator . commit ( ) ;
229+ } else {
230+ self . validator . rollback ( ) ;
231+ }
232+ res
233+ }
234+
205235 /// Get the operator visitor for the next operator in the function.
206236 ///
207237 /// The returned visitor is intended to visit just one instruction at the `offset`.
0 commit comments