1 /* Copyright 2017 Mozilla Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #[cfg(feature = "std")]
17 #[cfg(test)]
18 mod simple_tests {
19     use crate::operators_validator::OperatorValidatorConfig;
20     use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder};
21     use crate::primitives::{Operator, SectionCode};
22     use crate::validator::{ValidatingParser, ValidatingParserConfig};
23     use std::fs::{read_dir, File};
24     use std::io::prelude::*;
25     use std::path::PathBuf;
26 
27     const VALIDATOR_CONFIG: Option<ValidatingParserConfig> = Some(ValidatingParserConfig {
28         operator_config: OperatorValidatorConfig {
29             enable_threads: true,
30             enable_reference_types: true,
31             enable_simd: true,
32             enable_bulk_memory: true,
33             enable_multi_value: true,
34 
35             #[cfg(feature = "deterministic")]
36             deterministic_only: true,
37         },
38     });
39 
read_file_data(path: &PathBuf) -> Vec<u8>40     fn read_file_data(path: &PathBuf) -> Vec<u8> {
41         println!("Parsing {:?}", path);
42         let mut data = Vec::new();
43         let mut f = File::open(path).ok().unwrap();
44         f.read_to_end(&mut data).unwrap();
45         data
46     }
47 
48     #[allow(dead_code)]
scan_tests_files(prefix: &str) -> Vec<PathBuf>49     fn scan_tests_files(prefix: &str) -> Vec<PathBuf> {
50         let mut files = Vec::new();
51 
52         for entry in read_dir("tests/").unwrap() {
53             let dir = entry.unwrap();
54             if !dir.file_type().unwrap().is_file() {
55                 continue;
56             }
57 
58             let path = dir.path();
59             let file = path.to_str().unwrap();
60 
61             if file.starts_with(prefix) {
62                 files.push(path);
63             }
64         }
65 
66         return files;
67     }
68 
69     #[test]
it_works()70     fn it_works() {
71         for entry in read_dir("tests").unwrap() {
72             let dir = entry.unwrap();
73             if !dir.file_type().unwrap().is_file() {
74                 continue;
75             }
76             let data = read_file_data(&dir.path());
77             let mut parser = Parser::new(data.as_slice());
78             let mut max_iteration = 100000000;
79             loop {
80                 let state = parser.read();
81                 match *state {
82                     ParserState::EndWasm => break,
83                     ParserState::Error(err) => panic!("Error: {:?}", err),
84                     _ => (),
85                 }
86                 max_iteration -= 1;
87                 if max_iteration == 0 {
88                     panic!("Max iterations exceeded");
89                 }
90             }
91         }
92     }
93 
94     #[test]
validator_not_fails()95     fn validator_not_fails() {
96         for entry in read_dir("tests").unwrap() {
97             let dir = entry.unwrap();
98             if !dir.file_type().unwrap().is_file() {
99                 continue;
100             }
101             let data = read_file_data(&dir.path());
102             let mut parser = ValidatingParser::new(data.as_slice(), VALIDATOR_CONFIG);
103             let mut max_iteration = 100000000;
104             loop {
105                 let state = parser.read();
106                 match *state {
107                     ParserState::EndWasm => break,
108                     ParserState::Error(err) => panic!("Error: {:?}", err),
109                     _ => (),
110                 }
111                 max_iteration -= 1;
112                 if max_iteration == 0 {
113                     panic!("Max iterations exceeded");
114                 }
115             }
116         }
117     }
118 
119     #[test]
validator_fails()120     fn validator_fails() {
121         for entry in read_dir("tests/invalid").unwrap() {
122             let dir = entry.unwrap();
123             if !dir.file_type().unwrap().is_file() {
124                 continue;
125             }
126             let data = read_file_data(&dir.path());
127             let mut parser = ValidatingParser::new(data.as_slice(), VALIDATOR_CONFIG);
128             let mut max_iteration = 100000000;
129             let mut error = false;
130             loop {
131                 let state = parser.read();
132                 if let ParserState::Error(_) = *state {
133                     error = true;
134                     break;
135                 }
136                 if let ParserState::EndWasm = *state {
137                     break;
138                 }
139                 max_iteration -= 1;
140                 if max_iteration == 0 {
141                     panic!("Max iterations exceeded");
142                 }
143             }
144             if !error {
145                 panic!("fail is expected");
146             }
147         }
148     }
149 
150     macro_rules! expect_state {
151         ($state:expr, $expected:pat) => {{
152             {
153                 let state: &ParserState = $state;
154                 match *state {
155                     $expected => (),
156                     _ => panic!("Unexpected state during testing: {:?}", state),
157                 }
158             }
159         }};
160     }
161 
162     #[test]
default_read()163     fn default_read() {
164         let data = read_file_data(&PathBuf::from("tests/simple.wasm"));
165         let mut parser = Parser::new(data.as_slice());
166 
167         expect_state!(parser.read(), ParserState::BeginWasm { .. });
168         expect_state!(parser.read(), ParserState::BeginSection { code: SectionCode::Type, .. });
169         expect_state!(parser.read(), ParserState::TypeSectionEntry(_));
170         expect_state!(parser.read(), ParserState::EndSection);
171         expect_state!(parser.read(), ParserState::BeginSection { code: SectionCode::Function, .. });
172         expect_state!(parser.read(), ParserState::FunctionSectionEntry(_));
173         expect_state!(parser.read(), ParserState::EndSection);
174         expect_state!(parser.read(), ParserState::BeginSection { code: SectionCode::Code, .. });
175         expect_state!(parser.read(), ParserState::BeginFunctionBody { .. });
176         expect_state!(parser.read(), ParserState::FunctionBodyLocals { .. });
177         expect_state!(parser.read(), ParserState::CodeOperator(_));
178         expect_state!(parser.read(), ParserState::CodeOperator(Operator::End));
179         expect_state!(parser.read(), ParserState::EndFunctionBody);
180         expect_state!(parser.read(), ParserState::EndSection);
181         expect_state!(parser.read(), ParserState::EndWasm);
182     }
183 
184     #[test]
default_read_with_input()185     fn default_read_with_input() {
186         let data = read_file_data(&PathBuf::from("tests/simple.wasm"));
187         let mut parser = Parser::new(data.as_slice());
188 
189         expect_state!(parser.read(), ParserState::BeginWasm { .. });
190         expect_state!(parser.read_with_input(ParserInput::Default),
191             ParserState::BeginSection { code: SectionCode::Type, .. });
192         expect_state!(parser.read(), ParserState::TypeSectionEntry(_));
193         expect_state!(parser.read(), ParserState::EndSection);
194         expect_state!(parser.read(), ParserState::BeginSection { code: SectionCode::Function, ..});
195         expect_state!(
196             parser.read_with_input(ParserInput::ReadSectionRawData),
197             ParserState::SectionRawData(_)
198         );
199         expect_state!(parser.read(), ParserState::EndSection);
200         expect_state!(parser.read(), ParserState::BeginSection { code: SectionCode::Code, .. });
201         expect_state!(parser.read(), ParserState::BeginFunctionBody { .. });
202         expect_state!(
203             parser.read_with_input(ParserInput::SkipFunctionBody),
204             ParserState::EndSection
205         );
206         expect_state!(parser.read(), ParserState::EndWasm);
207     }
208 
209     #[test]
skipping()210     fn skipping() {
211         let data = read_file_data(&PathBuf::from("tests/naming.wasm"));
212         let mut parser = Parser::new(data.as_slice());
213 
214         expect_state!(parser.read(),
215             ParserState::BeginWasm { .. });
216         expect_state!(parser.read_with_input(ParserInput::Default),
217             ParserState::BeginSection { code: SectionCode::Type, .. });
218         expect_state!(parser.read_with_input(ParserInput::SkipSection),
219             ParserState::BeginSection { code: SectionCode::Import, ..});
220         expect_state!(parser.read_with_input(ParserInput::SkipSection),
221             ParserState::BeginSection { code: SectionCode::Function, ..});
222         expect_state!(parser.read_with_input(ParserInput::SkipSection),
223             ParserState::BeginSection { code: SectionCode::Global, ..});
224         expect_state!(parser.read_with_input(ParserInput::SkipSection),
225             ParserState::BeginSection { code: SectionCode::Export, ..});
226         expect_state!(parser.read_with_input(ParserInput::SkipSection),
227             ParserState::BeginSection { code: SectionCode::Element, ..});
228         expect_state!(parser.read_with_input(ParserInput::SkipSection),
229             ParserState::BeginSection { code: SectionCode::Code, .. });
230         expect_state!(parser.read(),
231             ParserState::BeginFunctionBody { .. });
232         expect_state!(parser.read_with_input(ParserInput::SkipFunctionBody),
233             ParserState::BeginFunctionBody { .. });
234         expect_state!(parser.read_with_input(ParserInput::SkipFunctionBody),
235             ParserState::BeginFunctionBody { .. });
236         expect_state!(parser.read_with_input(ParserInput::SkipFunctionBody),
237             ParserState::BeginFunctionBody { .. });
238         expect_state!(parser.read_with_input(ParserInput::SkipFunctionBody),
239             ParserState::BeginFunctionBody { .. });
240         expect_state!(parser.read_with_input(ParserInput::SkipFunctionBody),
241             ParserState::BeginFunctionBody { .. });
242         expect_state!(parser.read_with_input(ParserInput::SkipFunctionBody),
243             ParserState::BeginFunctionBody { .. });
244         expect_state!(
245             parser.read_with_input(ParserInput::SkipFunctionBody),
246             ParserState::EndSection
247         );
248         expect_state!(parser.read(),
249             ParserState::BeginSection { code: SectionCode::Custom { .. }, ..});
250         expect_state!(parser.read_with_input(ParserInput::SkipSection),
251             ParserState::BeginSection { code: SectionCode::Custom { .. }, .. });
252         expect_state!(
253             parser.read_with_input(ParserInput::SkipSection),
254             ParserState::EndWasm
255         );
256     }
257 
258     #[cfg(feature = "deterministic")]
run_deterministic_enabled_test(path: &PathBuf)259     fn run_deterministic_enabled_test(path: &PathBuf) {
260         let data = read_file_data(path);
261 
262         let config = Some(ValidatingParserConfig {
263             operator_config: OperatorValidatorConfig {
264                 deterministic_only: false,
265                 enable_threads: true,
266                 enable_reference_types: true,
267                 enable_simd: true,
268                 enable_bulk_memory: true,
269                 enable_multi_value: true,
270             },
271         });
272 
273         let mut parser = ValidatingParser::new(data.as_slice(), config);
274         let mut error = false;
275 
276         loop {
277             let state = parser.read();
278             if let ParserState::Error(_) = *state {
279                 error = true;
280                 break;
281             }
282             if let ParserState::EndWasm = *state {
283                 break;
284             }
285         }
286 
287         assert!(error);
288     }
289 
290     #[cfg(feature = "deterministic")]
291     #[test]
deterministic_enabled()292     fn deterministic_enabled() {
293         // `float_exprs.*.wasm`
294         let mut tests_count = 0;
295         for path in scan_tests_files("tests/float_exprs.") {
296             run_deterministic_enabled_test(&path);
297             tests_count += 1;
298         }
299         assert_eq!(96, tests_count);
300 
301         // `float_memory.*.wasm`
302         let mut tests_count = 0;
303         for path in scan_tests_files("tests/float_memory.") {
304             run_deterministic_enabled_test(&path);
305             tests_count += 1;
306         }
307         assert_eq!(6, tests_count);
308 
309         // `float_misc.*.wasm`
310         let mut tests_count = 0;
311         for path in scan_tests_files("tests/float_misc.") {
312             run_deterministic_enabled_test(&path);
313             tests_count += 1;
314         }
315         assert_eq!(1, tests_count);
316     }
317 
318 }
319 
320 #[cfg(feature = "std")]
321 #[cfg(test)]
322 mod wast_tests {
323     use crate::operators_validator::OperatorValidatorConfig;
324     use crate::parser::{ParserState, WasmDecoder};
325     use crate::validator::{ValidatingParser, ValidatingParserConfig};
326     use crate::BinaryReaderError;
327     use std::fs::{read, read_dir};
328     use wabt::script::{Command, CommandKind, ModuleBinary, ScriptParser};
329     use wabt::Features;
330 
331     const SPEC_TESTS_PATH: &str = "testsuite";
332 
default_config() -> ValidatingParserConfig333     fn default_config() -> ValidatingParserConfig {
334         ValidatingParserConfig {
335             operator_config: OperatorValidatorConfig {
336                 enable_threads: false,
337                 enable_reference_types: false,
338                 enable_simd: false,
339                 enable_bulk_memory: false,
340                 enable_multi_value: false,
341 
342                 #[cfg(feature = "deterministic")]
343                 deterministic_only: true,
344             },
345         }
346     }
347 
validate_module( module: ModuleBinary, config: ValidatingParserConfig, ) -> Result<(), BinaryReaderError>348     fn validate_module(
349         module: ModuleBinary,
350         config: ValidatingParserConfig,
351     ) -> Result<(), BinaryReaderError> {
352         let data = &module.into_vec();
353         let mut parser = ValidatingParser::new(data.as_slice(), Some(config));
354         let mut max_iteration = 100000000;
355         loop {
356             let state = parser.read();
357             match *state {
358                 ParserState::EndWasm => break,
359                 ParserState::Error(err) => return Err(err),
360                 _ => (),
361             }
362             max_iteration -= 1;
363             if max_iteration == 0 {
364                 panic!("Max iterations exceeded");
365             }
366         }
367         Ok(())
368     }
369 
to_features(config: &ValidatingParserConfig) -> Features370     fn to_features(config: &ValidatingParserConfig) -> Features {
371         let mut features = Features::new();
372         if config.operator_config.enable_simd {
373             features.enable_simd();
374         }
375         if config.operator_config.enable_reference_types {
376             features.enable_reference_types();
377         }
378         features
379     }
380 
run_wabt_scripts<F>( filename: &str, wast: &[u8], config: ValidatingParserConfig, skip_test: F, ) where F: Fn(&str, u64) -> bool,381     fn run_wabt_scripts<F>(
382         filename: &str,
383         wast: &[u8],
384         config: ValidatingParserConfig,
385         skip_test: F,
386     ) where
387         F: Fn(&str, u64) -> bool,
388     {
389         println!("Parsing {:?}", filename);
390         // Check if we need to skip entire wast file test/parsing.
391         if skip_test(filename, /* line = */ 0) {
392             println!("{}: skipping", filename);
393             return;
394         }
395 
396         let features = to_features(&config);
397         let mut parser: ScriptParser<f32, f64> =
398             ScriptParser::from_source_and_name_with_features(wast, filename, features)
399                 .expect("script parser");
400 
401         while let Some(Command { kind, line }) = parser.next().expect("parser") {
402             if skip_test(filename, line) {
403                 println!("{}:{}: skipping", filename, line);
404                 continue;
405             }
406 
407             match kind {
408                 CommandKind::Module { module, .. }
409                 | CommandKind::AssertUninstantiable { module, .. }
410                 | CommandKind::AssertUnlinkable { module, .. } => {
411                     if let Err(err) = validate_module(module, config.clone()) {
412                         panic!("{}:{}: invalid module: {}", filename, line, err.message);
413                     }
414                 }
415                 CommandKind::AssertInvalid { module, .. }
416                 | CommandKind::AssertMalformed { module, .. } => {
417                     // TODO diffentiate between assert_invalid and assert_malformed
418                     if let Ok(_) = validate_module(module, config.clone()) {
419                         panic!(
420                             "{}:{}: invalid module was successfully parsed",
421                             filename, line
422                         );
423                     }
424                     // TODO: Check the assert_invalid or assert_malformed message
425                 }
426                 CommandKind::Register { .. }
427                 | CommandKind::PerformAction(_)
428                 | CommandKind::AssertReturn { .. }
429                 | CommandKind::AssertTrap { .. }
430                 | CommandKind::AssertExhaustion { .. }
431                 | CommandKind::AssertReturnCanonicalNan { .. }
432                 | CommandKind::AssertReturnArithmeticNan { .. } => (),
433             }
434         }
435     }
436 
run_proposal_tests<F>(name: &str, config: ValidatingParserConfig, skip_test: F) where F: Fn(&str, u64) -> bool,437     fn run_proposal_tests<F>(name: &str, config: ValidatingParserConfig, skip_test: F)
438     where
439         F: Fn(&str, u64) -> bool,
440     {
441         let proposal_dir = format!("{}/proposals/{}", SPEC_TESTS_PATH, name);
442         for entry in read_dir(&proposal_dir).unwrap() {
443             let dir = entry.unwrap();
444             if !dir.file_type().unwrap().is_file()
445                 || dir.path().extension().map(|s| s.to_str().unwrap()) != Some("wast")
446             {
447                 continue;
448             }
449 
450             let data = read(&dir.path()).expect("wast data");
451             run_wabt_scripts(
452                 dir.file_name().to_str().expect("name"),
453                 &data,
454                 config,
455                 |name, line| skip_test(name, line),
456             );
457         }
458     }
459 
460     #[test]
run_proposals_tests()461     fn run_proposals_tests() {
462         run_proposal_tests(
463             "simd",
464             {
465                 let mut config: ValidatingParserConfig = default_config();
466                 config.operator_config.enable_simd = true;
467                 config
468             },
469             |name, line| match (name, line) {
470                 ("simd_address.wast", _)
471                 | ("simd_const.wast", _)
472                 | ("simd_f32x4_cmp.wast", _)
473                 | ("simd_store.wast", _)
474                 | ("simd_lane.wast", _)
475                 | ("simd_load.wast", _) => true,
476                 _ => false,
477             },
478         );
479 
480         run_proposal_tests(
481             "reference-types",
482             {
483                 let mut config: ValidatingParserConfig = default_config();
484                 config.operator_config.enable_reference_types = true;
485                 config
486             },
487             |name, line| match (name, line) {
488                 ("ref_null.wast", _)
489                 | ("ref_is_null.wast", _)
490                 | ("ref_func.wast", _)
491                 | ("linking.wast", _)
492                 | ("globals.wast", _)
493                 | ("imports.wast", _)
494                 | ("br_table.wast", _)
495                 | ("select.wast", _)
496                 | ("table_get.wast", _)
497                 | ("table_set.wast", _)
498                 | ("table_size.wast", _)
499                 | ("table_fill.wast", _)
500                 | ("table_grow.wast", _)
501                 | ("exports.wast", _) => true,
502                 _ => false,
503             },
504         );
505     }
506 
507     #[test]
run_spec_tests()508     fn run_spec_tests() {
509         for entry in read_dir(SPEC_TESTS_PATH).unwrap() {
510             let dir = entry.unwrap();
511             if !dir.file_type().unwrap().is_file()
512                 || dir.path().extension().map(|s| s.to_str().unwrap()) != Some("wast")
513             {
514                 continue;
515             }
516 
517             let data = read(&dir.path()).expect("wast data");
518             run_wabt_scripts(
519                 dir.file_name().to_str().expect("name"),
520                 &data,
521                 default_config(),
522                 |_, _| false,
523             );
524         }
525     }
526 }
527