@@ -10,6 +10,7 @@ use crate::environ::{
10
10
WasmFuncType , WasmResult ,
11
11
} ;
12
12
use crate :: func_translator:: FuncTranslator ;
13
+ use crate :: state:: FuncTranslationState ;
13
14
use crate :: translation_utils:: {
14
15
DataIndex , DefinedFuncIndex , ElemIndex , FuncIndex , Global , GlobalIndex , Memory , MemoryIndex ,
15
16
Table , TableIndex , TypeIndex ,
@@ -25,7 +26,7 @@ use cranelift_frontend::FunctionBuilder;
25
26
use std:: boxed:: Box ;
26
27
use std:: string:: String ;
27
28
use std:: vec:: Vec ;
28
- use wasmparser:: { FuncValidator , FunctionBody , ValidatorResources , WasmFeatures } ;
29
+ use wasmparser:: { FuncValidator , FunctionBody , Operator , ValidatorResources , WasmFeatures } ;
29
30
30
31
/// Compute a `ir::ExternalName` for a given wasm function index.
31
32
fn get_func_name ( func_index : FuncIndex ) -> ir:: ExternalName {
@@ -111,6 +112,31 @@ impl DummyModuleInfo {
111
112
}
112
113
}
113
114
115
+ /// State for tracking and checking reachability at each operator. Used for unit testing with the
116
+ /// `DummyEnvironment`.
117
+ #[ derive( Clone ) ]
118
+ pub struct ExpectedReachability {
119
+ /// Before- and after-reachability
120
+ reachability : Vec < ( bool , bool ) > ,
121
+ before_idx : usize ,
122
+ after_idx : usize ,
123
+ }
124
+
125
+ impl ExpectedReachability {
126
+ fn check_before ( & mut self , reachable : bool ) {
127
+ assert_eq ! ( reachable, self . reachability[ self . before_idx] . 0 ) ;
128
+ self . before_idx += 1 ;
129
+ }
130
+ fn check_after ( & mut self , reachable : bool ) {
131
+ assert_eq ! ( reachable, self . reachability[ self . after_idx] . 1 ) ;
132
+ self . after_idx += 1 ;
133
+ }
134
+ fn check_end ( & self ) {
135
+ assert_eq ! ( self . before_idx, self . reachability. len( ) ) ;
136
+ assert_eq ! ( self . after_idx, self . reachability. len( ) ) ;
137
+ }
138
+ }
139
+
114
140
/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
115
141
/// emitting placeholders when forced to. Don't try to execute code translated for this
116
142
/// environment, essentially here for translation debug purposes.
@@ -135,6 +161,9 @@ pub struct DummyEnvironment {
135
161
136
162
/// Function names.
137
163
function_names : SecondaryMap < FuncIndex , String > ,
164
+
165
+ /// Expected reachability data (before/after for each op) to assert. This is used for testing.
166
+ expected_reachability : Option < ExpectedReachability > ,
138
167
}
139
168
140
169
impl DummyEnvironment {
@@ -148,13 +177,18 @@ impl DummyEnvironment {
148
177
debug_info,
149
178
module_name : None ,
150
179
function_names : SecondaryMap :: new ( ) ,
180
+ expected_reachability : None ,
151
181
}
152
182
}
153
183
154
184
/// Return a `DummyFuncEnvironment` for translating functions within this
155
185
/// `DummyEnvironment`.
156
186
pub fn func_env ( & self ) -> DummyFuncEnvironment {
157
- DummyFuncEnvironment :: new ( & self . info , self . return_mode )
187
+ DummyFuncEnvironment :: new (
188
+ & self . info ,
189
+ self . return_mode ,
190
+ self . expected_reachability . clone ( ) ,
191
+ )
158
192
}
159
193
160
194
fn get_func_type ( & self , func_index : FuncIndex ) -> TypeIndex {
@@ -171,20 +205,39 @@ impl DummyEnvironment {
171
205
pub fn get_func_name ( & self , func_index : FuncIndex ) -> Option < & str > {
172
206
self . function_names . get ( func_index) . map ( String :: as_ref)
173
207
}
208
+
209
+ /// Test reachability bits before and after every opcode during translation, as provided by the
210
+ /// `FuncTranslationState`. This is generally used only for unit tests. This is applied to
211
+ /// every function in the module (so is likely only useful for test modules with one function).
212
+ pub fn test_expected_reachability ( & mut self , reachability : Vec < ( bool , bool ) > ) {
213
+ self . expected_reachability = Some ( ExpectedReachability {
214
+ reachability,
215
+ before_idx : 0 ,
216
+ after_idx : 0 ,
217
+ } ) ;
218
+ }
174
219
}
175
220
176
221
/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
177
222
pub struct DummyFuncEnvironment < ' dummy_environment > {
178
223
pub mod_info : & ' dummy_environment DummyModuleInfo ,
179
224
180
225
return_mode : ReturnMode ,
226
+
227
+ /// Expected reachability data (before/after for each op) to assert. This is used for testing.
228
+ expected_reachability : Option < ExpectedReachability > ,
181
229
}
182
230
183
231
impl < ' dummy_environment > DummyFuncEnvironment < ' dummy_environment > {
184
- pub fn new ( mod_info : & ' dummy_environment DummyModuleInfo , return_mode : ReturnMode ) -> Self {
232
+ pub fn new (
233
+ mod_info : & ' dummy_environment DummyModuleInfo ,
234
+ return_mode : ReturnMode ,
235
+ expected_reachability : Option < ExpectedReachability > ,
236
+ ) -> Self {
185
237
Self {
186
238
mod_info,
187
239
return_mode,
240
+ expected_reachability,
188
241
}
189
242
}
190
243
@@ -307,6 +360,41 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
307
360
} ) )
308
361
}
309
362
363
+ fn before_translate_operator (
364
+ & mut self ,
365
+ _op : & Operator ,
366
+ _builder : & mut FunctionBuilder ,
367
+ state : & FuncTranslationState ,
368
+ ) -> WasmResult < ( ) > {
369
+ if let Some ( ref mut r) = & mut self . expected_reachability {
370
+ r. check_before ( state. reachable ( ) ) ;
371
+ }
372
+ Ok ( ( ) )
373
+ }
374
+
375
+ fn after_translate_operator (
376
+ & mut self ,
377
+ _op : & Operator ,
378
+ _builder : & mut FunctionBuilder ,
379
+ state : & FuncTranslationState ,
380
+ ) -> WasmResult < ( ) > {
381
+ if let Some ( ref mut r) = & mut self . expected_reachability {
382
+ r. check_after ( state. reachable ( ) ) ;
383
+ }
384
+ Ok ( ( ) )
385
+ }
386
+
387
+ fn after_translate_function (
388
+ & mut self ,
389
+ _builder : & mut FunctionBuilder ,
390
+ _state : & FuncTranslationState ,
391
+ ) -> WasmResult < ( ) > {
392
+ if let Some ( ref mut r) = & mut self . expected_reachability {
393
+ r. check_end ( ) ;
394
+ }
395
+ Ok ( ( ) )
396
+ }
397
+
310
398
fn translate_call_indirect (
311
399
& mut self ,
312
400
mut pos : FuncCursor ,
@@ -746,7 +834,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
746
834
self . func_bytecode_sizes
747
835
. push ( body. get_binary_reader ( ) . bytes_remaining ( ) ) ;
748
836
let func = {
749
- let mut func_environ = DummyFuncEnvironment :: new ( & self . info , self . return_mode ) ;
837
+ let mut func_environ = DummyFuncEnvironment :: new (
838
+ & self . info ,
839
+ self . return_mode ,
840
+ self . expected_reachability . clone ( ) ,
841
+ ) ;
750
842
let func_index =
751
843
FuncIndex :: new ( self . get_num_func_imports ( ) + self . info . function_bodies . len ( ) ) ;
752
844
let name = get_func_name ( func_index) ;
0 commit comments