Initial reorg.
This is largely the same as #305, but updated for the current tree.
This commit is contained in:
34
crates/wast/src/lib.rs
Normal file
34
crates/wast/src/lib.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
//! Implementation of the WAST text format for wasmtime.
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![deny(unstable_features)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
allow(clippy::new_without_default, clippy::new_without_default_derive)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
warn(
|
||||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod spectest;
|
||||
mod wast;
|
||||
|
||||
pub use crate::spectest::instantiate_spectest;
|
||||
pub use crate::wast::WastContext;
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
229
crates/wast/src/spectest.rs
Normal file
229
crates/wast/src/spectest.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_codegen::{ir, isa};
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType};
|
||||
use std::collections::hash_map::HashMap;
|
||||
use target_lexicon::HOST;
|
||||
use wasmtime_environ::{translate_signature, Export, MemoryPlan, Module, TablePlan};
|
||||
use wasmtime_jit::target_tunables;
|
||||
use wasmtime_runtime::{Imports, InstanceHandle, InstantiationError, VMContext, VMFunctionBody};
|
||||
|
||||
extern "C" fn spectest_print() {}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
extern "C" fn spectest_print_i32(_vmctx: &mut VMContext, x: i32) {
|
||||
println!("{}: i32", x);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
extern "C" fn spectest_print_i64(_vmctx: &mut VMContext, x: i64) {
|
||||
println!("{}: i64", x);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
extern "C" fn spectest_print_f32(_vmctx: &mut VMContext, x: f32) {
|
||||
println!("{}: f32", x);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
extern "C" fn spectest_print_f64(_vmctx: &mut VMContext, x: f64) {
|
||||
println!("{}: f64", x);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
extern "C" fn spectest_print_i32_f32(_vmctx: &mut VMContext, x: i32, y: f32) {
|
||||
println!("{}: i32", x);
|
||||
println!("{}: f32", y);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
extern "C" fn spectest_print_f64_f64(_vmctx: &mut VMContext, x: f64, y: f64) {
|
||||
println!("{}: f64", x);
|
||||
println!("{}: f64", y);
|
||||
}
|
||||
|
||||
/// Return an instance implementing the "spectest" interface used in the
|
||||
/// spec testsuite.
|
||||
pub fn instantiate_spectest() -> Result<InstanceHandle, InstantiationError> {
|
||||
let call_conv = isa::CallConv::triple_default(&HOST);
|
||||
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||
let mut module = Module::new();
|
||||
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =
|
||||
PrimaryMap::new();
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_i32".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_i32 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_i64".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_i64 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_f32".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_f32 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_f64".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_f64 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32), ir::AbiParam::new(types::F32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_i32_f32".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_i32_f32 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64), ir::AbiParam::new(types::F64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_f64_f64".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_f64_f64 as *const VMFunctionBody);
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I32Const(666),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_i32".to_owned(), Export::Global(global));
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::I64,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I64Const(666),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_i64".to_owned(), Export::Global(global));
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::F32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F32Const(0x44268000),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_f32".to_owned(), Export::Global(global));
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::F64,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F64Const(0x4084d00000000000),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_f64".to_owned(), Export::Global(global));
|
||||
|
||||
let tunables = target_tunables(&HOST);
|
||||
let table = module.table_plans.push(TablePlan::for_table(
|
||||
Table {
|
||||
ty: TableElementType::Func,
|
||||
minimum: 10,
|
||||
maximum: Some(20),
|
||||
},
|
||||
&tunables,
|
||||
));
|
||||
module
|
||||
.exports
|
||||
.insert("table".to_owned(), Export::Table(table));
|
||||
|
||||
let memory = module.memory_plans.push(MemoryPlan::for_memory(
|
||||
Memory {
|
||||
minimum: 1,
|
||||
maximum: Some(2),
|
||||
shared: false,
|
||||
},
|
||||
&tunables,
|
||||
));
|
||||
module
|
||||
.exports
|
||||
.insert("memory".to_owned(), Export::Memory(memory));
|
||||
|
||||
let imports = Imports::none();
|
||||
let data_initializers = Vec::new();
|
||||
let signatures = PrimaryMap::new();
|
||||
|
||||
InstanceHandle::new(
|
||||
Rc::new(module),
|
||||
Rc::new(RefCell::new(HashMap::new())),
|
||||
finished_functions.into_boxed_slice(),
|
||||
imports,
|
||||
&data_initializers,
|
||||
signatures.into_boxed_slice(),
|
||||
None,
|
||||
Box::new(()),
|
||||
)
|
||||
}
|
||||
386
crates/wast/src/wast.rs
Normal file
386
crates/wast/src/wast.rs
Normal file
@@ -0,0 +1,386 @@
|
||||
use crate::spectest::instantiate_spectest;
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
use wasmtime_jit::{
|
||||
ActionError, ActionOutcome, Compiler, Context, Features, InstanceHandle, InstantiationError,
|
||||
RuntimeValue, SetupError,
|
||||
};
|
||||
|
||||
/// Translate from a `script::Value` to a `RuntimeValue`.
|
||||
fn runtime_value(v: &wast::Expression<'_>) -> RuntimeValue {
|
||||
use wast::Instruction::*;
|
||||
|
||||
if v.instrs.len() != 1 {
|
||||
panic!("too many instructions in {:?}", v);
|
||||
}
|
||||
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),
|
||||
V128Const(x) => RuntimeValue::V128(x.to_le_bytes()),
|
||||
other => panic!("couldn't convert {:?} to a runtime value", other),
|
||||
}
|
||||
}
|
||||
|
||||
/// The wast test script language allows modules to be defined and actions
|
||||
/// to be performed on them.
|
||||
pub struct WastContext {
|
||||
/// Wast files have a concept of a "current" module, which is the most
|
||||
/// recently defined.
|
||||
current: Option<InstanceHandle>,
|
||||
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl WastContext {
|
||||
/// Construct a new instance of `WastContext`.
|
||||
pub fn new(compiler: Box<Compiler>) -> Self {
|
||||
Self {
|
||||
current: None,
|
||||
context: Context::new(compiler),
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new instance with the given features using the current `Context`
|
||||
pub fn with_features(self, features: Features) -> Self {
|
||||
Self {
|
||||
context: self.context.with_features(features),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
fn get_instance(&mut self, instance_name: Option<&str>) -> Result<&mut InstanceHandle> {
|
||||
let instance = if let Some(instance_name) = instance_name {
|
||||
self.context
|
||||
.get_instance(instance_name)
|
||||
.context("failed to fetch instance")?
|
||||
} else {
|
||||
self.current
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow::format_err!("no current instance"))?
|
||||
};
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
/// Register "spectest" which is used by the spec testsuite.
|
||||
pub fn register_spectest(&mut self) -> Result<()> {
|
||||
let instance = instantiate_spectest()?;
|
||||
self.context.name_instance("spectest".to_owned(), instance);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the action portion of a command.
|
||||
fn perform_execute(&mut self, exec: wast::WastExecute<'_>) -> Result<ActionOutcome> {
|
||||
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> {
|
||||
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<&str>, module: &[u8]) -> Result<()> {
|
||||
let index = self
|
||||
.context
|
||||
.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<&str>, as_name: &str) -> Result<()> {
|
||||
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<&str>,
|
||||
field: &str,
|
||||
args: &[wast::Expression],
|
||||
) -> Result<ActionOutcome> {
|
||||
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)
|
||||
.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<&str>, field: &str) -> Result<ActionOutcome> {
|
||||
let instance = self
|
||||
.get_instance(instance_name.as_ref().map(|x| &**x))?
|
||||
.clone();
|
||||
let result = self
|
||||
.context
|
||||
.get(&instance, field)
|
||||
.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<()> {
|
||||
use wast::WastDirective::*;
|
||||
|
||||
let wast = str::from_utf8(wast)?;
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
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))?;
|
||||
}
|
||||
Register { span, name, module } => {
|
||||
self.register(module.map(|s| s.name()), name)
|
||||
.with_context(|| context(span))?;
|
||||
}
|
||||
Invoke(i) => {
|
||||
let span = i.span;
|
||||
self.perform_invoke(i).with_context(|| context(span))?;
|
||||
}
|
||||
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;
|
||||
}
|
||||
bail!("{}\nexpected {}, got {}", context(span), e, v)
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), message)
|
||||
}
|
||||
},
|
||||
AssertTrap {
|
||||
span,
|
||||
exec,
|
||||
message,
|
||||
} => match self.perform_execute(exec).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;
|
||||
}
|
||||
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(_) => {
|
||||
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 {
|
||||
bail!("{}\nexpected canonical NaN", context(span))
|
||||
}
|
||||
}
|
||||
RuntimeValue::F64(x) => {
|
||||
if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 {
|
||||
bail!("{}\nexpected canonical NaN", context(span))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), message)
|
||||
}
|
||||
}
|
||||
}
|
||||
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(_) => {
|
||||
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 {
|
||||
bail!("{}\nexpected arithmetic NaN", context(span))
|
||||
}
|
||||
}
|
||||
RuntimeValue::F64(x) => {
|
||||
if (x & 0x0008000000000000) != 0x0008000000000000 {
|
||||
bail!("{}\nexpected arithmetic NaN", context(span))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
ActionOutcome::Trapped { message } => {
|
||||
bail!("{}\nunexpected trap: {}", context(span), message)
|
||||
}
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
let error_message = err.to_string();
|
||||
if !error_message.contains(&message) {
|
||||
// TODO: change to bail!
|
||||
println!(
|
||||
"{}\nassert_invalid: expected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
let error_message = err.to_string();
|
||||
if !error_message.contains(&message) {
|
||||
// TODO: change to bail!
|
||||
println!(
|
||||
"{}\nassert_malformed: expected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
let error_message = err.to_string();
|
||||
if !error_message.contains(&message) {
|
||||
bail!(
|
||||
"{}\nassert_unlinkable: expected {}, got {}",
|
||||
context(span),
|
||||
message,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
}
|
||||
AssertReturnFunc { .. } => panic!("need to implement assert_return_func"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run a wast script from a file.
|
||||
pub fn run_file(&mut self, path: &Path) -> Result<()> {
|
||||
let bytes =
|
||||
std::fs::read(path).with_context(|| format!("failed to read `{}`", path.display()))?;
|
||||
self.run_buffer(path.to_str().unwrap(), &bytes)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user