Switch from wabt crate to wast (#434)

* Switch lightbeam from `wabt` to `wast`

Switch from a C++-based `*.wat` parser to a Rust-based parser

* Remove unneeded `wabt` dev-dependency from wasmtime-api

* Rewrite `wasmtime-wast` crate with `wast-parser`

This commit moves the `wasmtime-wast` crate off the `wabt` crate on to
the `wast-parser` crate which is a Rust implementation of a `*.wast` and
`*.wat` parser. The intention here is to continue to reduce the amount
of C++ required to build wasmtime!

* Use new `wat` and `wast` crate names
This commit is contained in:
Alex Crichton
2019-10-18 15:25:48 -05:00
committed by Dan Gohman
parent ebef2c6b57
commit 9947bc5209
10 changed files with 236 additions and 456 deletions

View File

@@ -33,7 +33,7 @@ failure = "0.1"
target-lexicon = { version = "0.8.1", default-features = false }
pretty_env_logger = "0.3.0"
file-per-thread-logger = "0.1.1"
wabt = "0.9.2"
wat = "1.0.2"
libc = "0.2.60"
rayon = "1.1"
wasm-webidl-bindings = "0.5"

View File

@@ -26,7 +26,7 @@ typemap = "0.3"
[dev-dependencies]
lazy_static = "1.2"
wabt = "0.9.2"
wat = "1.0.2"
quickcheck = "0.9.0"
[badges]

View File

@@ -20,8 +20,6 @@ extern crate lazy_static;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[cfg(test)]
extern crate wabt;
// Just so we can implement `Signature` for `cranelift_codegen::ir::Signature`
extern crate cranelift_codegen;
extern crate multi_mut;

View File

@@ -1,8 +1,7 @@
use super::{module::ExecutionError, translate, ExecutableModule};
use wabt;
fn translate_wat(wat: &str) -> ExecutableModule {
let wasm = wabt::wat2wasm(wat).unwrap();
let wasm = wat::parse_str(wat).unwrap();
let compiled = translate(&wasm).unwrap();
compiled
}

View File

@@ -40,9 +40,8 @@ use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::File;
use std::path::Component;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::process::exit;
use wabt;
use wasi_common::preopen_dir;
use wasmtime::pick_compilation_strategy;
use wasmtime_api::{Config, Engine, HostRef, Instance, Module, Store};
@@ -115,19 +114,6 @@ struct Args {
flag_wasi_c: bool,
}
fn read_wasm(path: PathBuf) -> Result<Vec<u8>, Error> {
let data = std::fs::read(&path)
.with_context(|_| format!("failed to read file: {}", path.display()))?;
// If data is a wasm binary, use that. If it's using wat format, convert it
// to a wasm binary with wat2wasm.
Ok(if data.starts_with(&[b'\0', b'a', b's', b'm']) {
data
} else {
wabt::wat2wasm(data)?
})
}
fn compute_preopen_dirs(flag_dir: &[String], flag_mapdir: &[String]) -> Vec<(String, File)> {
let mut preopen_dirs = Vec::new();
@@ -353,8 +339,8 @@ fn instantiate_module(
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
path: &Path,
) -> Result<(HostRef<Instance>, HostRef<Module>, Vec<u8>), Error> {
// Read the wasm module binary.
let data = read_wasm(path.to_path_buf())?;
// Read the wasm module binary either as `*.wat` or a raw binary
let data = wat::parse_file(path.to_path_buf())?;
let module = HostRef::new(Module::new(store.clone(), &data)?);

View File

@@ -5,32 +5,16 @@ use core::cell::RefCell;
use cranelift_codegen::settings;
use cranelift_codegen::settings::Configurable;
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::PathBuf;
use wabt;
use wasmtime_jit::{instantiate, CompilationStrategy, Compiler, NullResolver};
#[cfg(test)]
const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat";
#[cfg(test)]
fn read_to_end(path: PathBuf) -> Result<Vec<u8>, io::Error> {
let mut buf: Vec<u8> = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut buf)?;
Ok(buf)
}
/// Simple test reading a wasm-file and translating to binary representation.
#[test]
fn test_environ_translate() {
let path = PathBuf::from(PATH_MODULE_RS2WASM_ADD_FUNC);
let wat_data = read_to_end(path).unwrap();
assert!(wat_data.len() > 0);
let data = wabt::wat2wasm(wat_data).expect("expecting valid wat-file");
let data = wat::parse_file(path).expect("expecting valid wat-file");
assert!(data.len() > 0);
let mut flag_builder = settings::builder();

View File

@@ -39,7 +39,6 @@ wasi-common = { git = "https://github.com/CraneStation/wasi-common", rev = "c3bf
docopt = "1.0.1"
serde = { "version" = "1.0.94", features = ["derive"] }
pretty_env_logger = "0.3.0"
wabt = "0.9.2"
wasmtime-wast = { path="../wasmtime-wast" }
wasmtime-wasi = { path="../wasmtime-wasi" }
rayon = "1.1"

View File

@@ -17,10 +17,9 @@ cranelift-wasm = { version = "0.46.1", features = ["enable-serde"] }
wasmtime-jit = { path = "../wasmtime-jit" }
wasmtime-runtime = { path = "../wasmtime-runtime" }
wasmtime-environ = { path = "../wasmtime-environ" }
wabt = "0.9.2"
wast = "2.0.0"
target-lexicon = "0.8.1"
failure = { version = "0.1.3", default-features = false }
failure_derive = { version = "0.1.3", default-features = false }
[badges]
maintenance = { status = "experimental" }

View File

@@ -23,14 +23,12 @@
)]
extern crate alloc;
#[macro_use]
extern crate failure_derive;
mod spectest;
mod wast;
pub use crate::spectest::instantiate_spectest;
pub use crate::wast::{WastContext, WastError};
pub use crate::wast::WastContext;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -1,73 +1,28 @@
use crate::spectest::instantiate_spectest;
use std::io::Read;
use failure::{bail, Error, ResultExt};
use std::path::Path;
use std::{fmt, fs, io, str};
use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value};
use wabt::Features as WabtFeatures;
use std::str;
use wasmtime_jit::{
ActionError, ActionOutcome, Compiler, Context, Features, InstanceHandle, InstantiationError,
RuntimeValue, UnknownInstance,
RuntimeValue, SetupError,
};
/// 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()),
Value::V128(x) => RuntimeValue::V128(x.to_le_bytes()),
fn runtime_value(v: &wast::Expression<'_>) -> RuntimeValue {
use wast::Instruction::*;
if v.instrs.len() != 1 {
panic!("too many instructions in {:?}", v);
}
}
/// Error message used by `WastContext`.
#[derive(Fail, Debug)]
pub enum WastError {
/// An assert command was not satisfied.
Assert(String),
/// An unknown instance name was used.
Instance(UnknownInstance),
/// No default instance has been registered yet.
NoDefaultInstance,
/// 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 a syntax error while parsing the wast script.
Syntax(wabt::script::Error),
/// The was a character encoding error while parsing the wast script.
Utf8(str::Utf8Error),
/// 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::Instance(ref error) => error.fmt(f),
WastError::NoDefaultInstance => write!(f, "no default instance defined yet"),
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::Syntax(ref message) => write!(f, "syntax error: {}", message),
WastError::Utf8(ref message) => write!(f, "UTF-8 decoding error: {}", message),
WastError::IO(ref error) => write!(f, "I/O error: {}", error),
}
match &v.instrs[0] {
I32Const(x) => RuntimeValue::I32(*x),
I64Const(x) => RuntimeValue::I64(*x),
F32Const(x) => RuntimeValue::F32(x.bits),
F64Const(x) => RuntimeValue::F64(x.bits),
other => panic!("couldn't convert {:?} to a runtime value", other),
}
}
/// 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,
}
/// The wast test script language allows modules to be defined and actions
/// to be performed on them.
pub struct WastContext {
@@ -95,429 +50,321 @@ impl WastContext {
}
}
fn get_instance(
&mut self,
instance_name: Option<&str>,
) -> Result<&mut InstanceHandle, WastError> {
fn get_instance(&mut self, instance_name: Option<&str>) -> Result<&mut InstanceHandle, Error> {
let instance = if let Some(instance_name) = instance_name {
self.context
.get_instance(instance_name)
.map_err(WastError::Instance)
.context("failed to fetch instance")?
} else {
self.current
.as_mut()
.ok_or_else(|| WastError::NoDefaultInstance)
}?;
.ok_or_else(|| failure::format_err!("no current instance"))?
};
Ok(instance)
}
/// Register "spectest" which is used by the spec testsuite.
pub fn register_spectest(&mut self) -> Result<(), InstantiationError> {
pub fn register_spectest(&mut self) -> Result<(), Error> {
let instance = instantiate_spectest()?;
self.context.name_instance("spectest".to_owned(), instance);
Ok(())
}
/// Perform the action portion of a command.
fn perform_action(&mut self, action: Action) -> Result<ActionOutcome, WastError> {
match action {
Action::Invoke {
module: instance_name,
field,
args,
} => self.invoke(instance_name, &field, &args),
Action::Get {
module: instance_name,
field,
} => self.get(instance_name, &field),
fn perform_execute(&mut self, exec: wast::WastExecute<'_>) -> Result<ActionOutcome, Error> {
match exec {
wast::WastExecute::Invoke(invoke) => self.perform_invoke(invoke),
wast::WastExecute::Module(mut module) => {
let binary = module.encode()?;
let result = self.context.instantiate_module(None, &binary);
match result {
Ok(_) => Ok(ActionOutcome::Returned { values: Vec::new() }),
Err(ActionError::Setup(SetupError::Instantiate(
InstantiationError::StartTrap(message),
))) => Ok(ActionOutcome::Trapped { message }),
Err(e) => Err(e.into()),
}
}
wast::WastExecute::Get { module, global } => self.get(module.map(|s| s.name()), global),
}
}
fn perform_invoke(&mut self, exec: wast::WastInvoke<'_>) -> Result<ActionOutcome, Error> {
self.invoke(exec.module.map(|i| i.name()), exec.name, &exec.args)
}
/// Define a module and register it.
fn module(
&mut self,
instance_name: Option<String>,
module: ModuleBinary,
) -> Result<(), ActionError> {
fn module(&mut self, instance_name: Option<&str>, module: &[u8]) -> Result<(), Error> {
let index = self
.context
.instantiate_module(instance_name, &module.into_vec())?;
.instantiate_module(instance_name.map(|s| s.to_string()), module)?;
self.current = Some(index);
Ok(())
}
/// Register an instance to make it available for performing actions.
fn register(&mut self, name: Option<String>, as_name: String) -> Result<(), WastError> {
let instance = self.get_instance(name.as_ref().map(|x| &**x))?.clone();
self.context.name_instance(as_name, instance);
fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<(), Error> {
let instance = self.get_instance(name)?.clone();
self.context.name_instance(as_name.to_string(), instance);
Ok(())
}
/// Invoke an exported function from an instance.
fn invoke(
&mut self,
instance_name: Option<String>,
instance_name: Option<&str>,
field: &str,
args: &[Value],
) -> Result<ActionOutcome, WastError> {
let value_args = args
.iter()
.map(|arg| runtime_value(*arg))
.collect::<Vec<_>>();
let mut instance = self
.get_instance(instance_name.as_ref().map(|x| &**x))?
.clone();
self.context
args: &[wast::Expression],
) -> Result<ActionOutcome, Error> {
let value_args = args.iter().map(runtime_value).collect::<Vec<_>>();
let mut instance = self.get_instance(instance_name)?.clone();
let result = self
.context
.invoke(&mut instance, field, &value_args)
.map_err(WastError::Action)
.with_context(|_| format!("failed to invoke `{}`", field))?;
Ok(result)
}
/// Get the value of an exported global from an instance.
fn get(
&mut self,
instance_name: Option<String>,
field: &str,
) -> Result<ActionOutcome, WastError> {
fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<ActionOutcome, Error> {
let instance = self
.get_instance(instance_name.as_ref().map(|x| &**x))?
.clone();
self.context
let result = self
.context
.get(&instance, field)
.map_err(WastError::Action)
}
/// Perform the action of a `PerformAction`.
fn perform_action_command(&mut self, action: Action) -> Result<(), WastError> {
match self.perform_action(action)? {
ActionOutcome::Returned { .. } => Ok(()),
ActionOutcome::Trapped { message } => Err(WastError::Trap(message)),
}
.with_context(|_| format!("failed to get field `{}`", field))?;
Ok(result)
}
/// Run a wast script from a byte buffer.
pub fn run_buffer(&mut self, filename: &str, wast: &[u8]) -> Result<(), WastFileError> {
let features: WabtFeatures = convert_features(self.context.features());
pub fn run_buffer(&mut self, filename: &str, wast: &[u8]) -> Result<(), Error> {
use wast::WastDirective::*;
// Work around https://github.com/pepyakin/wabt-rs/issues/59
let test_filename = Path::new(filename)
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_owned();
let wast = str::from_utf8(wast)?;
let mut parser = ScriptParser::from_source_and_name_with_features(
str::from_utf8(wast)
.map_err(|error| WastFileError {
filename: filename.to_string(),
line: 0,
error: WastError::Utf8(error),
})?
.as_bytes(),
&test_filename,
features,
)
.map_err(|error| WastFileError {
filename: filename.to_string(),
line: 0,
error: WastError::Syntax(error),
})?;
let adjust_wast = |mut err: wast::Error| {
err.set_path(filename.as_ref());
err.set_text(wast);
err
};
let context = |sp: wast::Span| {
let (line, col) = sp.linecol_in(wast);
format!("for directive on {}:{}:{}", filename, line, col)
};
while let Some(Command { kind, line }) = parser.next().expect("parser") {
match kind {
CommandKind::Module {
module: instance_name,
name,
} => {
self.module(name, instance_name)
.map_err(|error| WastFileError {
filename: filename.to_string(),
line,
error: WastError::Action(error),
})?;
let buf = wast::parser::ParseBuffer::new(wast).map_err(adjust_wast)?;
let wast = wast::parser::parse::<wast::Wast>(&buf).map_err(adjust_wast)?;
for directive in wast.directives {
match directive {
Module(mut module) => {
let binary = module.encode().map_err(adjust_wast)?;
self.module(module.name.map(|s| s.name()), &binary)
.with_context(|_| context(module.span))?;
}
CommandKind::Register { name, as_name } => {
self.register(name, as_name)
.map_err(|error| WastFileError {
filename: filename.to_string(),
line,
error,
})?;
Register { span, name, module } => {
self.register(module.map(|s| s.name()), name)
.with_context(|_| context(span))?;
}
CommandKind::PerformAction(action) => {
self.perform_action_command(action)
.map_err(|error| WastFileError {
filename: filename.to_string(),
line,
error,
})?;
Invoke(i) => {
let span = i.span;
self.perform_invoke(i).with_context(|_| context(span))?;
}
CommandKind::AssertReturn { action, expected } => {
match self.perform_action(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
)),
});
}
AssertReturn {
span,
exec,
results,
} => match self.perform_execute(exec).with_context(|_| context(span))? {
ActionOutcome::Returned { values } => {
for (v, e) in values.iter().zip(results.iter().map(runtime_value)) {
if *v == e {
continue;
}
}
ActionOutcome::Trapped { message } => {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!("unexpected trap: {}", message)),
});
bail!("{}\nexpected {}, got {}", context(span), e, v)
}
}
}
CommandKind::AssertTrap { action, message } => {
match self.perform_action(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,
} => {
if !trap_message.contains(&message) {
#[cfg(feature = "lightbeam")]
println!(
"{}:{}: TODO: Check the assert_trap message: {}",
filename, line, message
);
#[cfg(not(feature = "lightbeam"))]
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!(
"expected {}, got {}",
message, trap_message
)),
});
}
}
ActionOutcome::Trapped { message } => {
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
CommandKind::AssertExhaustion { action, message } => {
match self.perform_action(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: trap_message,
} => {
if !trap_message.contains(&message) {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!(
"expected exhaustion with {}, got {}",
message, trap_message
)),
});
}
}
},
AssertTrap {
span,
exec,
message,
} => match self.perform_execute(exec).with_context(|_| context(span))? {
ActionOutcome::Returned { values } => {
bail!("{}\nexpected trap, got {:?}", context(span), values)
}
}
CommandKind::AssertReturnCanonicalNan { action } => {
match self.perform_action(action).map_err(|error| WastFileError {
filename: filename.to_string(),
line,
error,
})? {
ActionOutcome::Trapped {
message: trap_message,
} => {
if trap_message.contains(message) {
continue;
}
if cfg!(feature = "lightbeam") {
println!(
"{}\nTODO: Check the assert_trap message: {}",
context(span),
message
);
continue;
}
bail!(
"{}\nexpected {}, got {}",
context(span),
message,
trap_message
)
}
},
AssertExhaustion {
span,
call,
message,
} => match self.perform_invoke(call).with_context(|_| context(span))? {
ActionOutcome::Returned { values } => {
bail!("{}\nexpected trap, got {:?}", context(span), values)
}
ActionOutcome::Trapped {
message: trap_message,
} => {
if trap_message.contains(message) {
continue;
}
bail!(
"{}\nexpected exhaustion with {}, got {}",
context(span),
message,
trap_message
)
}
},
AssertReturnCanonicalNan { span, invoke } => {
match self
.perform_invoke(invoke)
.with_context(|_| context(span))?
{
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"
)),
});
bail!("{}\nunexpected integer in NaN test", context(span))
}
RuntimeValue::V128(_) => {
bail!("{}\nunexpected vector in NaN test", context(span))
}
RuntimeValue::F32(x) => {
if (x & 0x7fffffff) != 0x7fc00000 {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!(
"expected canonical NaN"
)),
});
bail!("{}\nexpected canonical NaN", context(span))
}
}
RuntimeValue::F64(x) => {
if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!(
"expected canonical NaN"
)),
});
bail!("{}\nexpected canonical NaN", context(span))
}
}
RuntimeValue::V128(_) => {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Type(format!(
"unexpected vector type in NaN test"
)),
});
}
};
}
}
ActionOutcome::Trapped { message } => {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!("unexpected trap: {}", message)),
});
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
}
CommandKind::AssertReturnArithmeticNan { action } => {
match self.perform_action(action).map_err(|error| WastFileError {
filename: filename.to_string(),
line,
error,
})? {
AssertReturnArithmeticNan { span, invoke } => {
match self
.perform_invoke(invoke)
.with_context(|_| context(span))?
{
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",
)),
});
bail!("{}\nunexpected integer in NaN test", context(span))
}
RuntimeValue::V128(_) => {
bail!("{}\nunexpected vector in NaN test", context(span))
}
RuntimeValue::F32(x) => {
if (x & 0x00400000) != 0x00400000 {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!(
"expected arithmetic NaN"
)),
});
bail!("{}\nexpected arithmetic NaN", context(span))
}
}
RuntimeValue::F64(x) => {
if (x & 0x0008000000000000) != 0x0008000000000000 {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!(
"expected arithmetic NaN"
)),
});
bail!("{}\nexpected arithmetic NaN", context(span))
}
}
RuntimeValue::V128(_) => {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Type(format!(
"unexpected vector type in NaN test",
)),
});
}
};
}
}
ActionOutcome::Trapped { message } => {
return Err(WastFileError {
filename: filename.to_string(),
line,
error: WastError::Assert(format!("unexpected trap: {}", message)),
});
bail!("{}\nunexpected trap: {}", context(span), message)
}
}
}
CommandKind::AssertInvalid { module, message } => {
self.module(None, module).expect_err(&format!(
"{}:{}: invalid module was successfully instantiated",
filename, line
));
AssertInvalid {
span,
mut module,
message,
} => {
let bytes = module.encode().map_err(adjust_wast)?;
let err = match self.module(None, &bytes) {
Ok(()) => bail!("{}\nexpected module to fail to build", context(span)),
Err(e) => e,
};
println!(
"{}:{}: TODO: Check the assert_invalid message: {}",
filename, line, message
"{}\nTODO: Check the assert_invalid message: {}",
context(span),
message
);
drop(err);
}
CommandKind::AssertMalformed { module, message } => {
self.module(None, module).expect_err(&format!(
"{}:{}: malformed module was successfully instantiated",
filename, line
));
AssertMalformed {
span,
module,
message,
} => {
let mut module = match module {
wast::QuoteModule::Module(m) => m,
// this is a `*.wat` parser test which we're not
// interested in
wast::QuoteModule::Quote(_) => return Ok(()),
};
let bytes = module.encode().map_err(adjust_wast)?;
let err = match self.module(None, &bytes) {
Ok(()) => {
bail!("{}\nexpected module to fail to instantiate", context(span))
}
Err(e) => e,
};
println!(
"{}:{}: TODO: Check the assert_malformed message: {}",
filename, line, message
"{}\nTODO: Check the assert_malformed message: {}",
context(span),
message
);
drop(err);
}
CommandKind::AssertUninstantiable { module, message } => {
let _err = self.module(None, module).expect_err(&format!(
"{}:{}: uninstantiable module was successfully instantiated",
filename, line
));
AssertUnlinkable {
span,
mut module,
message,
} => {
let bytes = module.encode().map_err(adjust_wast)?;
let err = match self.module(None, &bytes) {
Ok(()) => bail!("{}\nexpected module to fail to link", context(span)),
Err(e) => e,
};
println!(
"{}:{}: TODO: Check the assert_uninstantiable message: {}",
filename, line, message
);
}
CommandKind::AssertUnlinkable { module, message } => {
let _err = self.module(None, module).expect_err(&format!(
"{}:{}: unlinkable module was successfully linked",
filename, line
));
println!(
"{}:{}: TODO: Check the assert_unlinkable message: {}",
filename, line, message
"{}\nTODO: Check the assert_unlinkable message: {}",
context(span),
message
);
drop(err);
}
AssertReturnFunc { .. } => panic!("need to implement assert_return_func"),
}
}
@@ -525,39 +372,9 @@ impl WastContext {
}
/// Run a wast script from a file.
pub fn run_file(&mut self, 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(&path.display().to_string(), &buffer)
pub fn run_file(&mut self, path: &Path) -> Result<(), Error> {
let bytes =
std::fs::read(path).with_context(|_| format!("failed to read `{}`", path.display()))?;
self.run_buffer(path.to_str().unwrap(), &bytes)
}
}
fn read_to_end(path: &Path) -> Result<Vec<u8>, io::Error> {
let mut buf: Vec<u8> = Vec::new();
let mut file = fs::File::open(path)?;
file.read_to_end(&mut buf)?;
Ok(buf)
}
/// Helper to convert wasmtime features to WABT features; would be nicer as Into<WabtFeatures> but
/// wasmtime-jit does not have a wabt dependency
fn convert_features(features: &Features) -> WabtFeatures {
let mut wabt_features = WabtFeatures::new();
if features.simd {
wabt_features.enable_simd()
}
if features.multi_value {
wabt_features.enable_multi_value()
}
if features.bulk_memory {
wabt_features.enable_bulk_memory()
}
if features.threads {
wabt_features.enable_threads()
}
wabt_features
}