1 #![deny(bare_trait_objects)]
2 
3 pub mod error;
4 pub mod script;
5 
6 pub use crate::error::Error;
7 pub use crate::result::{command_description, SpecScriptResult};
8 
9 mod bindings;
10 mod result;
11 
12 use crate::script::{ScriptEnv, ScriptError};
13 use lucet_runtime::{Error as RuntimeError, TrapCode, UntypedRetVal, Val};
14 use std::fs;
15 use std::path::PathBuf;
16 use wabt::script::{Action, CommandKind, ScriptParser, Value};
17 
run_spec_test(spec_path: &PathBuf) -> Result<SpecScriptResult, Error>18 pub fn run_spec_test(spec_path: &PathBuf) -> Result<SpecScriptResult, Error> {
19     let wast = fs::read_to_string(spec_path)?;
20     let mut parser: ScriptParser = ScriptParser::from_str(&wast)?;
21 
22     let mut script = ScriptEnv::new();
23     let mut res = SpecScriptResult::new();
24 
25     while let Some(ref cmd) = parser.next()? {
26         match step(&mut script, &cmd.kind) {
27             Ok(()) => res.pass(cmd),
28             Err(e) => match e {
29                 Error::UnsupportedCommand(_) | Error::UnsupportedLucetc => {
30                     println!("skipped unsupported command");
31                     res.skip(cmd, e)
32                 }
33                 _ => {
34                     println!("command failed");
35                     res.fail(cmd, e)
36                 }
37             },
38         }
39     }
40 
41     Ok(res)
42 }
43 
unexpected_failure(e: ScriptError) -> Error44 fn unexpected_failure(e: ScriptError) -> Error {
45     if e.unsupported() {
46         Error::UnsupportedLucetc
47     } else {
48         Error::UnexpectedFailure(e.to_string())
49     }
50 }
51 
step(script: &mut ScriptEnv, cmd: &CommandKind) -> Result<(), Error>52 fn step(script: &mut ScriptEnv, cmd: &CommandKind) -> Result<(), Error> {
53     match cmd {
54         CommandKind::Module {
55             ref module,
56             ref name,
57         } => {
58             println!("module {:?}", name);
59             let module = module.clone().into_vec();
60             script
61                 .instantiate(&module, name)
62                 .map_err(unexpected_failure)?;
63             Ok(())
64         }
65 
66         CommandKind::AssertInvalid { ref module, .. } => {
67             println!("assert_invalid");
68             let module = module.clone().into_vec();
69             match script.instantiate(&module, &None) {
70                 Err(ScriptError::ValidationError(_)) => Ok(()),
71                 Ok(_) => {
72                     script.delete_last();
73                     Err(Error::UnexpectedSuccess)
74                 }
75                 Err(e) => Err(unexpected_failure(e)),
76             }
77         }
78 
79         CommandKind::AssertMalformed { ref module, .. } => {
80             println!("assert_malformed");
81             let module = module.clone().into_vec();
82             match script.instantiate(&module, &None) {
83                 Err(ScriptError::ValidationError(_)) => Ok(()),
84                 Ok(_) => Err(Error::UnexpectedSuccess),
85                 Err(e) => Err(unexpected_failure(e)),
86             }
87         }
88 
89         CommandKind::AssertUninstantiable { module, .. } => {
90             println!("assert_uninstantiable");
91             let module = module.clone().into_vec();
92             match script.instantiate(&module, &None) {
93                 Err(ScriptError::InstantiateError(_)) => Ok(()),
94                 Ok(_) => Err(Error::UnexpectedSuccess),
95                 Err(e) => Err(unexpected_failure(e)),
96             }
97         }
98 
99         CommandKind::AssertUnlinkable { module, .. } => {
100             println!("assert_unlinkable");
101             let module = module.clone().into_vec();
102             match script.instantiate(&module, &None) {
103                 Err(ScriptError::ValidationError(_)) => Ok(()),
104                 Ok(_) => Err(Error::UnexpectedSuccess),
105                 Err(e) => Err(unexpected_failure(e)),
106             }
107         }
108 
109         CommandKind::Register {
110             ref name,
111             ref as_name,
112         } => {
113             println!("register {:?} {}", name, as_name);
114             script.register(name, as_name).map_err(unexpected_failure)?;
115             Ok(())
116         }
117 
118         CommandKind::PerformAction(ref action) => match action {
119             Action::Invoke {
120                 ref module,
121                 ref field,
122                 ref args,
123             } => {
124                 println!("invoke {:?} {} {:?}", module, field, args);
125                 let args = translate_args(args);
126                 let _res = script
127                     .run(module, field, args)
128                     .map_err(unexpected_failure)?;
129                 Ok(())
130             }
131             _ => {
132                 let message = format!("invoke {:?}", action);
133                 Err(Error::UnsupportedCommand(message))
134             }
135         },
136 
137         // TODO: verify the exhaustion message is what we expected
138         CommandKind::AssertExhaustion {
139             ref action,
140             message: _,
141         } => match action {
142             Action::Invoke {
143                 ref module,
144                 ref field,
145                 ref args,
146             } => {
147                 println!("assert_exhaustion {:?} {} {:?}", module, field, args);
148                 let args = translate_args(args);
149                 let res = script.run(module, field, args);
150                 match res {
151                     Ok(_) => Err(Error::UnexpectedSuccess),
152 
153                     Err(ScriptError::RuntimeError(RuntimeError::RuntimeFault(details))) => {
154                         match details.trapcode {
155                             Some(TrapCode::StackOverflow) => Ok(()),
156                             e => {
157                                 let message =
158                                     format!("AssertExhaustion expects stack overflow, got {:?}", e);
159                                 Err(Error::UnexpectedFailure(message))
160                             }
161                         }
162                     }
163 
164                     Err(e) => Err(unexpected_failure(e)),
165                 }
166             }
167             _ => {
168                 let message = format!("invoke {:?}", action);
169                 Err(Error::UnsupportedCommand(message))
170             }
171         },
172 
173         CommandKind::AssertReturn {
174             ref action,
175             ref expected,
176         } => match action {
177             Action::Invoke {
178                 ref module,
179                 ref field,
180                 ref args,
181             } => {
182                 println!(
183                     "assert_return (invoke {:?} {} {:?}) {:?}",
184                     module, field, args, expected
185                 );
186                 let args = translate_args(args);
187                 let res = script
188                     .run(module, field, args)
189                     .map_err(unexpected_failure)?;
190                 check_retval(expected, res)?;
191                 Ok(())
192             }
193             _ => Err(Error::UnsupportedCommand("non-invoke action".to_owned())),
194         },
195         CommandKind::AssertReturnCanonicalNan { action }
196         | CommandKind::AssertReturnArithmeticNan { action } => match action {
197             Action::Invoke {
198                 ref module,
199                 ref field,
200                 ref args,
201             } => {
202                 println!("assert_nan");
203                 let args = translate_args(args);
204                 let res = script
205                     .run(module, field, args)
206                     .map_err(unexpected_failure)?;
207                 if res.as_f32().is_nan() || res.as_f64().is_nan() {
208                     Ok(())
209                 } else {
210                     let message = format!("expected NaN, got {}", res);
211                     Err(Error::IncorrectResult(message))
212                 }
213             }
214             _ => Err(Error::UnsupportedCommand("non-invoke action".to_owned())),
215         },
216         CommandKind::AssertTrap { ref action, .. } => match action {
217             Action::Invoke {
218                 module,
219                 field,
220                 args,
221             } => {
222                 println!("assert_trap (invoke {:?} {} {:?})", module, field, args);
223                 let args = translate_args(args);
224                 let res = script.run(module, field, args);
225                 match res {
226                     Err(ScriptError::RuntimeError(_luceterror)) => Ok(()),
227                     Err(e) => Err(unexpected_failure(e)),
228                     Ok(_) => Err(Error::UnexpectedSuccess),
229                 }
230             }
231             _ => {
232                 let message = format!("invoke {:?}", action);
233                 Err(Error::UnsupportedCommand(message))
234             }
235         },
236     }
237 }
238 
check_retval(expected: &[Value], got: UntypedRetVal) -> Result<(), Error>239 fn check_retval(expected: &[Value], got: UntypedRetVal) -> Result<(), Error> {
240     match expected.len() {
241         0 => {}
242         1 => match expected[0] {
243             Value::I32(expected) => {
244                 if expected != got.as_i32() {
245                     let message = format!("expected {}, got {}", expected, got.as_i32());
246                     return Err(Error::IncorrectResult(message));
247                 }
248             }
249             Value::I64(expected) => {
250                 if expected != got.as_i64() {
251                     let message = format!("expected {}, got {}", expected, got.as_i64());
252                     return Err(Error::IncorrectResult(message));
253                 }
254             }
255             Value::F32(expected) => {
256                 if expected != got.as_f32() && !expected.is_nan() && !got.as_f32().is_nan() {
257                     let message = format!("expected {}, got {}", expected, got.as_f32());
258                     return Err(Error::IncorrectResult(message));
259                 }
260             }
261             Value::F64(expected) => {
262                 if expected != got.as_f64() && !expected.is_nan() && !got.as_f64().is_nan() {
263                     let message = format!("expected {}, got {}", expected, got.as_f64());
264                     return Err(Error::IncorrectResult(message));
265                 }
266             }
267             Value::V128(v) => {
268                 let message = format!("got unsupported SIMD V128 value: {}", v);
269                 return Err(Error::UnsupportedCommand(message));
270             }
271         },
272         n => {
273             let message = format!("{} expected return values not supported", n);
274             return Err(Error::UnsupportedCommand(message));
275         }
276     }
277     Ok(())
278 }
279 
translate_args(args: &[Value]) -> Vec<Val>280 fn translate_args(args: &[Value]) -> Vec<Val> {
281     let mut out = Vec::new();
282     for a in args {
283         let v = match a {
284             Value::I32(ref i) => Val::U32(*i as u32),
285             Value::I64(ref i) => Val::U64(*i as u64),
286             Value::F32(ref f) => Val::F32(*f),
287             Value::F64(ref f) => Val::F64(*f),
288             Value::V128(_) => panic!("unsupported SIMD argument size: v128"),
289         };
290         out.push(v);
291     }
292     out
293 }
294