Preserve signalling bit in NaNs (#87)
* Preserve signalling bit in NaNs * Fix warnings
This commit is contained in:
parent
b95e11c414
commit
3890dd379f
|
@ -14,13 +14,8 @@ exclude = [ "/res/*", "/tests/*", "/fuzz/*" ]
|
|||
parity-wasm = "0.27"
|
||||
byteorder = "1.0"
|
||||
memory_units = "0.3.0"
|
||||
nan-preserving-float = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "~0.2.2"
|
||||
assert_matches = "1.1"
|
||||
|
||||
[features]
|
||||
# 32-bit platforms are not supported and not tested. Use this flag if you really want to use
|
||||
# wasmi on these platforms.
|
||||
# See https://github.com/pepyakin/wasmi/issues/43
|
||||
opt-in-32bit = []
|
||||
wabt = "^0.2.3"
|
||||
|
|
|
@ -64,8 +64,8 @@ fn main() {
|
|||
function_type.params().iter().enumerate().map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
|
||||
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
|
||||
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i]))),
|
||||
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i]))),
|
||||
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()),
|
||||
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()),
|
||||
}).collect::<Vec<RuntimeValue>>()
|
||||
};
|
||||
|
||||
|
|
|
@ -105,13 +105,7 @@ extern crate assert_matches;
|
|||
extern crate parity_wasm;
|
||||
extern crate byteorder;
|
||||
extern crate memory_units as memory_units_crate;
|
||||
|
||||
#[cfg(all(not(feature = "opt-in-32bit"), target_pointer_width = "32"))]
|
||||
compile_error! {
|
||||
"32-bit targets are not supported at the moment.
|
||||
You can use 'opt-in-32bit' feature.
|
||||
See https://github.com/pepyakin/wasmi/issues/43"
|
||||
}
|
||||
extern crate nan_preserving_float;
|
||||
|
||||
use std::fmt;
|
||||
use std::error;
|
||||
|
|
|
@ -369,6 +369,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn alloc() {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let fixtures = &[
|
||||
(0, None, true),
|
||||
(0, Some(0), true),
|
||||
|
@ -381,6 +382,17 @@ mod tests {
|
|||
(65536, Some(0), false),
|
||||
(65536, None, true),
|
||||
];
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let fixtures = &[
|
||||
(0, None, true),
|
||||
(0, Some(0), true),
|
||||
(1, None, true),
|
||||
(1, Some(1), true),
|
||||
(0, Some(1), true),
|
||||
(1, Some(0), false),
|
||||
];
|
||||
|
||||
for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() {
|
||||
let initial: Pages = Pages(initial);
|
||||
let maximum: Option<Pages> = maybe_max.map(|m| Pages(m));
|
||||
|
|
|
@ -420,7 +420,7 @@ impl ModuleInstance {
|
|||
|
||||
// This check is not only for bailing out early, but also to check the case when
|
||||
// segment consist of 0 members.
|
||||
if offset_val as usize + element_segment.members().len() > table_inst.current_size() as usize {
|
||||
if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 {
|
||||
return Err(
|
||||
Error::Instantiation("elements segment does not fit".to_string())
|
||||
);
|
||||
|
|
133
src/runner.rs
133
src/runner.rs
|
@ -16,6 +16,7 @@ use host::Externals;
|
|||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
|
||||
use common::stack::StackWithLimit;
|
||||
use memory_units::Pages;
|
||||
use nan_preserving_float::{F32, F64};
|
||||
|
||||
/// Maximum number of entries in value stack.
|
||||
pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
|
||||
|
@ -196,8 +197,8 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
|||
|
||||
&Opcode::I32Load(align, offset) => self.run_load::<i32>(context, align, offset),
|
||||
&Opcode::I64Load(align, offset) => self.run_load::<i64>(context, align, offset),
|
||||
&Opcode::F32Load(align, offset) => self.run_load::<f32>(context, align, offset),
|
||||
&Opcode::F64Load(align, offset) => self.run_load::<f64>(context, align, offset),
|
||||
&Opcode::F32Load(align, offset) => self.run_load::<F32>(context, align, offset),
|
||||
&Opcode::F64Load(align, offset) => self.run_load::<F64>(context, align, offset),
|
||||
&Opcode::I32Load8S(align, offset) => self.run_load_extend::<i8, i32>(context, align, offset),
|
||||
&Opcode::I32Load8U(align, offset) => self.run_load_extend::<u8, i32>(context, align, offset),
|
||||
&Opcode::I32Load16S(align, offset) => self.run_load_extend::<i16, i32>(context, align, offset),
|
||||
|
@ -211,8 +212,8 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
|||
|
||||
&Opcode::I32Store(align, offset) => self.run_store::<i32>(context, align, offset),
|
||||
&Opcode::I64Store(align, offset) => self.run_store::<i64>(context, align, offset),
|
||||
&Opcode::F32Store(align, offset) => self.run_store::<f32>(context, align, offset),
|
||||
&Opcode::F64Store(align, offset) => self.run_store::<f64>(context, align, offset),
|
||||
&Opcode::F32Store(align, offset) => self.run_store::<F32>(context, align, offset),
|
||||
&Opcode::F64Store(align, offset) => self.run_store::<F64>(context, align, offset),
|
||||
&Opcode::I32Store8(align, offset) => self.run_store_wrap::<i32, i8>(context, align, offset),
|
||||
&Opcode::I32Store16(align, offset) => self.run_store_wrap::<i32, i16>(context, align, offset),
|
||||
&Opcode::I64Store8(align, offset) => self.run_store_wrap::<i64, i8>(context, align, offset),
|
||||
|
@ -251,19 +252,19 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
|||
&Opcode::I64GeS => self.run_gte::<i64>(context),
|
||||
&Opcode::I64GeU => self.run_gte::<u64>(context),
|
||||
|
||||
&Opcode::F32Eq => self.run_eq::<f32>(context),
|
||||
&Opcode::F32Ne => self.run_ne::<f32>(context),
|
||||
&Opcode::F32Lt => self.run_lt::<f32>(context),
|
||||
&Opcode::F32Gt => self.run_gt::<f32>(context),
|
||||
&Opcode::F32Le => self.run_lte::<f32>(context),
|
||||
&Opcode::F32Ge => self.run_gte::<f32>(context),
|
||||
&Opcode::F32Eq => self.run_eq::<F32>(context),
|
||||
&Opcode::F32Ne => self.run_ne::<F32>(context),
|
||||
&Opcode::F32Lt => self.run_lt::<F32>(context),
|
||||
&Opcode::F32Gt => self.run_gt::<F32>(context),
|
||||
&Opcode::F32Le => self.run_lte::<F32>(context),
|
||||
&Opcode::F32Ge => self.run_gte::<F32>(context),
|
||||
|
||||
&Opcode::F64Eq => self.run_eq::<f64>(context),
|
||||
&Opcode::F64Ne => self.run_ne::<f64>(context),
|
||||
&Opcode::F64Lt => self.run_lt::<f64>(context),
|
||||
&Opcode::F64Gt => self.run_gt::<f64>(context),
|
||||
&Opcode::F64Le => self.run_lte::<f64>(context),
|
||||
&Opcode::F64Ge => self.run_gte::<f64>(context),
|
||||
&Opcode::F64Eq => self.run_eq::<F64>(context),
|
||||
&Opcode::F64Ne => self.run_ne::<F64>(context),
|
||||
&Opcode::F64Lt => self.run_lt::<F64>(context),
|
||||
&Opcode::F64Gt => self.run_gt::<F64>(context),
|
||||
&Opcode::F64Le => self.run_lte::<F64>(context),
|
||||
&Opcode::F64Ge => self.run_gte::<F64>(context),
|
||||
|
||||
&Opcode::I32Clz => self.run_clz::<i32>(context),
|
||||
&Opcode::I32Ctz => self.run_ctz::<i32>(context),
|
||||
|
@ -303,62 +304,62 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
|||
&Opcode::I64Rotl => self.run_rotl::<i64>(context),
|
||||
&Opcode::I64Rotr => self.run_rotr::<i64>(context),
|
||||
|
||||
&Opcode::F32Abs => self.run_abs::<f32>(context),
|
||||
&Opcode::F32Neg => self.run_neg::<f32>(context),
|
||||
&Opcode::F32Ceil => self.run_ceil::<f32>(context),
|
||||
&Opcode::F32Floor => self.run_floor::<f32>(context),
|
||||
&Opcode::F32Trunc => self.run_trunc::<f32>(context),
|
||||
&Opcode::F32Nearest => self.run_nearest::<f32>(context),
|
||||
&Opcode::F32Sqrt => self.run_sqrt::<f32>(context),
|
||||
&Opcode::F32Add => self.run_add::<f32>(context),
|
||||
&Opcode::F32Sub => self.run_sub::<f32>(context),
|
||||
&Opcode::F32Mul => self.run_mul::<f32>(context),
|
||||
&Opcode::F32Div => self.run_div::<f32, f32>(context),
|
||||
&Opcode::F32Min => self.run_min::<f32>(context),
|
||||
&Opcode::F32Max => self.run_max::<f32>(context),
|
||||
&Opcode::F32Copysign => self.run_copysign::<f32>(context),
|
||||
&Opcode::F32Abs => self.run_abs::<F32>(context),
|
||||
&Opcode::F32Neg => self.run_neg::<F32>(context),
|
||||
&Opcode::F32Ceil => self.run_ceil::<F32>(context),
|
||||
&Opcode::F32Floor => self.run_floor::<F32>(context),
|
||||
&Opcode::F32Trunc => self.run_trunc::<F32>(context),
|
||||
&Opcode::F32Nearest => self.run_nearest::<F32>(context),
|
||||
&Opcode::F32Sqrt => self.run_sqrt::<F32>(context),
|
||||
&Opcode::F32Add => self.run_add::<F32>(context),
|
||||
&Opcode::F32Sub => self.run_sub::<F32>(context),
|
||||
&Opcode::F32Mul => self.run_mul::<F32>(context),
|
||||
&Opcode::F32Div => self.run_div::<F32, F32>(context),
|
||||
&Opcode::F32Min => self.run_min::<F32>(context),
|
||||
&Opcode::F32Max => self.run_max::<F32>(context),
|
||||
&Opcode::F32Copysign => self.run_copysign::<F32>(context),
|
||||
|
||||
&Opcode::F64Abs => self.run_abs::<f64>(context),
|
||||
&Opcode::F64Neg => self.run_neg::<f64>(context),
|
||||
&Opcode::F64Ceil => self.run_ceil::<f64>(context),
|
||||
&Opcode::F64Floor => self.run_floor::<f64>(context),
|
||||
&Opcode::F64Trunc => self.run_trunc::<f64>(context),
|
||||
&Opcode::F64Nearest => self.run_nearest::<f64>(context),
|
||||
&Opcode::F64Sqrt => self.run_sqrt::<f64>(context),
|
||||
&Opcode::F64Add => self.run_add::<f64>(context),
|
||||
&Opcode::F64Sub => self.run_sub::<f64>(context),
|
||||
&Opcode::F64Mul => self.run_mul::<f64>(context),
|
||||
&Opcode::F64Div => self.run_div::<f64, f64>(context),
|
||||
&Opcode::F64Min => self.run_min::<f64>(context),
|
||||
&Opcode::F64Max => self.run_max::<f64>(context),
|
||||
&Opcode::F64Copysign => self.run_copysign::<f64>(context),
|
||||
&Opcode::F64Abs => self.run_abs::<F64>(context),
|
||||
&Opcode::F64Neg => self.run_neg::<F64>(context),
|
||||
&Opcode::F64Ceil => self.run_ceil::<F64>(context),
|
||||
&Opcode::F64Floor => self.run_floor::<F64>(context),
|
||||
&Opcode::F64Trunc => self.run_trunc::<F64>(context),
|
||||
&Opcode::F64Nearest => self.run_nearest::<F64>(context),
|
||||
&Opcode::F64Sqrt => self.run_sqrt::<F64>(context),
|
||||
&Opcode::F64Add => self.run_add::<F64>(context),
|
||||
&Opcode::F64Sub => self.run_sub::<F64>(context),
|
||||
&Opcode::F64Mul => self.run_mul::<F64>(context),
|
||||
&Opcode::F64Div => self.run_div::<F64, F64>(context),
|
||||
&Opcode::F64Min => self.run_min::<F64>(context),
|
||||
&Opcode::F64Max => self.run_max::<F64>(context),
|
||||
&Opcode::F64Copysign => self.run_copysign::<F64>(context),
|
||||
|
||||
&Opcode::I32WrapI64 => self.run_wrap::<i64, i32>(context),
|
||||
&Opcode::I32TruncSF32 => self.run_trunc_to_int::<f32, i32, i32>(context),
|
||||
&Opcode::I32TruncUF32 => self.run_trunc_to_int::<f32, u32, i32>(context),
|
||||
&Opcode::I32TruncSF64 => self.run_trunc_to_int::<f64, i32, i32>(context),
|
||||
&Opcode::I32TruncUF64 => self.run_trunc_to_int::<f64, u32, i32>(context),
|
||||
&Opcode::I32TruncSF32 => self.run_trunc_to_int::<F32, i32, i32>(context),
|
||||
&Opcode::I32TruncUF32 => self.run_trunc_to_int::<F32, u32, i32>(context),
|
||||
&Opcode::I32TruncSF64 => self.run_trunc_to_int::<F64, i32, i32>(context),
|
||||
&Opcode::I32TruncUF64 => self.run_trunc_to_int::<F64, u32, i32>(context),
|
||||
&Opcode::I64ExtendSI32 => self.run_extend::<i32, i64, i64>(context),
|
||||
&Opcode::I64ExtendUI32 => self.run_extend::<u32, u64, i64>(context),
|
||||
&Opcode::I64TruncSF32 => self.run_trunc_to_int::<f32, i64, i64>(context),
|
||||
&Opcode::I64TruncUF32 => self.run_trunc_to_int::<f32, u64, i64>(context),
|
||||
&Opcode::I64TruncSF64 => self.run_trunc_to_int::<f64, i64, i64>(context),
|
||||
&Opcode::I64TruncUF64 => self.run_trunc_to_int::<f64, u64, i64>(context),
|
||||
&Opcode::F32ConvertSI32 => self.run_extend::<i32, f32, f32>(context),
|
||||
&Opcode::F32ConvertUI32 => self.run_extend::<u32, f32, f32>(context),
|
||||
&Opcode::F32ConvertSI64 => self.run_wrap::<i64, f32>(context),
|
||||
&Opcode::F32ConvertUI64 => self.run_wrap::<u64, f32>(context),
|
||||
&Opcode::F32DemoteF64 => self.run_wrap::<f64, f32>(context),
|
||||
&Opcode::F64ConvertSI32 => self.run_extend::<i32, f64, f64>(context),
|
||||
&Opcode::F64ConvertUI32 => self.run_extend::<u32, f64, f64>(context),
|
||||
&Opcode::F64ConvertSI64 => self.run_extend::<i64, f64, f64>(context),
|
||||
&Opcode::F64ConvertUI64 => self.run_extend::<u64, f64, f64>(context),
|
||||
&Opcode::F64PromoteF32 => self.run_extend::<f32, f64, f64>(context),
|
||||
&Opcode::I64TruncSF32 => self.run_trunc_to_int::<F32, i64, i64>(context),
|
||||
&Opcode::I64TruncUF32 => self.run_trunc_to_int::<F32, u64, i64>(context),
|
||||
&Opcode::I64TruncSF64 => self.run_trunc_to_int::<F64, i64, i64>(context),
|
||||
&Opcode::I64TruncUF64 => self.run_trunc_to_int::<F64, u64, i64>(context),
|
||||
&Opcode::F32ConvertSI32 => self.run_extend::<i32, F32, F32>(context),
|
||||
&Opcode::F32ConvertUI32 => self.run_extend::<u32, F32, F32>(context),
|
||||
&Opcode::F32ConvertSI64 => self.run_wrap::<i64, F32>(context),
|
||||
&Opcode::F32ConvertUI64 => self.run_wrap::<u64, F32>(context),
|
||||
&Opcode::F32DemoteF64 => self.run_wrap::<F64, F32>(context),
|
||||
&Opcode::F64ConvertSI32 => self.run_extend::<i32, F64, F64>(context),
|
||||
&Opcode::F64ConvertUI32 => self.run_extend::<u32, F64, F64>(context),
|
||||
&Opcode::F64ConvertSI64 => self.run_extend::<i64, F64, F64>(context),
|
||||
&Opcode::F64ConvertUI64 => self.run_extend::<u64, F64, F64>(context),
|
||||
&Opcode::F64PromoteF32 => self.run_extend::<F32, F64, F64>(context),
|
||||
|
||||
&Opcode::I32ReinterpretF32 => self.run_reinterpret::<f32, i32>(context),
|
||||
&Opcode::I64ReinterpretF64 => self.run_reinterpret::<f64, i64>(context),
|
||||
&Opcode::F32ReinterpretI32 => self.run_reinterpret::<i32, f32>(context),
|
||||
&Opcode::F64ReinterpretI64 => self.run_reinterpret::<i64, f64>(context),
|
||||
&Opcode::I32ReinterpretF32 => self.run_reinterpret::<F32, i32>(context),
|
||||
&Opcode::I64ReinterpretF64 => self.run_reinterpret::<F64, i64>(context),
|
||||
&Opcode::F32ReinterpretI32 => self.run_reinterpret::<i32, F32>(context),
|
||||
&Opcode::F64ReinterpretI64 => self.run_reinterpret::<i64, F64>(context),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
200
src/value.rs
200
src/value.rs
|
@ -1,6 +1,7 @@
|
|||
use std::{i32, i64, u32, u64, f32};
|
||||
use std::io;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use nan_preserving_float::{F32, F64};
|
||||
use std::io;
|
||||
use std::{f32, i32, i64, u32, u64};
|
||||
use TrapKind;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -22,9 +23,9 @@ pub enum RuntimeValue {
|
|||
/// Value of 64-bit signed or unsigned integer.
|
||||
I64(i64),
|
||||
/// Value of 32-bit IEEE 754-2008 floating point number.
|
||||
F32(f32),
|
||||
F32(F32),
|
||||
/// Value of 64-bit IEEE 754-2008 floating point number.
|
||||
F64(f64),
|
||||
F64(F64),
|
||||
}
|
||||
|
||||
/// Trait for creating value from a [`RuntimeValue`].
|
||||
|
@ -136,19 +137,19 @@ impl RuntimeValue {
|
|||
match value_type {
|
||||
::types::ValueType::I32 => RuntimeValue::I32(0),
|
||||
::types::ValueType::I64 => RuntimeValue::I64(0),
|
||||
::types::ValueType::F32 => RuntimeValue::F32(0f32),
|
||||
::types::ValueType::F64 => RuntimeValue::F64(0f64),
|
||||
::types::ValueType::F32 => RuntimeValue::F32(0f32.into()),
|
||||
::types::ValueType::F64 => RuntimeValue::F64(0f64.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new value by interpreting passed u32 as f32.
|
||||
pub fn decode_f32(val: u32) -> Self {
|
||||
RuntimeValue::F32(f32::from_bits(val))
|
||||
RuntimeValue::F32(F32::from_bits(val))
|
||||
}
|
||||
|
||||
/// Creates new value by interpreting passed u64 as f64.
|
||||
pub fn decode_f64(val: u64) -> Self {
|
||||
RuntimeValue::F64(f64::from_bits(val))
|
||||
RuntimeValue::F64(F64::from_bits(val))
|
||||
}
|
||||
|
||||
/// Get variable type for this value.
|
||||
|
@ -197,14 +198,14 @@ impl From<u64> for RuntimeValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<f32> for RuntimeValue {
|
||||
fn from(val: f32) -> Self {
|
||||
impl From<F32> for RuntimeValue {
|
||||
fn from(val: F32) -> Self {
|
||||
RuntimeValue::F32(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for RuntimeValue {
|
||||
fn from(val: f64) -> Self {
|
||||
impl From<F64> for RuntimeValue {
|
||||
fn from(val: F64) -> Self {
|
||||
RuntimeValue::F64(val)
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +215,7 @@ macro_rules! impl_from_runtime_value {
|
|||
impl FromRuntimeValue for $into {
|
||||
fn from_runtime_value(val: RuntimeValue) -> Option<Self> {
|
||||
match val {
|
||||
RuntimeValue::$expected_rt_ty(val) => Some(val as $into),
|
||||
RuntimeValue::$expected_rt_ty(val) => Some(val.transmute_into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -237,19 +238,26 @@ impl FromRuntimeValue for bool {
|
|||
|
||||
impl_from_runtime_value!(I32, i32);
|
||||
impl_from_runtime_value!(I64, i64);
|
||||
impl_from_runtime_value!(F32, f32);
|
||||
impl_from_runtime_value!(F64, f64);
|
||||
impl_from_runtime_value!(F32, F32);
|
||||
impl_from_runtime_value!(F64, F64);
|
||||
impl_from_runtime_value!(I32, u32);
|
||||
impl_from_runtime_value!(I64, u64);
|
||||
|
||||
macro_rules! impl_wrap_into {
|
||||
($from: ident, $into: ident) => {
|
||||
($from:ident, $into:ident) => {
|
||||
impl WrapInto<$into> for $from {
|
||||
fn wrap_into(self) -> $into {
|
||||
self as $into
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($from:ident, $intermediate:ident, $into:ident) => {
|
||||
impl WrapInto<$into> for $from {
|
||||
fn wrap_into(self) -> $into {
|
||||
$into::from(self as $intermediate)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_wrap_into!(i32, i8);
|
||||
|
@ -257,13 +265,19 @@ impl_wrap_into!(i32, i16);
|
|||
impl_wrap_into!(i64, i8);
|
||||
impl_wrap_into!(i64, i16);
|
||||
impl_wrap_into!(i64, i32);
|
||||
impl_wrap_into!(i64, f32);
|
||||
impl_wrap_into!(u64, f32);
|
||||
impl_wrap_into!(i64, f32, F32);
|
||||
impl_wrap_into!(u64, f32, F32);
|
||||
// Casting from an f64 to an f32 will produce the closest possible value (rounding strategy unspecified)
|
||||
// NOTE: currently this will cause Undefined Behavior if the value is finite but larger or smaller than the
|
||||
// largest or smallest finite value representable by f32. This is a bug and will be fixed.
|
||||
impl_wrap_into!(f64, f32);
|
||||
|
||||
impl WrapInto<F32> for F64 {
|
||||
fn wrap_into(self) -> F32 {
|
||||
(f64::from(self) as f32).into()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_try_truncate_into {
|
||||
($from: ident, $into: ident) => {
|
||||
impl TryTruncateInto<$into, TrapKind> for $from {
|
||||
|
@ -284,7 +298,14 @@ macro_rules! impl_try_truncate_into {
|
|||
Ok(self as $into)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($from:ident, $intermediate:ident, $into:ident) => {
|
||||
impl TryTruncateInto<$into, TrapKind> for $from {
|
||||
fn try_truncate_into(self) -> Result<$into, TrapKind> {
|
||||
$intermediate::from(self).try_truncate_into()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_try_truncate_into!(f32, i32);
|
||||
|
@ -295,15 +316,30 @@ impl_try_truncate_into!(f32, u32);
|
|||
impl_try_truncate_into!(f32, u64);
|
||||
impl_try_truncate_into!(f64, u32);
|
||||
impl_try_truncate_into!(f64, u64);
|
||||
impl_try_truncate_into!(F32, f32, i32);
|
||||
impl_try_truncate_into!(F32, f32, i64);
|
||||
impl_try_truncate_into!(F64, f64, i32);
|
||||
impl_try_truncate_into!(F64, f64, i64);
|
||||
impl_try_truncate_into!(F32, f32, u32);
|
||||
impl_try_truncate_into!(F32, f32, u64);
|
||||
impl_try_truncate_into!(F64, f64, u32);
|
||||
impl_try_truncate_into!(F64, f64, u64);
|
||||
|
||||
macro_rules! impl_extend_into {
|
||||
($from: ident, $into: ident) => {
|
||||
($from:ident, $into:ident) => {
|
||||
impl ExtendInto<$into> for $from {
|
||||
fn extend_into(self) -> $into {
|
||||
self as $into
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($from:ident, $intermediate:ident, $into:ident) => {
|
||||
impl ExtendInto<$into> for $from {
|
||||
fn extend_into(self) -> $into {
|
||||
$into::from(self as $intermediate)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_extend_into!(i8, i32);
|
||||
|
@ -325,6 +361,20 @@ impl_extend_into!(i64, f64);
|
|||
impl_extend_into!(u64, f64);
|
||||
impl_extend_into!(f32, f64);
|
||||
|
||||
impl_extend_into!(i32, f32, F32);
|
||||
impl_extend_into!(i32, f64, F64);
|
||||
impl_extend_into!(u32, f32, F32);
|
||||
impl_extend_into!(u32, f64, F64);
|
||||
impl_extend_into!(i64, f64, F64);
|
||||
impl_extend_into!(u64, f64, F64);
|
||||
impl_extend_into!(f32, f64, F64);
|
||||
|
||||
impl ExtendInto<F64> for F32 {
|
||||
fn extend_into(self) -> F64 {
|
||||
(f32::from(self) as f64).into()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_transmute_into_self {
|
||||
($type: ident) => {
|
||||
impl TransmuteInto<$type> for $type {
|
||||
|
@ -339,6 +389,8 @@ impl_transmute_into_self!(i32);
|
|||
impl_transmute_into_self!(i64);
|
||||
impl_transmute_into_self!(f32);
|
||||
impl_transmute_into_self!(f64);
|
||||
impl_transmute_into_self!(F32);
|
||||
impl_transmute_into_self!(F64);
|
||||
|
||||
macro_rules! impl_transmute_into_as {
|
||||
($from: ident, $into: ident) => {
|
||||
|
@ -357,6 +409,49 @@ impl_transmute_into_as!(u32, i32);
|
|||
impl_transmute_into_as!(i64, u64);
|
||||
impl_transmute_into_as!(u64, i64);
|
||||
|
||||
macro_rules! impl_transmute_into_npf {
|
||||
($npf:ident, $float:ident, $signed:ident, $unsigned:ident) => {
|
||||
impl TransmuteInto<$float> for $npf {
|
||||
fn transmute_into(self) -> $float {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TransmuteInto<$npf> for $float {
|
||||
fn transmute_into(self) -> $npf {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TransmuteInto<$signed> for $npf {
|
||||
fn transmute_into(self) -> $signed {
|
||||
self.to_bits() as _
|
||||
}
|
||||
}
|
||||
|
||||
impl TransmuteInto<$unsigned> for $npf {
|
||||
fn transmute_into(self) -> $unsigned {
|
||||
self.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl TransmuteInto<$npf> for $signed {
|
||||
fn transmute_into(self) -> $npf {
|
||||
$npf::from_bits(self as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl TransmuteInto<$npf> for $unsigned {
|
||||
fn transmute_into(self) -> $npf {
|
||||
$npf::from_bits(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_transmute_into_npf!(F32, f32, i32, u32);
|
||||
impl_transmute_into_npf!(F64, f64, i64, u64);
|
||||
|
||||
impl TransmuteInto<i32> for f32 {
|
||||
fn transmute_into(self) -> i32 { self.to_bits() as i32 }
|
||||
}
|
||||
|
@ -497,6 +592,26 @@ impl LittleEndianConvert for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for F32 {
|
||||
fn into_little_endian(self) -> Vec<u8> {
|
||||
(self.to_bits() as i32).into_little_endian()
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
i32::from_little_endian(buffer).map(|val| Self::from_bits(val as _))
|
||||
}
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for F64 {
|
||||
fn into_little_endian(self) -> Vec<u8> {
|
||||
(self.to_bits() as i64).into_little_endian()
|
||||
}
|
||||
|
||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||
i64::from_little_endian(buffer).map(|val| Self::from_bits(val as _))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_integer_arithmetic_ops {
|
||||
($type: ident) => {
|
||||
impl ArithmeticOps<$type> for $type {
|
||||
|
@ -538,6 +653,8 @@ macro_rules! impl_float_arithmetic_ops {
|
|||
|
||||
impl_float_arithmetic_ops!(f32);
|
||||
impl_float_arithmetic_ops!(f64);
|
||||
impl_float_arithmetic_ops!(F32);
|
||||
impl_float_arithmetic_ops!(F64);
|
||||
|
||||
macro_rules! impl_integer {
|
||||
($type: ident) => {
|
||||
|
@ -561,13 +678,26 @@ impl_integer!(i64);
|
|||
impl_integer!(u64);
|
||||
|
||||
macro_rules! impl_float {
|
||||
($type: ident, $int_type: ident) => {
|
||||
($type:ident, $int_type:ident) => {
|
||||
impl_float!($type, $type, $int_type);
|
||||
};
|
||||
($type:ident, $intermediate:ident, $int_type:ident) => {
|
||||
impl Float<$type> for $type {
|
||||
fn abs(self) -> $type { self.abs() }
|
||||
fn floor(self) -> $type { self.floor() }
|
||||
fn ceil(self) -> $type { self.ceil() }
|
||||
fn trunc(self) -> $type { self.trunc() }
|
||||
fn round(self) -> $type { self.round() }
|
||||
fn abs(self) -> $type {
|
||||
$intermediate::abs(self.into()).into()
|
||||
}
|
||||
fn floor(self) -> $type {
|
||||
$intermediate::floor(self.into()).into()
|
||||
}
|
||||
fn ceil(self) -> $type {
|
||||
$intermediate::ceil(self.into()).into()
|
||||
}
|
||||
fn trunc(self) -> $type {
|
||||
$intermediate::trunc(self.into()).into()
|
||||
}
|
||||
fn round(self) -> $type {
|
||||
$intermediate::round(self.into()).into()
|
||||
}
|
||||
fn nearest(self) -> $type {
|
||||
let round = self.round();
|
||||
if self.fract().abs() != 0.5 {
|
||||
|
@ -583,12 +713,13 @@ macro_rules! impl_float {
|
|||
round
|
||||
}
|
||||
}
|
||||
fn sqrt(self) -> $type { self.sqrt() }
|
||||
fn sqrt(self) -> $type {
|
||||
$intermediate::sqrt(self.into()).into()
|
||||
}
|
||||
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
|
||||
fn min(self, other: $type) -> $type {
|
||||
if self.is_nan() || other.is_nan() {
|
||||
use std::$type;
|
||||
return $type::NAN;
|
||||
return ::std::$intermediate::NAN.into();
|
||||
}
|
||||
|
||||
self.min(other)
|
||||
|
@ -596,8 +727,7 @@ macro_rules! impl_float {
|
|||
// This instruction corresponds to what is sometimes called "maxNaN" in other languages.
|
||||
fn max(self, other: $type) -> $type {
|
||||
if self.is_nan() || other.is_nan() {
|
||||
use std::$type;
|
||||
return $type::NAN;
|
||||
return ::std::$intermediate::NAN.into();
|
||||
}
|
||||
|
||||
self.max(other)
|
||||
|
@ -623,8 +753,10 @@ macro_rules! impl_float {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_float!(f32, i32);
|
||||
impl_float!(f64, i64);
|
||||
impl_float!(F32, f32, i32);
|
||||
impl_float!(F64, f64, i64);
|
||||
|
|
|
@ -2,22 +2,20 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use wasmi::{
|
||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
|
||||
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap,
|
||||
};
|
||||
use wasmi::memory_units::Pages;
|
||||
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
|
||||
use wasmi::memory_units::Pages;
|
||||
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
|
||||
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
|
||||
|
||||
fn spec_to_runtime_value(value: Value) -> RuntimeValue {
|
||||
match value {
|
||||
Value::I32(v) => RuntimeValue::I32(v),
|
||||
Value::I64(v) => RuntimeValue::I64(v),
|
||||
Value::F32(v) => RuntimeValue::F32(v),
|
||||
Value::F64(v) => RuntimeValue::F64(v),
|
||||
}
|
||||
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
||||
match val {
|
||||
Value::I32(v) => RuntimeValue::I32(v),
|
||||
Value::I64(v) => RuntimeValue::I64(v),
|
||||
Value::F32(v) => RuntimeValue::F32(v.into()),
|
||||
Value::F64(v) => RuntimeValue::F64(v.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -49,15 +47,15 @@ struct SpecModule {
|
|||
}
|
||||
|
||||
impl SpecModule {
|
||||
fn new() -> Self {
|
||||
SpecModule {
|
||||
table: TableInstance::alloc(10, Some(20)).unwrap(),
|
||||
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
|
||||
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
|
||||
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false),
|
||||
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0), false),
|
||||
}
|
||||
}
|
||||
fn new() -> Self {
|
||||
SpecModule {
|
||||
table: TableInstance::alloc(10, Some(20)).unwrap(),
|
||||
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
|
||||
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
|
||||
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false),
|
||||
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PRINT_FUNC_INDEX: usize = 0;
|
||||
|
@ -121,341 +119,365 @@ impl ModuleImportResolver for SpecModule {
|
|||
_ => Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host global import {}",
|
||||
field_name
|
||||
)))
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if field_name == "memory" {
|
||||
return Ok(self.memory.clone());
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if field_name == "memory" {
|
||||
return Ok(self.memory.clone());
|
||||
}
|
||||
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host memory import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host memory import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if field_name == "table" {
|
||||
return Ok(self.table.clone());
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if field_name == "table" {
|
||||
return Ok(self.table.clone());
|
||||
}
|
||||
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host table import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host table import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
struct SpecDriver {
|
||||
spec_module: SpecModule,
|
||||
instances: HashMap<String, ModuleRef>,
|
||||
last_module: Option<ModuleRef>,
|
||||
spec_module: SpecModule,
|
||||
instances: HashMap<String, ModuleRef>,
|
||||
last_module: Option<ModuleRef>,
|
||||
}
|
||||
|
||||
impl SpecDriver {
|
||||
fn new() -> SpecDriver {
|
||||
SpecDriver {
|
||||
spec_module: SpecModule::new(),
|
||||
instances: HashMap::new(),
|
||||
last_module: None,
|
||||
}
|
||||
}
|
||||
fn new() -> SpecDriver {
|
||||
SpecDriver {
|
||||
spec_module: SpecModule::new(),
|
||||
instances: HashMap::new(),
|
||||
last_module: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn spec_module(&mut self) -> &mut SpecModule {
|
||||
&mut self.spec_module
|
||||
}
|
||||
fn spec_module(&mut self) -> &mut SpecModule {
|
||||
&mut self.spec_module
|
||||
}
|
||||
|
||||
fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
|
||||
self.last_module = Some(module.clone());
|
||||
if let Some(name) = name {
|
||||
self.instances.insert(name, module);
|
||||
}
|
||||
}
|
||||
fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
|
||||
self.last_module = Some(module.clone());
|
||||
if let Some(name) = name {
|
||||
self.instances.insert(name, module);
|
||||
}
|
||||
}
|
||||
|
||||
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
|
||||
self.instances.get(name).cloned().ok_or_else(|| {
|
||||
InterpreterError::Instantiation(format!("Module not registered {}", name))
|
||||
})
|
||||
}
|
||||
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
|
||||
self.instances.get(name).cloned().ok_or_else(|| {
|
||||
InterpreterError::Instantiation(format!("Module not registered {}", name))
|
||||
})
|
||||
}
|
||||
|
||||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||
match name {
|
||||
Some(name) => self.module(name),
|
||||
None => self.last_module
|
||||
.clone()
|
||||
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
|
||||
}
|
||||
}
|
||||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||
match name {
|
||||
Some(name) => self.module(name),
|
||||
None => self.last_module
|
||||
.clone()
|
||||
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportResolver for SpecDriver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_func(field_name, func_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_func(field_name, func_type)
|
||||
}
|
||||
}
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_func(field_name, func_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_func(field_name, func_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_global(field_name, global_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_global(field_name, global_type)
|
||||
}
|
||||
}
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_global(field_name, global_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_global(field_name, global_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_memory(field_name, memory_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_memory(field_name, memory_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_table(field_name, table_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_table(field_name, table_type)
|
||||
}
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_table(field_name, table_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_table(field_name, table_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_load_module(wasm: &[u8]) -> Result<Module, Error> {
|
||||
Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string()))
|
||||
Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string()))
|
||||
}
|
||||
|
||||
fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> {
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
|
||||
instance
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
Ok(())
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
|
||||
instance
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_module(wasm: &[u8], name: &Option<String>, spec_driver: &mut SpecDriver) -> Result<ModuleRef, Error> {
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, spec_driver)
|
||||
.map_err(|e| Error::Load(e.to_string()))?
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
fn load_module(
|
||||
wasm: &[u8],
|
||||
name: &Option<String>,
|
||||
spec_driver: &mut SpecDriver,
|
||||
) -> Result<ModuleRef, Error> {
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, spec_driver)
|
||||
.map_err(|e| Error::Load(e.to_string()))?
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
|
||||
let module_name = name.clone();
|
||||
spec_driver.add_module(module_name, instance.clone());
|
||||
let module_name = name.clone();
|
||||
spec_driver.add_module(module_name, instance.clone());
|
||||
|
||||
Ok(instance)
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn run_action(
|
||||
program: &mut SpecDriver,
|
||||
action: &Action,
|
||||
program: &mut SpecDriver,
|
||||
action: &Action<u32, u64>,
|
||||
) -> Result<Option<RuntimeValue>, InterpreterError> {
|
||||
match *action {
|
||||
Action::Invoke {
|
||||
ref module,
|
||||
ref field,
|
||||
ref args,
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
module.invoke_export(
|
||||
field,
|
||||
&args.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>(),
|
||||
program.spec_module(),
|
||||
)
|
||||
}
|
||||
Action::Get {
|
||||
ref module,
|
||||
ref field,
|
||||
..
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let global = module
|
||||
.export_by_name(&field)
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
||||
})?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
||||
})?;
|
||||
Ok(Some(global.get()))
|
||||
}
|
||||
}
|
||||
match *action {
|
||||
Action::Invoke {
|
||||
ref module,
|
||||
ref field,
|
||||
ref args,
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let vec_args = args.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
module.invoke_export(field, &vec_args, program.spec_module())
|
||||
}
|
||||
Action::Get {
|
||||
ref module,
|
||||
ref field,
|
||||
..
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let global = module
|
||||
.export_by_name(&field)
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
||||
})?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
||||
})?;
|
||||
Ok(Some(global.get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spec(name: &str) {
|
||||
println!("running test: {}", name);
|
||||
try_spec(name).expect("Failed to run spec");
|
||||
println!("running test: {}", name);
|
||||
try_spec(name).expect("Failed to run spec");
|
||||
}
|
||||
|
||||
fn try_spec(name: &str) -> Result<(), Error> {
|
||||
let mut spec_driver = SpecDriver::new();
|
||||
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
|
||||
let mut parser = ScriptParser::from_file(spec_script_path).expect("Can't read spec script");
|
||||
while let Some(Command { kind, line }) = parser.next()? {
|
||||
println!("Line {}:", line);
|
||||
match kind {
|
||||
CommandKind::Module { name, module, .. } => {
|
||||
load_module(&module.into_vec()?, &name, &mut spec_driver).expect("Failed to load module");
|
||||
}
|
||||
CommandKind::AssertReturn { action, expected } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
let spec_expected = expected
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
|
||||
for (actual_result, spec_expected) in
|
||||
actual_result.iter().zip(spec_expected.iter())
|
||||
{
|
||||
assert_eq!(actual_result.value_type(), spec_expected.value_type());
|
||||
// f32::NAN != f32::NAN
|
||||
match spec_expected {
|
||||
&RuntimeValue::F32(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F32(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
&RuntimeValue::F64(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F64(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
spec_expected @ _ => assert_eq!(actual_result, spec_expected),
|
||||
}
|
||||
}
|
||||
println!("assert_return at line {} - success", line);
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Expected action to return value, got error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertReturnCanonicalNan { action }
|
||||
| CommandKind::AssertReturnArithmeticNan { action } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
|
||||
match actual_result {
|
||||
RuntimeValue::F32(val) => if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
RuntimeValue::F64(val) => if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
val @ _ => {
|
||||
panic!("Expected action to return float value, got {:?}", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("assert_return_nan at line {} - success", line);
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Expected action to return value, got error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertExhaustion { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
|
||||
Err(e) => println!("assert_exhaustion at line {} - success ({:?})", line, e),
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
panic!(
|
||||
"Expected action to result in a trap, got result: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("assert_trap at line {} - success ({:?})", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertInvalid { module, .. }
|
||||
| CommandKind::AssertMalformed { module, .. }
|
||||
| CommandKind::AssertUnlinkable { module, .. } => {
|
||||
let module_load = try_load(&module.into_vec()?, &mut spec_driver);
|
||||
match module_load {
|
||||
Ok(_) => panic!("Expected invalid module definition, got some module!"),
|
||||
Err(e) => println!("assert_invalid at line {} - success ({:?})", line, e),
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, .. } => {
|
||||
match try_load(&module.into_vec()?, &mut spec_driver) {
|
||||
Ok(_) => panic!("Expected error running start function at line {}", line),
|
||||
Err(e) => println!("assert_uninstantiable - success ({:?})", e),
|
||||
}
|
||||
}
|
||||
CommandKind::Register { name, as_name, .. } => {
|
||||
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
|
||||
Ok(module) => module,
|
||||
Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
|
||||
};
|
||||
spec_driver.add_module(Some(as_name.clone()), module);
|
||||
}
|
||||
CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) {
|
||||
Ok(_) => {}
|
||||
Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e),
|
||||
},
|
||||
}
|
||||
}
|
||||
let mut spec_driver = SpecDriver::new();
|
||||
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
|
||||
let mut parser = ScriptParser::from_file(spec_script_path).expect("Can't read spec script");
|
||||
let mut errors = vec![];
|
||||
|
||||
Ok(())
|
||||
while let Some(Command { kind, line }) = parser.next()? {
|
||||
macro_rules! assert_eq {
|
||||
($a:expr, $b:expr) => {{
|
||||
let (a, b) = ($a, $b);
|
||||
|
||||
if a != b {
|
||||
errors.push(format!(
|
||||
r#"ERROR (line {}):
|
||||
expected: {:?}
|
||||
got: {:?}
|
||||
"#,
|
||||
line, b, a,
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
match kind {
|
||||
CommandKind::Module { name, module, .. } => {
|
||||
load_module(&module.into_vec()?, &name, &mut spec_driver)
|
||||
.expect("Failed to load module");
|
||||
}
|
||||
CommandKind::AssertReturn { action, expected } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
let spec_expected = expected
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
|
||||
for (actual_result, spec_expected) in
|
||||
actual_result.iter().zip(spec_expected.iter())
|
||||
{
|
||||
assert_eq!(actual_result.value_type(), spec_expected.value_type());
|
||||
// f32::NAN != f32::NAN
|
||||
match spec_expected {
|
||||
&RuntimeValue::F32(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F32(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
&RuntimeValue::F64(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F64(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
spec_expected @ _ => assert_eq!(actual_result, spec_expected),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Expected action to return value, got error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertReturnCanonicalNan { action }
|
||||
| CommandKind::AssertReturnArithmeticNan { action } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
|
||||
match actual_result {
|
||||
RuntimeValue::F32(val) => if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
RuntimeValue::F64(val) => if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
val @ _ => {
|
||||
panic!("Expected action to return float value, got {:?}", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Expected action to return value, got error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertExhaustion { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
|
||||
Err(_e) => {},
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
panic!(
|
||||
"Expected action to result in a trap, got result: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertInvalid { module, .. }
|
||||
| CommandKind::AssertMalformed { module, .. }
|
||||
| CommandKind::AssertUnlinkable { module, .. } => {
|
||||
let module_load = try_load(&module.into_vec()?, &mut spec_driver);
|
||||
match module_load {
|
||||
Ok(_) => panic!("Expected invalid module definition, got some module!"),
|
||||
Err(_e) => {},
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, .. } => {
|
||||
match try_load(&module.into_vec()?, &mut spec_driver) {
|
||||
Ok(_) => panic!("Expected error running start function at line {}", line),
|
||||
Err(_e) => {},
|
||||
}
|
||||
}
|
||||
CommandKind::Register { name, as_name, .. } => {
|
||||
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
|
||||
Ok(module) => module,
|
||||
Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
|
||||
};
|
||||
spec_driver.add_module(Some(as_name.clone()), module);
|
||||
}
|
||||
CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) {
|
||||
Ok(_) => {}
|
||||
Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
use std::fmt::Write;
|
||||
let mut out = "\n".to_owned();
|
||||
for err in errors {
|
||||
write!(out, "{}", err).expect("Error formatting errors");
|
||||
}
|
||||
panic!(out);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue