Improve error handling, and start refactoring Instance.
Introduce proper error handling in several places, and perform a first pass at refactoring Instance to make it easier to use.
This commit is contained in:
@@ -24,6 +24,11 @@
|
||||
|
||||
extern crate cranelift_codegen;
|
||||
extern crate cranelift_wasm;
|
||||
#[macro_use]
|
||||
extern crate cranelift_entity;
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
extern crate target_lexicon;
|
||||
extern crate wabt;
|
||||
extern crate wasmtime_environ;
|
||||
@@ -32,4 +37,4 @@ extern crate wasmtime_execute;
|
||||
mod spectest;
|
||||
mod wast;
|
||||
|
||||
pub use wast::{wast_buffer, wast_file};
|
||||
pub use wast::{WastContext, WastError};
|
||||
|
||||
@@ -53,17 +53,17 @@ impl SpecTest {
|
||||
Self {
|
||||
spectest_global_i32: VMGlobal::definition(&Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::I32Const(0),
|
||||
}),
|
||||
spectest_global_f32: VMGlobal::definition(&Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::F32Const(0),
|
||||
}),
|
||||
spectest_global_f64: VMGlobal::definition(&Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::F64Const(0),
|
||||
}),
|
||||
spectest_table: VMTable::definition(ptr::null_mut(), 0),
|
||||
@@ -79,7 +79,7 @@ impl Resolver for SpecTest {
|
||||
match module {
|
||||
"spectest" => match field {
|
||||
"print" => Some(ExportValue::function(
|
||||
spectest_print as usize,
|
||||
spectest_print as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![],
|
||||
@@ -90,7 +90,7 @@ impl Resolver for SpecTest {
|
||||
),
|
||||
)),
|
||||
"print_i32" => Some(ExportValue::function(
|
||||
spectest_print_i32 as usize,
|
||||
spectest_print_i32 as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32)],
|
||||
@@ -101,7 +101,7 @@ impl Resolver for SpecTest {
|
||||
),
|
||||
)),
|
||||
"print_i64" => Some(ExportValue::function(
|
||||
spectest_print_i64 as usize,
|
||||
spectest_print_i64 as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I64)],
|
||||
@@ -112,7 +112,7 @@ impl Resolver for SpecTest {
|
||||
),
|
||||
)),
|
||||
"print_f32" => Some(ExportValue::function(
|
||||
spectest_print_f32 as usize,
|
||||
spectest_print_f32 as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F32)],
|
||||
@@ -123,7 +123,7 @@ impl Resolver for SpecTest {
|
||||
),
|
||||
)),
|
||||
"print_f64" => Some(ExportValue::function(
|
||||
spectest_print_f64 as usize,
|
||||
spectest_print_f64 as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64)],
|
||||
@@ -134,7 +134,7 @@ impl Resolver for SpecTest {
|
||||
),
|
||||
)),
|
||||
"print_i32_f32" => Some(ExportValue::function(
|
||||
spectest_print_i32_f32 as usize,
|
||||
spectest_print_i32_f32 as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![
|
||||
@@ -148,7 +148,7 @@ impl Resolver for SpecTest {
|
||||
),
|
||||
)),
|
||||
"print_f64_f64" => Some(ExportValue::function(
|
||||
spectest_print_f64_f64 as usize,
|
||||
spectest_print_f64_f64 as *const u8,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![
|
||||
|
||||
@@ -1,24 +1,98 @@
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use spectest::SpecTest;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
use wabt::script::{self, Action, Command, CommandKind, ModuleBinary, ScriptParser};
|
||||
use wasmtime_execute::{ActionOutcome, Code, InstanceWorld, Value};
|
||||
use std::{fmt, fs, io, str};
|
||||
use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value};
|
||||
use wasmtime_execute::{ActionError, ActionOutcome, Code, InstanceWorld, RuntimeValue};
|
||||
|
||||
struct Instances {
|
||||
current: Option<InstanceWorld>,
|
||||
namespace: HashMap<String, InstanceWorld>,
|
||||
/// Translate from a script::Value to a RuntimeValue.
|
||||
fn runtime_value(v: Value) -> RuntimeValue {
|
||||
match v {
|
||||
Value::I32(x) => RuntimeValue::I32(x),
|
||||
Value::I64(x) => RuntimeValue::I64(x),
|
||||
Value::F32(x) => RuntimeValue::F32(x.to_bits()),
|
||||
Value::F64(x) => RuntimeValue::F64(x.to_bits()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates an unknown module was specified.
|
||||
#[derive(Fail, Debug)]
|
||||
pub struct UnknownModule {
|
||||
module: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for UnknownModule {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.module {
|
||||
None => write!(f, "no default module present"),
|
||||
Some(ref name) => write!(f, "no module {} present", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error message used by `WastContext`.
|
||||
#[derive(Fail, Debug)]
|
||||
pub enum WastError {
|
||||
/// An assert command was not satisfied.
|
||||
Assert(String),
|
||||
/// An unknown module name was used.
|
||||
Module(UnknownModule),
|
||||
/// An error occured while performing an action.
|
||||
Action(ActionError),
|
||||
/// An action trapped.
|
||||
Trap(String),
|
||||
/// There was a type error in inputs or outputs of an action.
|
||||
Type(String),
|
||||
/// The was an I/O error while reading the wast file.
|
||||
IO(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for WastError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
WastError::Assert(ref message) => write!(f, "Assert command failed: {}", message),
|
||||
WastError::Module(ref error) => error.fmt(f),
|
||||
WastError::Action(ref error) => error.fmt(f),
|
||||
WastError::Trap(ref message) => write!(f, "trap: {}", message),
|
||||
WastError::Type(ref message) => write!(f, "type error: {}", message),
|
||||
WastError::IO(ref error) => write!(f, "I/O error: {}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error message with a source file and line number.
|
||||
#[derive(Fail, Debug)]
|
||||
#[fail(display = "{}:{}: {}", filename, line, error)]
|
||||
pub struct WastFileError {
|
||||
filename: String,
|
||||
line: u64,
|
||||
error: WastError,
|
||||
}
|
||||
|
||||
/// An opaque reference to an `InstanceWorld`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct WorldIndex(u32);
|
||||
entity_impl!(WorldIndex, "world");
|
||||
|
||||
/// The wast test script language allows modules to be defined and actions
|
||||
/// to be performed on them.
|
||||
pub struct WastContext {
|
||||
/// A namespace of wasm modules, keyed by an optional name.
|
||||
worlds: PrimaryMap<WorldIndex, InstanceWorld>,
|
||||
current: Option<WorldIndex>,
|
||||
namespace: HashMap<String, WorldIndex>,
|
||||
code: Code,
|
||||
spectest: SpecTest,
|
||||
}
|
||||
|
||||
impl Instances {
|
||||
impl WastContext {
|
||||
/// Construct a new instance of `WastContext`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
worlds: PrimaryMap::new(),
|
||||
current: None,
|
||||
namespace: HashMap::new(),
|
||||
code: Code::new(),
|
||||
@@ -30,290 +104,380 @@ impl Instances {
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
module: ModuleBinary,
|
||||
) -> Result<InstanceWorld, String> {
|
||||
) -> Result<InstanceWorld, ActionError> {
|
||||
InstanceWorld::new(&mut self.code, isa, &module.into_vec(), &mut self.spectest)
|
||||
}
|
||||
|
||||
pub fn define_unnamed_module(
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
module: ModuleBinary,
|
||||
) -> Result<(), String> {
|
||||
self.current = Some(self.instantiate(isa, module)?);
|
||||
Ok(())
|
||||
fn get_world(&mut self, module: &Option<String>) -> Result<WorldIndex, WastError> {
|
||||
let index = *if let Some(name) = module {
|
||||
self.namespace.get_mut(name).ok_or_else(|| {
|
||||
WastError::Module(UnknownModule {
|
||||
module: Some(name.to_owned()),
|
||||
})
|
||||
})
|
||||
} else {
|
||||
self.current
|
||||
.as_mut()
|
||||
.ok_or_else(|| WastError::Module(UnknownModule { module: None }))
|
||||
}?;
|
||||
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
pub fn define_named_module(
|
||||
/// Define a module and register it.
|
||||
pub fn module(
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
name: String,
|
||||
name: Option<String>,
|
||||
module: ModuleBinary,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), ActionError> {
|
||||
let world = self.instantiate(isa, module)?;
|
||||
self.namespace.insert(name, world);
|
||||
let index = if let Some(name) = name {
|
||||
self.register(name, world)
|
||||
} else {
|
||||
self.worlds.push(world)
|
||||
};
|
||||
self.current = Some(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn perform_action(
|
||||
/// Register a module to make it available for performing actions.
|
||||
pub fn register(&mut self, name: String, world: InstanceWorld) -> WorldIndex {
|
||||
let index = self.worlds.push(world);
|
||||
self.namespace.insert(name, index);
|
||||
index
|
||||
}
|
||||
|
||||
/// Invoke an exported function from a defined module.
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
module: Option<String>,
|
||||
field: &str,
|
||||
args: &[Value],
|
||||
) -> Result<ActionOutcome, WastError> {
|
||||
let mut value_args = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
value_args.push(runtime_value(*arg));
|
||||
}
|
||||
let index = self.get_world(&module)?;
|
||||
self.worlds[index]
|
||||
.invoke(&mut self.code, isa, &field, &value_args)
|
||||
.map_err(WastError::Action)
|
||||
}
|
||||
|
||||
/// Get the value of an exported global from a defined module.
|
||||
pub fn get(&mut self, module: Option<String>, field: &str) -> Result<RuntimeValue, WastError> {
|
||||
let index = self.get_world(&module)?;
|
||||
self.worlds[index].get(&field).map_err(WastError::Action)
|
||||
}
|
||||
|
||||
fn perform_action(
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
action: Action,
|
||||
) -> Result<ActionOutcome, String> {
|
||||
) -> Result<ActionOutcome, WastError> {
|
||||
match action {
|
||||
Action::Invoke {
|
||||
module,
|
||||
field,
|
||||
args,
|
||||
} => {
|
||||
let mut value_args = Vec::with_capacity(args.len());
|
||||
for a in args {
|
||||
value_args.push(match a {
|
||||
script::Value::I32(i) => Value::I32(i),
|
||||
script::Value::I64(i) => Value::I64(i),
|
||||
script::Value::F32(i) => Value::F32(i.to_bits()),
|
||||
script::Value::F64(i) => Value::F64(i.to_bits()),
|
||||
});
|
||||
}
|
||||
match module {
|
||||
None => match self.current {
|
||||
None => Err("invoke performed with no module present".to_string()),
|
||||
Some(ref mut instance_world) => instance_world
|
||||
.invoke(&mut self.code, isa, &field, &value_args)
|
||||
.map_err(|e| {
|
||||
format!("error invoking {} in current module: {}", field, e)
|
||||
}),
|
||||
},
|
||||
Some(name) => self
|
||||
.namespace
|
||||
.get_mut(&name)
|
||||
.ok_or_else(|| format!("module {} not declared", name))?
|
||||
.invoke(&mut self.code, isa, &field, &value_args)
|
||||
.map_err(|e| format!("error invoking {} in module {}: {}", field, name, e)),
|
||||
}
|
||||
}
|
||||
} => self.invoke(isa, module, &field, &args),
|
||||
Action::Get { module, field } => {
|
||||
let value = match module {
|
||||
None => match self.current {
|
||||
None => return Err("get performed with no module present".to_string()),
|
||||
Some(ref mut instance_world) => {
|
||||
instance_world.get(&field).map_err(|e| {
|
||||
format!("error getting {} in current module: {}", field, e)
|
||||
})?
|
||||
}
|
||||
},
|
||||
Some(name) => self
|
||||
.namespace
|
||||
.get_mut(&name)
|
||||
.ok_or_else(|| format!("module {} not declared", name))?
|
||||
.get(&field)
|
||||
.map_err(|e| {
|
||||
format!("error getting {} in module {}: {}", field, name, e)
|
||||
})?,
|
||||
};
|
||||
let value = self.get(module, &field)?;
|
||||
Ok(ActionOutcome::Returned {
|
||||
values: vec![value],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a wast script from a byte buffer.
|
||||
pub fn wast_buffer(name: &str, isa: &isa::TargetIsa, wast: &[u8]) -> Result<(), String> {
|
||||
let mut parser = ScriptParser::from_str(str::from_utf8(wast).unwrap()).unwrap();
|
||||
let mut instances = Instances::new();
|
||||
/// Run a wast script from a byte buffer.
|
||||
pub fn run_buffer(
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
filename: &str,
|
||||
wast: &[u8],
|
||||
) -> Result<(), WastFileError> {
|
||||
let mut parser = ScriptParser::from_str(str::from_utf8(wast).unwrap()).unwrap();
|
||||
|
||||
while let Some(Command { kind, line }) = parser.next().unwrap() {
|
||||
match kind {
|
||||
CommandKind::Module { module, name } => {
|
||||
if let Some(name) = name {
|
||||
instances.define_named_module(isa, name, module.clone())?;
|
||||
while let Some(Command { kind, line }) = parser.next().unwrap() {
|
||||
match kind {
|
||||
CommandKind::Module { module, name } => {
|
||||
self.module(isa, name, module)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Action(error),
|
||||
})?;
|
||||
}
|
||||
|
||||
instances.define_unnamed_module(isa, module)?;
|
||||
}
|
||||
CommandKind::Register {
|
||||
name: _name,
|
||||
as_name: _as_name,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement register", name, line);
|
||||
}
|
||||
CommandKind::PerformAction(action) => match instances.perform_action(isa, action)? {
|
||||
ActionOutcome::Returned { .. } => {}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
panic!("{}:{}: a trap occurred: {}", name, line, message);
|
||||
CommandKind::Register {
|
||||
name: _name,
|
||||
as_name: _as_name,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement register", filename, line);
|
||||
}
|
||||
},
|
||||
CommandKind::AssertReturn { action, expected } => {
|
||||
match instances.perform_action(isa, action)? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
for (v, e) in values.iter().zip(expected.iter()) {
|
||||
match *e {
|
||||
script::Value::I32(x) => {
|
||||
assert_eq!(x, v.unwrap_i32(), "at {}:{}", name, line)
|
||||
CommandKind::PerformAction(action) => match self
|
||||
.perform_action(isa, action)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})? {
|
||||
ActionOutcome::Returned { .. } => {}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Trap(message),
|
||||
});
|
||||
}
|
||||
},
|
||||
CommandKind::AssertReturn { action, expected } => {
|
||||
match self
|
||||
.perform_action(isa, action)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
for (v, e) in values
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(expected.iter().cloned().map(runtime_value))
|
||||
{
|
||||
if v != e {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected {}, got {}",
|
||||
e, v
|
||||
)),
|
||||
});
|
||||
}
|
||||
script::Value::I64(x) => {
|
||||
assert_eq!(x, v.unwrap_i64(), "at {}:{}", name, line)
|
||||
}
|
||||
script::Value::F32(x) => {
|
||||
assert_eq!(x.to_bits(), v.unwrap_f32(), "at {}:{}", name, line)
|
||||
}
|
||||
script::Value::F64(x) => {
|
||||
assert_eq!(x.to_bits(), v.unwrap_f64(), "at {}:{}", name, line)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!("unexpected trap: {}", message)),
|
||||
});
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
panic!(
|
||||
"{}:{}: expected normal return, but a trap occurred: {}",
|
||||
name, line, message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, message } => {
|
||||
match instances.perform_action(isa, action)? {
|
||||
ActionOutcome::Returned { values } => panic!(
|
||||
"{}:{}: expected trap, but invoke returned with {:?}",
|
||||
name, line, values
|
||||
),
|
||||
ActionOutcome::Trapped {
|
||||
message: trap_message,
|
||||
} => {
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_trap message: expected {}, got {}",
|
||||
name, line, message, trap_message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertExhaustion { action } => {
|
||||
match instances.perform_action(isa, action)? {
|
||||
ActionOutcome::Returned { values } => panic!(
|
||||
"{}:{}: expected exhaustion, but invoke returned with {:?}",
|
||||
name, line, values
|
||||
),
|
||||
ActionOutcome::Trapped { message } => {
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_exhaustion message: {}",
|
||||
name, line, message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertReturnCanonicalNan { action } => {
|
||||
match instances.perform_action(isa, action)? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
for v in values.iter() {
|
||||
match v {
|
||||
Value::I32(_) | Value::I64(_) => {
|
||||
panic!("unexpected integer type in NaN test");
|
||||
}
|
||||
Value::F32(x) => assert_eq!(
|
||||
x & 0x7fffffff,
|
||||
0x7fc00000,
|
||||
"expected canonical NaN at {}:{}",
|
||||
name,
|
||||
line
|
||||
),
|
||||
Value::F64(x) => assert_eq!(
|
||||
x & 0x7fffffffffffffff,
|
||||
0x7ff8000000000000,
|
||||
"expected canonical NaN at {}:{}",
|
||||
name,
|
||||
line
|
||||
),
|
||||
};
|
||||
CommandKind::AssertTrap { action, message } => {
|
||||
match self
|
||||
.perform_action(isa, action)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected trap, but invoke returned with {:?}",
|
||||
values
|
||||
)),
|
||||
});
|
||||
}
|
||||
ActionOutcome::Trapped {
|
||||
message: trap_message,
|
||||
} => {
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_trap message: expected {}, got {}",
|
||||
filename, line, message, trap_message
|
||||
);
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
panic!(
|
||||
"{}:{}: expected canonical NaN return, but a trap occurred: {}",
|
||||
name, line, message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertReturnArithmeticNan { action } => {
|
||||
match instances.perform_action(isa, action)? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
for v in values.iter() {
|
||||
match v {
|
||||
Value::I32(_) | Value::I64(_) => {
|
||||
panic!("unexpected integer type in NaN test");
|
||||
}
|
||||
Value::F32(x) => assert_eq!(
|
||||
x & 0x00400000,
|
||||
0x00400000,
|
||||
"expected arithmetic NaN at {}:{}",
|
||||
name,
|
||||
line
|
||||
),
|
||||
Value::F64(x) => assert_eq!(
|
||||
x & 0x0008000000000000,
|
||||
0x0008000000000000,
|
||||
"expected arithmetic NaN at {}:{}",
|
||||
name,
|
||||
line
|
||||
),
|
||||
};
|
||||
CommandKind::AssertExhaustion { action } => {
|
||||
match self
|
||||
.perform_action(isa, action)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected callstack exhaustion, but invoke returned with {:?}",
|
||||
values
|
||||
)),
|
||||
});
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_exhaustion message: {}",
|
||||
filename, line, message
|
||||
);
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
panic!(
|
||||
"{}:{}: expected canonical NaN return, but a trap occurred: {}",
|
||||
name, line, message
|
||||
);
|
||||
}
|
||||
CommandKind::AssertReturnCanonicalNan { action } => {
|
||||
match self
|
||||
.perform_action(isa, action)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
for v in values.iter() {
|
||||
match v {
|
||||
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Type(format!(
|
||||
"unexpected integer type in NaN test"
|
||||
)),
|
||||
});
|
||||
}
|
||||
RuntimeValue::F32(x) => {
|
||||
if (x & 0x7fffffff) != 0x7fc00000 {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected canonical NaN"
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
RuntimeValue::F64(x) => {
|
||||
if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected canonical NaN"
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!("unexpected trap: {}", message)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertInvalid {
|
||||
module: _module,
|
||||
message: _message,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement assert_invalid", name, line);
|
||||
}
|
||||
CommandKind::AssertMalformed {
|
||||
module: _module,
|
||||
message: _message,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement assert_malformed", name, line);
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, message } => {
|
||||
let _err = instances
|
||||
.define_unnamed_module(isa, module)
|
||||
.expect_err(&format!(
|
||||
CommandKind::AssertReturnArithmeticNan { action } => {
|
||||
match self
|
||||
.perform_action(isa, action)
|
||||
.map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})? {
|
||||
ActionOutcome::Returned { values } => {
|
||||
for v in values.iter() {
|
||||
match v {
|
||||
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Type(format!(
|
||||
"unexpected integer type in NaN test",
|
||||
)),
|
||||
});
|
||||
}
|
||||
RuntimeValue::F32(x) => {
|
||||
if (x & 0x00400000) != 0x00400000 {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected arithmetic NaN"
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
RuntimeValue::F64(x) => {
|
||||
if (x & 0x0008000000000000) != 0x0008000000000000 {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!(
|
||||
"expected arithmetic NaN"
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
return Err(WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error: WastError::Assert(format!("unexpected trap: {}", message)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertInvalid {
|
||||
module: _module,
|
||||
message: _message,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement assert_invalid", filename, line);
|
||||
}
|
||||
CommandKind::AssertMalformed {
|
||||
module: _module,
|
||||
message: _message,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement assert_malformed", filename, line);
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, message } => {
|
||||
let _err = self.module(isa, None, module).expect_err(&format!(
|
||||
"{}:{}: uninstantiable module was successfully instantiated",
|
||||
name, line
|
||||
filename, line
|
||||
));
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_uninstantiable message: {}",
|
||||
name, line, message
|
||||
);
|
||||
}
|
||||
CommandKind::AssertUnlinkable { module, message } => {
|
||||
let _err = instances
|
||||
.define_unnamed_module(isa, module)
|
||||
.expect_err(&format!(
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_uninstantiable message: {}",
|
||||
filename, line, message
|
||||
);
|
||||
}
|
||||
CommandKind::AssertUnlinkable { module, message } => {
|
||||
let _err = self.module(isa, None, module).expect_err(&format!(
|
||||
"{}:{}: unlinkable module was successfully linked",
|
||||
name, line
|
||||
filename, line
|
||||
));
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_unlinkable message: {}",
|
||||
name, line, message
|
||||
);
|
||||
println!(
|
||||
"{}:{}: TODO: Check the assert_unlinkable message: {}",
|
||||
filename, line, message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run a wast script from a file.
|
||||
pub fn wast_file(path: &Path, isa: &isa::TargetIsa) -> Result<(), String> {
|
||||
let wast = read_to_end(path).map_err(|e| e.to_string())?;
|
||||
wast_buffer(&path.display().to_string(), isa, &wast)
|
||||
/// Run a wast script from a file.
|
||||
pub fn run_file(&mut self, isa: &isa::TargetIsa, path: &Path) -> Result<(), WastFileError> {
|
||||
let filename = path.display().to_string();
|
||||
let buffer = read_to_end(path).map_err(|e| WastFileError {
|
||||
filename,
|
||||
line: 0,
|
||||
error: WastError::IO(e),
|
||||
})?;
|
||||
self.run_buffer(isa, &path.display().to_string(), &buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_end(path: &Path) -> Result<Vec<u8>, io::Error> {
|
||||
|
||||
Reference in New Issue
Block a user