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