From 495b8e2f002ce76cec4ddb2c9fbed481f5339c59 Mon Sep 17 00:00:00 2001 From: Astrale Date: Mon, 19 Jun 2023 10:33:04 +0200 Subject: [PATCH] fmt + clippy --- build.rs | 38 +- src/ast.rs | 272 +++--- src/binary.rs | 1560 +++++++++++++++++---------------- src/interpreter.rs | 1849 ++++++++++++++++++++++------------------ src/lib.rs | 930 ++++++++++---------- src/ops.rs | 1027 +++++++++++----------- src/runtime.rs | 361 ++++---- src/types.rs | 92 +- src/valid.rs | 978 +++++++++++---------- src/values.rs | 96 +-- tests/run_suite.rs | 2 +- tests/script/mod.rs | 44 +- tests/script/parser.rs | 685 +++++++-------- tests/script/run.rs | 550 +++++++----- 14 files changed, 4585 insertions(+), 3899 deletions(-) diff --git a/build.rs b/build.rs index 78bc5da..abc6a7d 100644 --- a/build.rs +++ b/build.rs @@ -1,25 +1,35 @@ +use std::env; use std::fs::{self, File}; use std::io::prelude::*; -use std::env; use std::path::Path; fn main() { - // generate a driver for the test suite - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("test_suite.rs"); - let mut f = File::create(dest_path).unwrap(); + // generate a driver for the test suite + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("test_suite.rs"); + let mut f = File::create(dest_path).unwrap(); - for entry in fs::read_dir("tests/suite").unwrap() { - let path = entry.unwrap().path(); - let name = path.file_name().unwrap().to_str().unwrap().split('.').next().unwrap().replace("-", "_"); + for entry in fs::read_dir("tests/suite").unwrap() { + let path = entry.unwrap().path(); + let name = path + .file_name() + .unwrap() + .to_str() + .unwrap() + .split('.') + .next() + .unwrap() + .replace('-', "_"); - write!(f, -"#[test] + write!( + f, + "#[test] fn run_{}() {{ script::run(\"{}\"); }}\n", - name, - path.display(), - ).unwrap(); - } + name, + path.display(), + ) + .unwrap(); + } } diff --git a/src/ast.rs b/src/ast.rs index 58bc769..efad263 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -3,206 +3,258 @@ use values; #[derive(Debug)] pub enum IUnOp { - Clz, Ctz, Popcnt, + Clz, + Ctz, + Popcnt, } #[derive(Debug)] pub enum FUnOp { - Neg, Abs, Ceil, Floor, Trunc, Nearest, Sqrt, + Neg, + Abs, + Ceil, + Floor, + Trunc, + Nearest, + Sqrt, } #[derive(Debug)] pub enum IBinOp { - Add, Sub, Mul, DivS, DivU, RemS, RemU, And, Or, Xor, Shl, ShrS, ShrU, Rotl, Rotr, + Add, + Sub, + Mul, + DivS, + DivU, + RemS, + RemU, + And, + Or, + Xor, + Shl, + ShrS, + ShrU, + Rotl, + Rotr, } #[derive(Debug)] pub enum FBinOp { - Add, Sub, Mul, Div, Min, Max, CopySign, + Add, + Sub, + Mul, + Div, + Min, + Max, + CopySign, } #[derive(Debug)] pub enum ITestOp { - Eqz, + Eqz, } #[derive(Debug)] pub enum IRelOp { - Eq_, Ne, LtS, LtU, GtS, GtU, LeS, LeU, GeS, GeU, + Eq_, + Ne, + LtS, + LtU, + GtS, + GtU, + LeS, + LeU, + GeS, + GeU, } #[derive(Debug)] pub enum FRelOp { - Eq_, Ne, Lt, Gt, Le, Ge + Eq_, + Ne, + Lt, + Gt, + Le, + Ge, } #[derive(Debug)] pub enum ConvertOp { - I32WrapI64, - I64ExtendUI32, - I64ExtendSI32, - Trunc { from: types::Float, to: types::Int, signed: bool }, - Convert { from: types::Int, to: types::Float, signed: bool }, - Reinterpret { from: types::Value, to: types::Value }, - F32DemoteF64, - F64PromoteF32, + I32WrapI64, + I64ExtendUI32, + I64ExtendSI32, + Trunc { + from: types::Float, + to: types::Int, + signed: bool, + }, + Convert { + from: types::Int, + to: types::Float, + signed: bool, + }, + Reinterpret { + from: types::Value, + to: types::Value, + }, + F32DemoteF64, + F64PromoteF32, } #[derive(Debug)] pub struct MemOp { - pub align: u32, - pub offset: u32, - pub type_: types::Value, - pub opt: Option, + pub align: u32, + pub offset: u32, + pub type_: types::Value, + pub opt: Option, } /// A memory load with optional size and sign pub type LoadOp = MemOp<(u32, bool)>; /// A memory store with optional size -pub type StoreOp = MemOp<(u32)>; +pub type StoreOp = MemOp; #[derive(Debug)] pub enum Instr { - Unreachable, // trap unconditionally - Nop, // do nothing - Block(Vec, Vec), // execute in sequence - Loop(Vec, Vec), // loop header - If(Vec, Vec, Vec), // conditional - Br(Index), // break to n-th surrounding label - BrIf(Index), // conditional break - BrTable(Vec, Index), // indexed break - Return, // break from function body - Call(Index), // call function - CallIndirect(Index), // call function through table - Drop_, // forget a value - Select, // branchless conditional - GetLocal(Index), // read local variable - SetLocal(Index), // write local variable - TeeLocal(Index), // write local variable and keep value - GetGlobal(Index), // read global variable - SetGlobal(Index), // write global variable - Load(LoadOp), // read memory at address - Store(StoreOp), // write memory at address - CurrentMemory, // size(linear memory - GrowMemory, // grow linear memory - Const(values::Value), // constant - IUnary(types::Int, IUnOp), // integer unary numeric operators - FUnary(types::Float, FUnOp), // floating unary numeric operators - IBin(types::Int, IBinOp), // integer binary numeric operators - FBin(types::Float, FBinOp), // floating binary numeric operators - ITest(types::Int, ITestOp), // integer numeric test - IRel(types::Int, IRelOp), // integer numeric comparison - FRel(types::Float, FRelOp), // floating numeric comparison - Convert(ConvertOp), // conversion + Unreachable, // trap unconditionally + Nop, // do nothing + Block(Vec, Vec), // execute in sequence + Loop(Vec, Vec), // loop header + If(Vec, Vec, Vec), // conditional + Br(Index), // break to n-th surrounding label + BrIf(Index), // conditional break + BrTable(Vec, Index), // indexed break + Return, // break from function body + Call(Index), // call function + CallIndirect(Index), // call function through table + Drop_, // forget a value + Select, // branchless conditional + GetLocal(Index), // read local variable + SetLocal(Index), // write local variable + TeeLocal(Index), // write local variable and keep value + GetGlobal(Index), // read global variable + SetGlobal(Index), // write global variable + Load(LoadOp), // read memory at address + Store(StoreOp), // write memory at address + CurrentMemory, // size(linear memory + GrowMemory, // grow linear memory + Const(values::Value), // constant + IUnary(types::Int, IUnOp), // integer unary numeric operators + FUnary(types::Float, FUnOp), // floating unary numeric operators + IBin(types::Int, IBinOp), // integer binary numeric operators + FBin(types::Float, FBinOp), // floating binary numeric operators + ITest(types::Int, ITestOp), // integer numeric test + IRel(types::Int, IRelOp), // integer numeric comparison + FRel(types::Float, FRelOp), // floating numeric comparison + Convert(ConvertOp), // conversion } pub type Expr = Vec; #[derive(Debug)] pub struct Module { - pub types: Vec, - pub funcs: Vec, - pub tables: Vec, - pub memories: Vec, - pub globals: Vec, - pub elems: Vec>, // initial values for tables - pub data: Vec>, // initial values for memories - pub start: Option, // optionnal index to a start function - pub imports: Vec, - pub exports: Vec, + pub types: Vec, + pub funcs: Vec, + pub tables: Vec
, + pub memories: Vec, + pub globals: Vec, + pub elems: Vec>, // initial values for tables + pub data: Vec>, // initial values for memories + pub start: Option, // optionnal index to a start function + pub imports: Vec, + pub exports: Vec, } pub type Index = u32; #[derive(Debug)] pub struct Func { - pub type_index: Index, - pub locals: Vec, - pub body: Expr, + pub type_index: Index, + pub locals: Vec, + pub body: Expr, } #[derive(Debug)] pub struct Table { - pub type_: types::Table, + pub type_: types::Table, } #[derive(Debug)] pub struct Memory { - pub type_: types::Memory, + pub type_: types::Memory, } #[derive(Debug)] pub struct Global { - pub type_: types::Global, - pub value: Expr, // NB: Must be constant + pub type_: types::Global, + pub value: Expr, // NB: Must be constant } #[derive(Debug)] pub struct Segment { - pub index: Index, - pub offset: Expr, // NB: Must be constant - pub init: Vec, + pub index: Index, + pub offset: Expr, // NB: Must be constant + pub init: Vec, } #[derive(Debug)] pub struct Export { - pub name: String, - pub desc: ExportDesc, + pub name: String, + pub desc: ExportDesc, } #[derive(Debug)] pub enum ExportDesc { - Func(Index), - Table(Index), - Memory(Index), - Global(Index), + Func(Index), + Table(Index), + Memory(Index), + Global(Index), } #[derive(Debug)] pub struct Import { - pub module: String, - pub name: String, - pub desc: ImportDesc, + pub module: String, + pub name: String, + pub desc: ImportDesc, } #[derive(Debug)] pub enum ImportDesc { - Func(Index), - Table(types::Table), - Memory(types::Memory), - Global(types::Global), + Func(Index), + Table(types::Table), + Memory(types::Memory), + Global(types::Global), } impl Import { - pub fn type_(&self, module: &Module) -> types::Extern { - match self.desc { - ImportDesc::Func(idx) => types::Extern::Func(module.types[idx as usize].clone()), - ImportDesc::Table(ref t) => types::Extern::Table(t.clone()), - ImportDesc::Memory(ref t) => types::Extern::Memory(t.clone()), - ImportDesc::Global(ref t) => types::Extern::Global(t.clone()), - } - } + pub fn type_(&self, module: &Module) -> types::Extern { + match self.desc { + ImportDesc::Func(idx) => types::Extern::Func(module.types[idx as usize].clone()), + ImportDesc::Table(ref t) => types::Extern::Table(t.clone()), + ImportDesc::Memory(ref t) => types::Extern::Memory(t.clone()), + ImportDesc::Global(ref t) => types::Extern::Global(t.clone()), + } + } } - // Helper function for tests impl Module { - // Right now, we cannot only publish this function for test - // See https://github.com/rust-lang/rust/issues/45599 - // #[cfg(test)] - pub fn empty() -> Module { - Module { - types: Vec::new(), - funcs: Vec::new(), - tables: Vec::new(), - memories: Vec::new(), - globals: Vec::new(), - elems: Vec::new(), - data: Vec::new(), - start: None, - imports: Vec::new(), - exports: Vec::new(), - } - } + // Right now, we cannot only publish this function for test + // See https://github.com/rust-lang/rust/issues/45599 + // #[cfg(test)] + pub fn empty() -> Module { + Module { + types: Vec::new(), + funcs: Vec::new(), + tables: Vec::new(), + memories: Vec::new(), + globals: Vec::new(), + elems: Vec::new(), + data: Vec::new(), + start: None, + imports: Vec::new(), + exports: Vec::new(), + } + } } diff --git a/src/binary.rs b/src/binary.rs index ccbf964..a1e534c 100644 --- a/src/binary.rs +++ b/src/binary.rs @@ -1,9 +1,9 @@ -use std::{i32, i64, io}; -use std::io::Read; use ops::IntOp; +use std::io::Read; +use std::{i32, i64, io}; -use types; use ast::*; +use types; use values::Value; const MAGIC: u32 = 0x6d736100; @@ -12,744 +12,866 @@ pub const VERSION: u32 = 1; /// Decode a Web Assembly module from the given `reader` pub fn decode(reader: R) -> Result { - Decoder { reader: reader, pos: 0 }.read_module() + Decoder { + reader, + pos: 0, + } + .read_module() } #[derive(Debug)] pub enum DecodeError { - Io(io::Error), - MalformedBinary, + Io(io::Error), + MalformedBinary, } impl From for DecodeError { - fn from(e: io::Error) -> Self { - DecodeError::Io(e) - } + fn from(e: io::Error) -> Self { + DecodeError::Io(e) + } } type DecodeResult = Result; struct Decoder { - reader: R, - pos: usize, + reader: R, + pos: usize, } impl Decoder { - fn read_byte(&mut self) -> DecodeResult { - let mut buf = [0]; - self.reader.read_exact(&mut buf)?; - self.pos += 1; - Ok(buf[0]) - } - - fn read_u16(&mut self) -> DecodeResult { - let lo = self.read_byte()?; - let hi = self.read_byte()?; - Ok((hi as u16) << 8 | lo as u16) - } - - fn read_u32(&mut self) -> DecodeResult { - let lo = self.read_u16()?; - let hi = self.read_u16()?; - Ok((hi as u32) << 16 | lo as u32) - } - - fn read_u64(&mut self) -> DecodeResult { - let lo = self.read_u32()?; - let hi = self.read_u32()?; - Ok((hi as u64) << 32 | lo as u64) - } - - fn read_bool(&mut self) -> DecodeResult { - match self.read_byte()? { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(DecodeError::MalformedBinary), - } - } - - /// Read a LEB128 encoded 32-bits unsigned integer - fn read_vu32(&mut self) -> DecodeResult { - let mut res = 0; - let mut shift = 0; - loop { - let b = self.read_byte()?; - - // forbid unused bits - if shift >= 32 - 32 % 7 && (b & 0x7f >= 1 << (32 % 7)) { - return Err(DecodeError::MalformedBinary); - } - - res |= ((b & 0x7f) as u32) << shift; - if b & 0x80 == 0 { - return Ok(res); - } - shift += 7; - - if shift >= 32 { - return Err(DecodeError::MalformedBinary); - } - } - } - - /// Read a LEB128 encoded 32-bits signed integer - fn read_vs32(&mut self) -> DecodeResult { - let mut res = 0; - let mut shift = 0; - loop { - let b = self.read_byte()?; - - // forbid unused bits - if shift >= 32 - 32 % 7 { - let mask = ((-1 << (32 % 7) - 1) & 0x7f) as u8; - if b & mask != 0 && b & mask != mask { - return Err(DecodeError::MalformedBinary); - } - } - - res |= ((b & 0x7f) as i32) << shift; - shift += 7; - if b & 0x80 == 0 { - if shift < 32 && (b & 0x40 != 0) { - res |= -1 << shift; - } - return Ok(res); - } - - if shift >= 32 { - return Err(DecodeError::MalformedBinary); - } - } - } - - /// Read a LEB128 encoded 64-bits signed integer - fn read_vs64(&mut self) -> DecodeResult { - let mut res = 0; - let mut shift = 0; - loop { - let b = self.read_byte()?; - - // forbid unused bits - if shift >= 64 - 64 % 7 { - let mask = 0x7fu8; - if b & mask != 0 && b & mask != mask { - return Err(DecodeError::MalformedBinary); - } - } - - res |= ((b & 0x7f) as i64) << shift; - shift += 7; - if b & 0x80 == 0 { - if shift < 64 && (b & 0x40 != 0) { - res |= -1 << shift; - } - return Ok(res); - } - - if shift >= 64 { - return Err(DecodeError::MalformedBinary); - } - } - } - - fn read_f32(&mut self) -> DecodeResult { - Ok(self.read_u32()?.reinterpret()) - } - - fn read_f64(&mut self) -> DecodeResult { - Ok(self.read_u64()?.reinterpret()) - } - - fn read_vec(&mut self, read_elem: F) -> DecodeResult> - where F: Fn(&mut Decoder) -> DecodeResult - { - let n = self.read_vu32()?; - let mut vec = Vec::with_capacity(n as usize); - for _ in 0..n { - vec.push(read_elem(self)?); - } - Ok(vec) - } - - fn read_name(&mut self) -> DecodeResult { - let vec = self.read_vec(Decoder::read_byte)?; - match String::from_utf8(vec) { - Ok(s) => Ok(s), - Err(_) => Err(DecodeError::MalformedBinary), - } - } - - fn read_value_type(&mut self) -> DecodeResult { - decode_value_type(self.read_byte()?) - } - - fn read_block_type(&mut self) -> DecodeResult> { - Ok(match self.read_byte()? { - 0x40 => vec![], - b => vec![decode_value_type(b)?], - }) - } - - fn read_func_type(&mut self) -> DecodeResult { - if self.read_byte()? != 0x60 { - return Err(DecodeError::MalformedBinary); - } - - let args = self.read_vec(Decoder::read_value_type)?; - let result = self.read_vec(Decoder::read_value_type)?; - - Ok(types::Func { args, result }) - } - - fn read_limits(&mut self) -> DecodeResult { - let has_max = self.read_bool()?; - let min = self.read_vu32()?; - let max = if has_max { - Some(self.read_vu32()?) - } else { - None - }; - - Ok(types::Limits { min, max }) - } - - fn read_memory_type(&mut self) -> DecodeResult { - Ok(types::Memory { limits: self.read_limits()? }) - } - - fn read_elem_type(&mut self) -> DecodeResult { - if self.read_byte()? != 0x70 { - return Err(DecodeError::MalformedBinary); - } - - Ok(types::Elem::AnyFunc) - } - - fn read_table_type(&mut self) -> DecodeResult { - let elem = self.read_elem_type()?; - let limits = self.read_limits()?; - Ok(types::Table { limits, elem }) - } - - fn read_global_type(&mut self) -> DecodeResult { - let value = self.read_value_type()?; - let mutable = self.read_bool()?; - Ok(types::Global { value, mutable }) - } - - fn read_index(&mut self) -> DecodeResult { - self.read_vu32() - } - - fn read_meta_instr(&mut self) -> DecodeResult { - use ast::Instr::*; - use types::Value::*; - use types::Int::*; - use types::Float::*; - - let opcode = self.read_byte()?; - - Ok(MetaInstr::Instr(match opcode { - 0x00 => Unreachable, - 0x01 => Nop, - - 0x02 => { - let block_type = self.read_block_type()?; - let instrs = self.read_instr_block()?; - Block(block_type, instrs) - }, - 0x03 => { - let block_type = self.read_block_type()?; - let instrs = self.read_instr_block()?; - Loop(block_type, instrs) - }, - 0x04 => { - let block_type = self.read_block_type()?; - let (instrs1, delim) = self.read_instr_block_with_delim()?; - let instrs2 = match delim { - PseudoInstr::End => Vec::new(), - PseudoInstr::Else => self.read_instr_block()?, - }; - If(block_type, instrs1, instrs2) - }, - - 0x05 => return Ok(MetaInstr::PseudoInstr(PseudoInstr::Else)), - 0x0b => return Ok(MetaInstr::PseudoInstr(PseudoInstr::End)), - - 0x0c => Br(self.read_index()?), - 0x0d => BrIf(self.read_index()?), - 0x0e => BrTable(self.read_vec(Decoder::read_index)?, self.read_index()?), - 0x0f => Return, - - 0x10 => Call(self.read_index()?), - 0x11 => { - let index = self.read_index()?; - if self.read_byte()? != 0 { - return Err(DecodeError::MalformedBinary); - } - CallIndirect(index) - }, - - 0x1a => Drop_, - 0x1b => Select, - - 0x20 => GetLocal(self.read_index()?), - 0x21 => SetLocal(self.read_index()?), - 0x22 => TeeLocal(self.read_index()?), - 0x23 => GetGlobal(self.read_index()?), - 0x24 => SetGlobal(self.read_index()?), - - 0x28 => self.read_load_op(Int(I32), None)?, - 0x29 => self.read_load_op(Int(I64), None)?, - 0x2a => self.read_load_op(Float(F32), None)?, - 0x2b => self.read_load_op(Float(F64), None)?, - 0x2c => self.read_load_op(Int(I32), Some((8, true)))?, - 0x2d => self.read_load_op(Int(I32), Some((8, false)))?, - 0x2e => self.read_load_op(Int(I32), Some((16, true)))?, - 0x2f => self.read_load_op(Int(I32), Some((16, false)))?, - 0x30 => self.read_load_op(Int(I64), Some((8, true)))?, - 0x31 => self.read_load_op(Int(I64), Some((8, false)))?, - 0x32 => self.read_load_op(Int(I64), Some((16, true)))?, - 0x33 => self.read_load_op(Int(I64), Some((16, false)))?, - 0x34 => self.read_load_op(Int(I64), Some((32, true)))?, - 0x35 => self.read_load_op(Int(I64), Some((32, false)))?, - - 0x36 => self.read_store_op(Int(I32), None)?, - 0x37 => self.read_store_op(Int(I64), None)?, - 0x38 => self.read_store_op(Float(F32), None)?, - 0x39 => self.read_store_op(Float(F64), None)?, - 0x3a => self.read_store_op(Int(I32), Some(8))?, - 0x3b => self.read_store_op(Int(I32), Some(16))?, - 0x3c => self.read_store_op(Int(I64), Some(8))?, - 0x3d => self.read_store_op(Int(I64), Some(16))?, - 0x3e => self.read_store_op(Int(I64), Some(32))?, - - 0x3f => { - if self.read_byte()? != 0 { - return Err(DecodeError::MalformedBinary); - } - CurrentMemory - }, - 0x40 => { - if self.read_byte()? != 0 { - return Err(DecodeError::MalformedBinary); - } - GrowMemory - }, - - 0x41 => Const(Value::from_i32(self.read_vs32()?)), - 0x42 => Const(Value::from_i64(self.read_vs64()?)), - 0x43 => Const(Value::F32(self.read_f32()?)), - 0x44 => Const(Value::F64(self.read_f64()?)), - - 0x45 => ITest(I32, ITestOp::Eqz), - 0x46 => IRel(I32, IRelOp::Eq_), - 0x47 => IRel(I32, IRelOp::Ne), - 0x48 => IRel(I32, IRelOp::LtS), - 0x49 => IRel(I32, IRelOp::LtU), - 0x4a => IRel(I32, IRelOp::GtS), - 0x4b => IRel(I32, IRelOp::GtU), - 0x4c => IRel(I32, IRelOp::LeS), - 0x4d => IRel(I32, IRelOp::LeU), - 0x4e => IRel(I32, IRelOp::GeS), - 0x4f => IRel(I32, IRelOp::GeU), - - 0x50 => ITest(I64, ITestOp::Eqz), - 0x51 => IRel(I64, IRelOp::Eq_), - 0x52 => IRel(I64, IRelOp::Ne), - 0x53 => IRel(I64, IRelOp::LtS), - 0x54 => IRel(I64, IRelOp::LtU), - 0x55 => IRel(I64, IRelOp::GtS), - 0x56 => IRel(I64, IRelOp::GtU), - 0x57 => IRel(I64, IRelOp::LeS), - 0x58 => IRel(I64, IRelOp::LeU), - 0x59 => IRel(I64, IRelOp::GeS), - 0x5a => IRel(I64, IRelOp::GeU), - - 0x5b => FRel(F32, FRelOp::Eq_), - 0x5c => FRel(F32, FRelOp::Ne), - 0x5d => FRel(F32, FRelOp::Lt), - 0x5e => FRel(F32, FRelOp::Gt), - 0x5f => FRel(F32, FRelOp::Le), - 0x60 => FRel(F32, FRelOp::Ge), - - 0x61 => FRel(F64, FRelOp::Eq_), - 0x62 => FRel(F64, FRelOp::Ne), - 0x63 => FRel(F64, FRelOp::Lt), - 0x64 => FRel(F64, FRelOp::Gt), - 0x65 => FRel(F64, FRelOp::Le), - 0x66 => FRel(F64, FRelOp::Ge), - - 0x67 => IUnary(I32, IUnOp::Clz), - 0x68 => IUnary(I32, IUnOp::Ctz), - 0x69 => IUnary(I32, IUnOp::Popcnt), - 0x6a => IBin(I32, IBinOp::Add), - 0x6b => IBin(I32, IBinOp::Sub), - 0x6c => IBin(I32, IBinOp::Mul), - 0x6d => IBin(I32, IBinOp::DivS), - 0x6e => IBin(I32, IBinOp::DivU), - 0x6f => IBin(I32, IBinOp::RemS), - 0x70 => IBin(I32, IBinOp::RemU), - 0x71 => IBin(I32, IBinOp::And), - 0x72 => IBin(I32, IBinOp::Or), - 0x73 => IBin(I32, IBinOp::Xor), - 0x74 => IBin(I32, IBinOp::Shl), - 0x75 => IBin(I32, IBinOp::ShrS), - 0x76 => IBin(I32, IBinOp::ShrU), - 0x77 => IBin(I32, IBinOp::Rotl), - 0x78 => IBin(I32, IBinOp::Rotr), - - 0x79 => IUnary(I64, IUnOp::Clz), - 0x7a => IUnary(I64, IUnOp::Ctz), - 0x7b => IUnary(I64, IUnOp::Popcnt), - 0x7c => IBin(I64, IBinOp::Add), - 0x7d => IBin(I64, IBinOp::Sub), - 0x7e => IBin(I64, IBinOp::Mul), - 0x7f => IBin(I64, IBinOp::DivS), - 0x80 => IBin(I64, IBinOp::DivU), - 0x81 => IBin(I64, IBinOp::RemS), - 0x82 => IBin(I64, IBinOp::RemU), - 0x83 => IBin(I64, IBinOp::And), - 0x84 => IBin(I64, IBinOp::Or), - 0x85 => IBin(I64, IBinOp::Xor), - 0x86 => IBin(I64, IBinOp::Shl), - 0x87 => IBin(I64, IBinOp::ShrS), - 0x88 => IBin(I64, IBinOp::ShrU), - 0x89 => IBin(I64, IBinOp::Rotl), - 0x8a => IBin(I64, IBinOp::Rotr), - - 0x8b => FUnary(F32, FUnOp::Abs), - 0x8c => FUnary(F32, FUnOp::Neg), - 0x8d => FUnary(F32, FUnOp::Ceil), - 0x8e => FUnary(F32, FUnOp::Floor), - 0x8f => FUnary(F32, FUnOp::Trunc), - 0x90 => FUnary(F32, FUnOp::Nearest), - 0x91 => FUnary(F32, FUnOp::Sqrt), - 0x92 => FBin(F32, FBinOp::Add), - 0x93 => FBin(F32, FBinOp::Sub), - 0x94 => FBin(F32, FBinOp::Mul), - 0x95 => FBin(F32, FBinOp::Div), - 0x96 => FBin(F32, FBinOp::Min), - 0x97 => FBin(F32, FBinOp::Max), - 0x98 => FBin(F32, FBinOp::CopySign), - - 0x99 => FUnary(F64, FUnOp::Abs), - 0x9a => FUnary(F64, FUnOp::Neg), - 0x9b => FUnary(F64, FUnOp::Ceil), - 0x9c => FUnary(F64, FUnOp::Floor), - 0x9d => FUnary(F64, FUnOp::Trunc), - 0x9e => FUnary(F64, FUnOp::Nearest), - 0x9f => FUnary(F64, FUnOp::Sqrt), - 0xa0 => FBin(F64, FBinOp::Add), - 0xa1 => FBin(F64, FBinOp::Sub), - 0xa2 => FBin(F64, FBinOp::Mul), - 0xa3 => FBin(F64, FBinOp::Div), - 0xa4 => FBin(F64, FBinOp::Min), - 0xa5 => FBin(F64, FBinOp::Max), - 0xa6 => FBin(F64, FBinOp::CopySign), - - 0xa7 => Convert(ConvertOp::I32WrapI64), - 0xa8 => Convert(ConvertOp::Trunc { from: F32, to: I32, signed: true }), - 0xa9 => Convert(ConvertOp::Trunc { from: F32, to: I32, signed: false }), - 0xaa => Convert(ConvertOp::Trunc { from: F64, to: I32, signed: true }), - 0xab => Convert(ConvertOp::Trunc { from: F64, to: I32, signed: false }), - 0xac => Convert(ConvertOp::I64ExtendSI32), - 0xad => Convert(ConvertOp::I64ExtendUI32), - 0xae => Convert(ConvertOp::Trunc { from: F32, to: I64, signed: true }), - 0xaf => Convert(ConvertOp::Trunc { from: F32, to: I64, signed: false }), - 0xb0 => Convert(ConvertOp::Trunc { from: F64, to: I64, signed: true }), - 0xb1 => Convert(ConvertOp::Trunc { from: F64, to: I64, signed: false }), - 0xb2 => Convert(ConvertOp::Convert { from: I32, to: F32, signed: true }), - 0xb3 => Convert(ConvertOp::Convert { from: I32, to: F32, signed: false }), - 0xb4 => Convert(ConvertOp::Convert { from: I64, to: F32, signed: true }), - 0xb5 => Convert(ConvertOp::Convert { from: I64, to: F32, signed: false }), - 0xb6 => Convert(ConvertOp::F32DemoteF64), - 0xb7 => Convert(ConvertOp::Convert { from: I32, to: F64, signed: true }), - 0xb8 => Convert(ConvertOp::Convert { from: I32, to: F64, signed: false }), - 0xb9 => Convert(ConvertOp::Convert { from: I64, to: F64, signed: true }), - 0xba => Convert(ConvertOp::Convert { from: I64, to: F64, signed: false }), - 0xbb => Convert(ConvertOp::F64PromoteF32), - - 0xbc => Convert(ConvertOp::Reinterpret { from: Float(F32), to: Int(I32) }), - 0xbd => Convert(ConvertOp::Reinterpret { from: Float(F64), to: Int(I64) }), - 0xbe => Convert(ConvertOp::Reinterpret { from: Int(I32), to: Float(F32) }), - 0xbf => Convert(ConvertOp::Reinterpret { from: Int(I64), to: Float(F64) }), - - _ => return Err(DecodeError::MalformedBinary), - })) - } - - fn read_instr_block_with_delim(&mut self) -> DecodeResult<(Vec, PseudoInstr)> { - let mut res = Vec::new(); - loop { - match self.read_meta_instr()? { - MetaInstr::PseudoInstr(delim) => return Ok((res, delim)), - MetaInstr::Instr(instr) => res.push(instr), - } - } - } - - fn read_instr_block(&mut self) -> DecodeResult> { - let (instrs, delim) = self.read_instr_block_with_delim()?; - match delim { - PseudoInstr::Else => Err(DecodeError::MalformedBinary), - PseudoInstr::End => Ok(instrs), - } - } - - fn read_expr(&mut self) -> DecodeResult> { - self.read_instr_block() - } - - fn read_load_op(&mut self, type_: types::Value, opt: Option<(u32, bool)>) -> DecodeResult { - let align = self.read_vu32()?; - let offset = self.read_vu32()?; - Ok(Instr::Load(LoadOp { align, offset, type_, opt })) - } - - fn read_store_op(&mut self, type_: types::Value, opt: Option) -> DecodeResult { - let align = self.read_vu32()?; - let offset = self.read_vu32()?; - Ok(Instr::Store(StoreOp { align, offset, type_, opt })) - } - - fn skip_custom_section(&mut self, size: u32) -> DecodeResult<()> { - // Even if we ignore custom sections, we must ensure that its name is valid utf8. - let start_pos = self.pos; - let _ = self.read_name()?; - let nread = self.pos - start_pos; - // ensure that we didn't read more than the declared size of the section - if nread > size as usize { - return Err(DecodeError::MalformedBinary); - } - let nskip = size as usize - nread; - - for _ in 0..nskip { - let _ = self.read_byte()?; - } - - Ok(()) - } - - fn read_type_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_func_type) - } - - fn read_import_desc(&mut self) -> DecodeResult { - match self.read_byte()? { - 0x00 => Ok(ImportDesc::Func(self.read_index()?)), - 0x01 => Ok(ImportDesc::Table(self.read_table_type()?)), - 0x02 => Ok(ImportDesc::Memory(self.read_memory_type()?)), - 0x03 => Ok(ImportDesc::Global(self.read_global_type()?)), - _ => Err(DecodeError::MalformedBinary), - } - } - - fn read_import(&mut self) -> DecodeResult { - let module = self.read_name()?; - let name = self.read_name()?; - let desc = self.read_import_desc()?; - Ok(Import { module, name, desc }) - } - - fn read_import_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_import) - } - - fn read_func_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_index) - } - - fn read_table(&mut self) -> DecodeResult
{ - Ok(Table { type_: self.read_table_type()? }) - } - - fn read_table_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_table) - } - - fn read_memory(&mut self) -> DecodeResult { - Ok(Memory { type_: self.read_memory_type()? }) - } - - fn read_memory_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_memory) - } - - fn read_global(&mut self) -> DecodeResult { - let type_ = self.read_global_type()?; - let value = self.read_expr()?; - Ok(Global { type_, value }) - } - - fn read_global_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_global) - } - - fn read_export_desc(&mut self) -> DecodeResult { - match self.read_byte()? { - 0x00 => Ok(ExportDesc::Func(self.read_index()?)), - 0x01 => Ok(ExportDesc::Table(self.read_index()?)), - 0x02 => Ok(ExportDesc::Memory(self.read_index()?)), - 0x03 => Ok(ExportDesc::Global(self.read_index()?)), - _ => Err(DecodeError::MalformedBinary), - } - } - - fn read_export(&mut self) -> DecodeResult { - let name = self.read_name()?; - let desc = self.read_export_desc()?; - Ok(Export { name, desc }) - } - - fn read_export_section(&mut self) -> DecodeResult> { - self.read_vec(Decoder::read_export) - } - - fn read_start_section(&mut self) -> DecodeResult> { - Ok(Some(self.read_index()?)) - } - - fn read_segment(&mut self, read_elem: F) -> DecodeResult> - where F: Fn(&mut Decoder) -> DecodeResult - { - let index = self.read_index()?; - let offset = self.read_expr()?; - let init = self.read_vec(read_elem)?; - Ok(Segment { index, offset, init }) - } - - fn read_elem(&mut self) -> DecodeResult> { - self.read_segment(Decoder::read_index) - } - - fn read_elem_section(&mut self) -> DecodeResult>> { - self.read_vec(Decoder::read_elem) - } - - fn read_local_decl(&mut self) -> DecodeResult<(u32, types::Value)> { - let n = self.read_vu32()?; - let t = self.read_value_type()?; - Ok((n, t)) - } - - fn read_code(&mut self) -> DecodeResult<(Vec, Expr)> { - let _size = self.read_vu32()?; - let local_decls = self.read_vec(Decoder::read_local_decl)?; - let n_total: u64 = local_decls.iter().map(|&(n, _)| n as u64).sum(); - if n_total > std::u32::MAX as u64 { - return Err(DecodeError::MalformedBinary); - } - - let mut locals = Vec::new(); - locals.reserve(n_total as usize); - for (n, t) in local_decls { - locals.extend(std::iter::repeat(t).take(n as usize)); - } - - let body = self.read_expr()?; - Ok((locals, body)) - } - - fn read_code_section(&mut self) -> DecodeResult, Expr)>> { - self.read_vec(Decoder::read_code) - } - - fn read_data(&mut self) -> DecodeResult> { - self.read_segment(Decoder::read_byte) - } - - fn read_data_section(&mut self) -> DecodeResult>> { - self.read_vec(Decoder::read_data) - } - - fn read_module(&mut self) -> DecodeResult { - if self.read_u32()? != MAGIC { - return Err(DecodeError::MalformedBinary); - } - if self.read_u32()? != VERSION { - return Err(DecodeError::MalformedBinary); - } - - let mut types = Vec::new(); - let mut imports = Vec::new(); - let mut func_types = Vec::new(); - let mut tables = Vec::new(); - let mut memories = Vec::new(); - let mut globals = Vec::new(); - let mut exports = Vec::new(); - let mut start = None; - let mut elems = Vec::new(); - let mut func_bodies = Vec::new(); - let mut data = Vec::new(); - - // All sections are optional and they are ordered according to their - // id, except for custom section (id=0) that may appear anywhere. - - let mut last_id = 0; - loop { - match self.read_byte() { - Err(DecodeError::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => break, - Err(e) => return Err(e), - Ok(id) => { - if id != 0 && id <= last_id { - return Err(DecodeError::MalformedBinary); - } - last_id = id; - - let size = self.read_vu32()?; - - match id { - 0 => self.skip_custom_section(size)?, // ignore custom sections - 1 => types = self.read_type_section()?, - 2 => imports = self.read_import_section()?, - 3 => func_types = self.read_func_section()?, - 4 => tables = self.read_table_section()?, - 5 => memories = self.read_memory_section()?, - 6 => globals = self.read_global_section()?, - 7 => exports = self.read_export_section()?, - 8 => start = self.read_start_section()?, - 9 => elems = self.read_elem_section()?, - 10 => func_bodies = self.read_code_section()?, - 11 => data = self.read_data_section()?, - _ => return Err(DecodeError::MalformedBinary), - } - } - } - } - - if func_types.len() != func_bodies.len() { - return Err(DecodeError::MalformedBinary); - } - - // pairwise merge function types and code - let funcs = func_types.into_iter() - .zip(func_bodies) - .map(|(type_index, (locals, body))| Func { type_index, locals, body }) - .collect(); - - Ok(Module { types, funcs, tables, memories, globals, elems, data, start, imports, exports }) - } + fn read_byte(&mut self) -> DecodeResult { + let mut buf = [0]; + self.reader.read_exact(&mut buf)?; + self.pos += 1; + Ok(buf[0]) + } + + fn read_u16(&mut self) -> DecodeResult { + let lo = self.read_byte()?; + let hi = self.read_byte()?; + Ok((hi as u16) << 8 | lo as u16) + } + + fn read_u32(&mut self) -> DecodeResult { + let lo = self.read_u16()?; + let hi = self.read_u16()?; + Ok((hi as u32) << 16 | lo as u32) + } + + fn read_u64(&mut self) -> DecodeResult { + let lo = self.read_u32()?; + let hi = self.read_u32()?; + Ok((hi as u64) << 32 | lo as u64) + } + + fn read_bool(&mut self) -> DecodeResult { + match self.read_byte()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(DecodeError::MalformedBinary), + } + } + + /// Read a LEB128 encoded 32-bits unsigned integer + fn read_vu32(&mut self) -> DecodeResult { + let mut res = 0; + let mut shift = 0; + loop { + let b = self.read_byte()?; + + // forbid unused bits + if shift >= 32 - 32 % 7 && (b & 0x7f >= 1 << (32 % 7)) { + return Err(DecodeError::MalformedBinary); + } + + res |= ((b & 0x7f) as u32) << shift; + if b & 0x80 == 0 { + return Ok(res); + } + shift += 7; + + if shift >= 32 { + return Err(DecodeError::MalformedBinary); + } + } + } + + /// Read a LEB128 encoded 32-bits signed integer + fn read_vs32(&mut self) -> DecodeResult { + let mut res = 0; + let mut shift = 0; + loop { + let b = self.read_byte()?; + + // forbid unused bits + if shift >= 32 - 32 % 7 { + let mask = ((-1 << ((32 % 7) - 1)) & 0x7f) as u8; + if b & mask != 0 && b & mask != mask { + return Err(DecodeError::MalformedBinary); + } + } + + res |= ((b & 0x7f) as i32) << shift; + shift += 7; + if b & 0x80 == 0 { + if shift < 32 && (b & 0x40 != 0) { + res |= -1 << shift; + } + return Ok(res); + } + + if shift >= 32 { + return Err(DecodeError::MalformedBinary); + } + } + } + + /// Read a LEB128 encoded 64-bits signed integer + fn read_vs64(&mut self) -> DecodeResult { + let mut res = 0; + let mut shift = 0; + loop { + let b = self.read_byte()?; + + // forbid unused bits + if shift >= 64 - 64 % 7 { + let mask = 0x7fu8; + if b & mask != 0 && b & mask != mask { + return Err(DecodeError::MalformedBinary); + } + } + + res |= ((b & 0x7f) as i64) << shift; + shift += 7; + if b & 0x80 == 0 { + if shift < 64 && (b & 0x40 != 0) { + res |= -1 << shift; + } + return Ok(res); + } + + if shift >= 64 { + return Err(DecodeError::MalformedBinary); + } + } + } + + fn read_f32(&mut self) -> DecodeResult { + Ok(self.read_u32()?.reinterpret()) + } + + fn read_f64(&mut self) -> DecodeResult { + Ok(self.read_u64()?.reinterpret()) + } + + fn read_vec(&mut self, read_elem: F) -> DecodeResult> + where + F: Fn(&mut Decoder) -> DecodeResult, + { + let n = self.read_vu32()?; + let mut vec = Vec::with_capacity(n as usize); + for _ in 0..n { + vec.push(read_elem(self)?); + } + Ok(vec) + } + + fn read_name(&mut self) -> DecodeResult { + let vec = self.read_vec(Decoder::read_byte)?; + match String::from_utf8(vec) { + Ok(s) => Ok(s), + Err(_) => Err(DecodeError::MalformedBinary), + } + } + + fn read_value_type(&mut self) -> DecodeResult { + decode_value_type(self.read_byte()?) + } + + fn read_block_type(&mut self) -> DecodeResult> { + Ok(match self.read_byte()? { + 0x40 => vec![], + b => vec![decode_value_type(b)?], + }) + } + + fn read_func_type(&mut self) -> DecodeResult { + if self.read_byte()? != 0x60 { + return Err(DecodeError::MalformedBinary); + } + + let args = self.read_vec(Decoder::read_value_type)?; + let result = self.read_vec(Decoder::read_value_type)?; + + Ok(types::Func { args, result }) + } + + fn read_limits(&mut self) -> DecodeResult { + let has_max = self.read_bool()?; + let min = self.read_vu32()?; + let max = if has_max { + Some(self.read_vu32()?) + } else { + None + }; + + Ok(types::Limits { min, max }) + } + + fn read_memory_type(&mut self) -> DecodeResult { + Ok(types::Memory { + limits: self.read_limits()?, + }) + } + + fn read_elem_type(&mut self) -> DecodeResult { + if self.read_byte()? != 0x70 { + return Err(DecodeError::MalformedBinary); + } + + Ok(types::Elem::AnyFunc) + } + + fn read_table_type(&mut self) -> DecodeResult { + let elem = self.read_elem_type()?; + let limits = self.read_limits()?; + Ok(types::Table { limits, elem }) + } + + fn read_global_type(&mut self) -> DecodeResult { + let value = self.read_value_type()?; + let mutable = self.read_bool()?; + Ok(types::Global { value, mutable }) + } + + fn read_index(&mut self) -> DecodeResult { + self.read_vu32() + } + + fn read_meta_instr(&mut self) -> DecodeResult { + use ast::Instr::*; + use types::Float::*; + use types::Int::*; + use types::Value::*; + + let opcode = self.read_byte()?; + + Ok(MetaInstr::Instr(match opcode { + 0x00 => Unreachable, + 0x01 => Nop, + + 0x02 => { + let block_type = self.read_block_type()?; + let instrs = self.read_instr_block()?; + Block(block_type, instrs) + } + 0x03 => { + let block_type = self.read_block_type()?; + let instrs = self.read_instr_block()?; + Loop(block_type, instrs) + } + 0x04 => { + let block_type = self.read_block_type()?; + let (instrs1, delim) = self.read_instr_block_with_delim()?; + let instrs2 = match delim { + PseudoInstr::End => Vec::new(), + PseudoInstr::Else => self.read_instr_block()?, + }; + If(block_type, instrs1, instrs2) + } + + 0x05 => return Ok(MetaInstr::PseudoInstr(PseudoInstr::Else)), + 0x0b => return Ok(MetaInstr::PseudoInstr(PseudoInstr::End)), + + 0x0c => Br(self.read_index()?), + 0x0d => BrIf(self.read_index()?), + 0x0e => BrTable(self.read_vec(Decoder::read_index)?, self.read_index()?), + 0x0f => Return, + + 0x10 => Call(self.read_index()?), + 0x11 => { + let index = self.read_index()?; + if self.read_byte()? != 0 { + return Err(DecodeError::MalformedBinary); + } + CallIndirect(index) + } + + 0x1a => Drop_, + 0x1b => Select, + + 0x20 => GetLocal(self.read_index()?), + 0x21 => SetLocal(self.read_index()?), + 0x22 => TeeLocal(self.read_index()?), + 0x23 => GetGlobal(self.read_index()?), + 0x24 => SetGlobal(self.read_index()?), + + 0x28 => self.read_load_op(Int(I32), None)?, + 0x29 => self.read_load_op(Int(I64), None)?, + 0x2a => self.read_load_op(Float(F32), None)?, + 0x2b => self.read_load_op(Float(F64), None)?, + 0x2c => self.read_load_op(Int(I32), Some((8, true)))?, + 0x2d => self.read_load_op(Int(I32), Some((8, false)))?, + 0x2e => self.read_load_op(Int(I32), Some((16, true)))?, + 0x2f => self.read_load_op(Int(I32), Some((16, false)))?, + 0x30 => self.read_load_op(Int(I64), Some((8, true)))?, + 0x31 => self.read_load_op(Int(I64), Some((8, false)))?, + 0x32 => self.read_load_op(Int(I64), Some((16, true)))?, + 0x33 => self.read_load_op(Int(I64), Some((16, false)))?, + 0x34 => self.read_load_op(Int(I64), Some((32, true)))?, + 0x35 => self.read_load_op(Int(I64), Some((32, false)))?, + + 0x36 => self.read_store_op(Int(I32), None)?, + 0x37 => self.read_store_op(Int(I64), None)?, + 0x38 => self.read_store_op(Float(F32), None)?, + 0x39 => self.read_store_op(Float(F64), None)?, + 0x3a => self.read_store_op(Int(I32), Some(8))?, + 0x3b => self.read_store_op(Int(I32), Some(16))?, + 0x3c => self.read_store_op(Int(I64), Some(8))?, + 0x3d => self.read_store_op(Int(I64), Some(16))?, + 0x3e => self.read_store_op(Int(I64), Some(32))?, + + 0x3f => { + if self.read_byte()? != 0 { + return Err(DecodeError::MalformedBinary); + } + CurrentMemory + } + 0x40 => { + if self.read_byte()? != 0 { + return Err(DecodeError::MalformedBinary); + } + GrowMemory + } + + 0x41 => Const(Value::from_i32(self.read_vs32()?)), + 0x42 => Const(Value::from_i64(self.read_vs64()?)), + 0x43 => Const(Value::F32(self.read_f32()?)), + 0x44 => Const(Value::F64(self.read_f64()?)), + + 0x45 => ITest(I32, ITestOp::Eqz), + 0x46 => IRel(I32, IRelOp::Eq_), + 0x47 => IRel(I32, IRelOp::Ne), + 0x48 => IRel(I32, IRelOp::LtS), + 0x49 => IRel(I32, IRelOp::LtU), + 0x4a => IRel(I32, IRelOp::GtS), + 0x4b => IRel(I32, IRelOp::GtU), + 0x4c => IRel(I32, IRelOp::LeS), + 0x4d => IRel(I32, IRelOp::LeU), + 0x4e => IRel(I32, IRelOp::GeS), + 0x4f => IRel(I32, IRelOp::GeU), + + 0x50 => ITest(I64, ITestOp::Eqz), + 0x51 => IRel(I64, IRelOp::Eq_), + 0x52 => IRel(I64, IRelOp::Ne), + 0x53 => IRel(I64, IRelOp::LtS), + 0x54 => IRel(I64, IRelOp::LtU), + 0x55 => IRel(I64, IRelOp::GtS), + 0x56 => IRel(I64, IRelOp::GtU), + 0x57 => IRel(I64, IRelOp::LeS), + 0x58 => IRel(I64, IRelOp::LeU), + 0x59 => IRel(I64, IRelOp::GeS), + 0x5a => IRel(I64, IRelOp::GeU), + + 0x5b => FRel(F32, FRelOp::Eq_), + 0x5c => FRel(F32, FRelOp::Ne), + 0x5d => FRel(F32, FRelOp::Lt), + 0x5e => FRel(F32, FRelOp::Gt), + 0x5f => FRel(F32, FRelOp::Le), + 0x60 => FRel(F32, FRelOp::Ge), + + 0x61 => FRel(F64, FRelOp::Eq_), + 0x62 => FRel(F64, FRelOp::Ne), + 0x63 => FRel(F64, FRelOp::Lt), + 0x64 => FRel(F64, FRelOp::Gt), + 0x65 => FRel(F64, FRelOp::Le), + 0x66 => FRel(F64, FRelOp::Ge), + + 0x67 => IUnary(I32, IUnOp::Clz), + 0x68 => IUnary(I32, IUnOp::Ctz), + 0x69 => IUnary(I32, IUnOp::Popcnt), + 0x6a => IBin(I32, IBinOp::Add), + 0x6b => IBin(I32, IBinOp::Sub), + 0x6c => IBin(I32, IBinOp::Mul), + 0x6d => IBin(I32, IBinOp::DivS), + 0x6e => IBin(I32, IBinOp::DivU), + 0x6f => IBin(I32, IBinOp::RemS), + 0x70 => IBin(I32, IBinOp::RemU), + 0x71 => IBin(I32, IBinOp::And), + 0x72 => IBin(I32, IBinOp::Or), + 0x73 => IBin(I32, IBinOp::Xor), + 0x74 => IBin(I32, IBinOp::Shl), + 0x75 => IBin(I32, IBinOp::ShrS), + 0x76 => IBin(I32, IBinOp::ShrU), + 0x77 => IBin(I32, IBinOp::Rotl), + 0x78 => IBin(I32, IBinOp::Rotr), + + 0x79 => IUnary(I64, IUnOp::Clz), + 0x7a => IUnary(I64, IUnOp::Ctz), + 0x7b => IUnary(I64, IUnOp::Popcnt), + 0x7c => IBin(I64, IBinOp::Add), + 0x7d => IBin(I64, IBinOp::Sub), + 0x7e => IBin(I64, IBinOp::Mul), + 0x7f => IBin(I64, IBinOp::DivS), + 0x80 => IBin(I64, IBinOp::DivU), + 0x81 => IBin(I64, IBinOp::RemS), + 0x82 => IBin(I64, IBinOp::RemU), + 0x83 => IBin(I64, IBinOp::And), + 0x84 => IBin(I64, IBinOp::Or), + 0x85 => IBin(I64, IBinOp::Xor), + 0x86 => IBin(I64, IBinOp::Shl), + 0x87 => IBin(I64, IBinOp::ShrS), + 0x88 => IBin(I64, IBinOp::ShrU), + 0x89 => IBin(I64, IBinOp::Rotl), + 0x8a => IBin(I64, IBinOp::Rotr), + + 0x8b => FUnary(F32, FUnOp::Abs), + 0x8c => FUnary(F32, FUnOp::Neg), + 0x8d => FUnary(F32, FUnOp::Ceil), + 0x8e => FUnary(F32, FUnOp::Floor), + 0x8f => FUnary(F32, FUnOp::Trunc), + 0x90 => FUnary(F32, FUnOp::Nearest), + 0x91 => FUnary(F32, FUnOp::Sqrt), + 0x92 => FBin(F32, FBinOp::Add), + 0x93 => FBin(F32, FBinOp::Sub), + 0x94 => FBin(F32, FBinOp::Mul), + 0x95 => FBin(F32, FBinOp::Div), + 0x96 => FBin(F32, FBinOp::Min), + 0x97 => FBin(F32, FBinOp::Max), + 0x98 => FBin(F32, FBinOp::CopySign), + + 0x99 => FUnary(F64, FUnOp::Abs), + 0x9a => FUnary(F64, FUnOp::Neg), + 0x9b => FUnary(F64, FUnOp::Ceil), + 0x9c => FUnary(F64, FUnOp::Floor), + 0x9d => FUnary(F64, FUnOp::Trunc), + 0x9e => FUnary(F64, FUnOp::Nearest), + 0x9f => FUnary(F64, FUnOp::Sqrt), + 0xa0 => FBin(F64, FBinOp::Add), + 0xa1 => FBin(F64, FBinOp::Sub), + 0xa2 => FBin(F64, FBinOp::Mul), + 0xa3 => FBin(F64, FBinOp::Div), + 0xa4 => FBin(F64, FBinOp::Min), + 0xa5 => FBin(F64, FBinOp::Max), + 0xa6 => FBin(F64, FBinOp::CopySign), + + 0xa7 => Convert(ConvertOp::I32WrapI64), + 0xa8 => Convert(ConvertOp::Trunc { + from: F32, + to: I32, + signed: true, + }), + 0xa9 => Convert(ConvertOp::Trunc { + from: F32, + to: I32, + signed: false, + }), + 0xaa => Convert(ConvertOp::Trunc { + from: F64, + to: I32, + signed: true, + }), + 0xab => Convert(ConvertOp::Trunc { + from: F64, + to: I32, + signed: false, + }), + 0xac => Convert(ConvertOp::I64ExtendSI32), + 0xad => Convert(ConvertOp::I64ExtendUI32), + 0xae => Convert(ConvertOp::Trunc { + from: F32, + to: I64, + signed: true, + }), + 0xaf => Convert(ConvertOp::Trunc { + from: F32, + to: I64, + signed: false, + }), + 0xb0 => Convert(ConvertOp::Trunc { + from: F64, + to: I64, + signed: true, + }), + 0xb1 => Convert(ConvertOp::Trunc { + from: F64, + to: I64, + signed: false, + }), + 0xb2 => Convert(ConvertOp::Convert { + from: I32, + to: F32, + signed: true, + }), + 0xb3 => Convert(ConvertOp::Convert { + from: I32, + to: F32, + signed: false, + }), + 0xb4 => Convert(ConvertOp::Convert { + from: I64, + to: F32, + signed: true, + }), + 0xb5 => Convert(ConvertOp::Convert { + from: I64, + to: F32, + signed: false, + }), + 0xb6 => Convert(ConvertOp::F32DemoteF64), + 0xb7 => Convert(ConvertOp::Convert { + from: I32, + to: F64, + signed: true, + }), + 0xb8 => Convert(ConvertOp::Convert { + from: I32, + to: F64, + signed: false, + }), + 0xb9 => Convert(ConvertOp::Convert { + from: I64, + to: F64, + signed: true, + }), + 0xba => Convert(ConvertOp::Convert { + from: I64, + to: F64, + signed: false, + }), + 0xbb => Convert(ConvertOp::F64PromoteF32), + + 0xbc => Convert(ConvertOp::Reinterpret { + from: Float(F32), + to: Int(I32), + }), + 0xbd => Convert(ConvertOp::Reinterpret { + from: Float(F64), + to: Int(I64), + }), + 0xbe => Convert(ConvertOp::Reinterpret { + from: Int(I32), + to: Float(F32), + }), + 0xbf => Convert(ConvertOp::Reinterpret { + from: Int(I64), + to: Float(F64), + }), + + _ => return Err(DecodeError::MalformedBinary), + })) + } + + fn read_instr_block_with_delim(&mut self) -> DecodeResult<(Vec, PseudoInstr)> { + let mut res = Vec::new(); + loop { + match self.read_meta_instr()? { + MetaInstr::PseudoInstr(delim) => return Ok((res, delim)), + MetaInstr::Instr(instr) => res.push(instr), + } + } + } + + fn read_instr_block(&mut self) -> DecodeResult> { + let (instrs, delim) = self.read_instr_block_with_delim()?; + match delim { + PseudoInstr::Else => Err(DecodeError::MalformedBinary), + PseudoInstr::End => Ok(instrs), + } + } + + fn read_expr(&mut self) -> DecodeResult> { + self.read_instr_block() + } + + fn read_load_op( + &mut self, + type_: types::Value, + opt: Option<(u32, bool)>, + ) -> DecodeResult { + let align = self.read_vu32()?; + let offset = self.read_vu32()?; + Ok(Instr::Load(LoadOp { + align, + offset, + type_, + opt, + })) + } + + fn read_store_op(&mut self, type_: types::Value, opt: Option) -> DecodeResult { + let align = self.read_vu32()?; + let offset = self.read_vu32()?; + Ok(Instr::Store(StoreOp { + align, + offset, + type_, + opt, + })) + } + + fn skip_custom_section(&mut self, size: u32) -> DecodeResult<()> { + // Even if we ignore custom sections, we must ensure that its name is valid utf8. + let start_pos = self.pos; + let _ = self.read_name()?; + let nread = self.pos - start_pos; + // ensure that we didn't read more than the declared size of the section + if nread > size as usize { + return Err(DecodeError::MalformedBinary); + } + let nskip = size as usize - nread; + + for _ in 0..nskip { + let _ = self.read_byte()?; + } + + Ok(()) + } + + fn read_type_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_func_type) + } + + fn read_import_desc(&mut self) -> DecodeResult { + match self.read_byte()? { + 0x00 => Ok(ImportDesc::Func(self.read_index()?)), + 0x01 => Ok(ImportDesc::Table(self.read_table_type()?)), + 0x02 => Ok(ImportDesc::Memory(self.read_memory_type()?)), + 0x03 => Ok(ImportDesc::Global(self.read_global_type()?)), + _ => Err(DecodeError::MalformedBinary), + } + } + + fn read_import(&mut self) -> DecodeResult { + let module = self.read_name()?; + let name = self.read_name()?; + let desc = self.read_import_desc()?; + Ok(Import { module, name, desc }) + } + + fn read_import_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_import) + } + + fn read_func_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_index) + } + + fn read_table(&mut self) -> DecodeResult
{ + Ok(Table { + type_: self.read_table_type()?, + }) + } + + fn read_table_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_table) + } + + fn read_memory(&mut self) -> DecodeResult { + Ok(Memory { + type_: self.read_memory_type()?, + }) + } + + fn read_memory_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_memory) + } + + fn read_global(&mut self) -> DecodeResult { + let type_ = self.read_global_type()?; + let value = self.read_expr()?; + Ok(Global { type_, value }) + } + + fn read_global_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_global) + } + + fn read_export_desc(&mut self) -> DecodeResult { + match self.read_byte()? { + 0x00 => Ok(ExportDesc::Func(self.read_index()?)), + 0x01 => Ok(ExportDesc::Table(self.read_index()?)), + 0x02 => Ok(ExportDesc::Memory(self.read_index()?)), + 0x03 => Ok(ExportDesc::Global(self.read_index()?)), + _ => Err(DecodeError::MalformedBinary), + } + } + + fn read_export(&mut self) -> DecodeResult { + let name = self.read_name()?; + let desc = self.read_export_desc()?; + Ok(Export { name, desc }) + } + + fn read_export_section(&mut self) -> DecodeResult> { + self.read_vec(Decoder::read_export) + } + + fn read_start_section(&mut self) -> DecodeResult> { + Ok(Some(self.read_index()?)) + } + + fn read_segment(&mut self, read_elem: F) -> DecodeResult> + where + F: Fn(&mut Decoder) -> DecodeResult, + { + let index = self.read_index()?; + let offset = self.read_expr()?; + let init = self.read_vec(read_elem)?; + Ok(Segment { + index, + offset, + init, + }) + } + + fn read_elem(&mut self) -> DecodeResult> { + self.read_segment(Decoder::read_index) + } + + fn read_elem_section(&mut self) -> DecodeResult>> { + self.read_vec(Decoder::read_elem) + } + + fn read_local_decl(&mut self) -> DecodeResult<(u32, types::Value)> { + let n = self.read_vu32()?; + let t = self.read_value_type()?; + Ok((n, t)) + } + + fn read_code(&mut self) -> DecodeResult<(Vec, Expr)> { + let _size = self.read_vu32()?; + let local_decls = self.read_vec(Decoder::read_local_decl)?; + let n_total: u64 = local_decls.iter().map(|&(n, _)| n as u64).sum(); + if n_total > std::u32::MAX as u64 { + return Err(DecodeError::MalformedBinary); + } + + let mut locals = Vec::new(); + locals.reserve(n_total as usize); + for (n, t) in local_decls { + locals.extend(std::iter::repeat(t).take(n as usize)); + } + + let body = self.read_expr()?; + Ok((locals, body)) + } + + fn read_code_section(&mut self) -> DecodeResult, Expr)>> { + self.read_vec(Decoder::read_code) + } + + fn read_data(&mut self) -> DecodeResult> { + self.read_segment(Decoder::read_byte) + } + + fn read_data_section(&mut self) -> DecodeResult>> { + self.read_vec(Decoder::read_data) + } + + fn read_module(&mut self) -> DecodeResult { + if self.read_u32()? != MAGIC { + return Err(DecodeError::MalformedBinary); + } + if self.read_u32()? != VERSION { + return Err(DecodeError::MalformedBinary); + } + + let mut types = Vec::new(); + let mut imports = Vec::new(); + let mut func_types = Vec::new(); + let mut tables = Vec::new(); + let mut memories = Vec::new(); + let mut globals = Vec::new(); + let mut exports = Vec::new(); + let mut start = None; + let mut elems = Vec::new(); + let mut func_bodies = Vec::new(); + let mut data = Vec::new(); + + // All sections are optional and they are ordered according to their + // id, except for custom section (id=0) that may appear anywhere. + + let mut last_id = 0; + loop { + match self.read_byte() { + Err(DecodeError::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e), + Ok(id) => { + if id != 0 && id <= last_id { + return Err(DecodeError::MalformedBinary); + } + last_id = id; + + let size = self.read_vu32()?; + + match id { + 0 => self.skip_custom_section(size)?, // ignore custom sections + 1 => types = self.read_type_section()?, + 2 => imports = self.read_import_section()?, + 3 => func_types = self.read_func_section()?, + 4 => tables = self.read_table_section()?, + 5 => memories = self.read_memory_section()?, + 6 => globals = self.read_global_section()?, + 7 => exports = self.read_export_section()?, + 8 => start = self.read_start_section()?, + 9 => elems = self.read_elem_section()?, + 10 => func_bodies = self.read_code_section()?, + 11 => data = self.read_data_section()?, + _ => return Err(DecodeError::MalformedBinary), + } + } + } + } + + if func_types.len() != func_bodies.len() { + return Err(DecodeError::MalformedBinary); + } + + // pairwise merge function types and code + let funcs = func_types + .into_iter() + .zip(func_bodies) + .map(|(type_index, (locals, body))| Func { + type_index, + locals, + body, + }) + .collect(); + + Ok(Module { + types, + funcs, + tables, + memories, + globals, + elems, + data, + start, + imports, + exports, + }) + } } enum PseudoInstr { - Else, - End, + Else, + End, } enum MetaInstr { - Instr(Instr), - PseudoInstr(PseudoInstr), + Instr(Instr), + PseudoInstr(PseudoInstr), } fn decode_value_type(b: u8) -> DecodeResult { - use types::*; - - match b { - 0x7f => Ok(Value::Int(Int::I32)), - 0x7e => Ok(Value::Int(Int::I64)), - 0x7d => Ok(Value::Float(Float::F32)), - 0x7c => Ok(Value::Float(Float::F64)), - _ => Err(DecodeError::MalformedBinary), - } + use types::*; + + match b { + 0x7f => Ok(Value::Int(Int::I32)), + 0x7e => Ok(Value::Int(Int::I64)), + 0x7d => Ok(Value::Float(Float::F32)), + 0x7c => Ok(Value::Float(Float::F64)), + _ => Err(DecodeError::MalformedBinary), + } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 22f1f2d..f2e9fcb 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,46 +1,46 @@ use ast::*; +use ops::{BitsOp, FloatDemoteOp, FloatOp, FloatPromoteOp, IntOp}; use runtime::*; use types; use values::Value; -use ops::{IntOp, FloatOp, FloatDemoteOp, FloatPromoteOp, BitsOp}; use std::rc::Rc; /// A struct storing the state of the current interpreted pub struct Interpreter { - pub(crate) stack: Vec, - /// Buffer to collect return values of host functions - return_buffer: Vec, + pub(crate) stack: Vec, + /// Buffer to collect return values of host functions + return_buffer: Vec, } #[derive(Debug, PartialEq)] /// Causes at the origin of a trap. pub enum TrapOrigin { - Unreachable, - UndefinedResult, - CallIndirectElemNotFound, - CallIndirectElemUnitialized, - CallIndirectTypesDiffer, - LoadOutOfMemory, - StoreOutOfMemory, - StackOverflow, - HostFunction(HostFunctionError), + Unreachable, + UndefinedResult, + CallIndirectElemNotFound, + CallIndirectElemUnitialized, + CallIndirectTypesDiffer, + LoadOutOfMemory, + StoreOutOfMemory, + StackOverflow, + HostFunction(HostFunctionError), } #[derive(Debug, PartialEq)] pub struct Trap { - /// Original cause of the trap. Useful for debugging. - pub origin: TrapOrigin + /// Original cause of the trap. Useful for debugging. + pub origin: TrapOrigin, } #[derive(Debug, PartialEq)] pub enum Control { - /// Continue the execution linearly - Continue, - /// Branch to the `nesting_levels` outer scope, 0 being the innermost surrounding scope. - Branch { nesting_levels: u32 }, - /// Exit the function - Return, + /// Continue the execution linearly + Continue, + /// Branch to the `nesting_levels` outer scope, 0 being the innermost surrounding scope. + Branch { nesting_levels: u32 }, + /// Exit the function + Return, } use self::Control::*; @@ -49,792 +49,1021 @@ type IntResult = Result; /// Stack frames tracks frame activation pub struct StackFrame { - module: Option>, - stack_idx: usize, // The size of the stack before pushing args & locals for the Frame - nested_levels: usize, + module: Option>, + stack_idx: usize, // The size of the stack before pushing args & locals for the Frame + nested_levels: usize, } // The stack budget (how many nested levels) const STACK_BUDGET: usize = 300; impl StackFrame { - pub fn new(module: Option>) -> StackFrame { - StackFrame { - module: module, - stack_idx: 0, - nested_levels: STACK_BUDGET, - } - } - - pub fn push(&self, module: Option>, stack_idx: usize) -> Option { - if self.nested_levels == 0 { - return None; - } - - Some(StackFrame { - module: module, - stack_idx: stack_idx, - nested_levels: self.nested_levels - 1 - }) - } + pub fn new(module: Option>) -> StackFrame { + StackFrame { + module, + stack_idx: 0, + nested_levels: STACK_BUDGET, + } + } + + pub fn push(&self, module: Option>, stack_idx: usize) -> Option { + if self.nested_levels == 0 { + return None; + } + + Some(StackFrame { + module, + stack_idx, + nested_levels: self.nested_levels - 1, + }) + } } impl Interpreter { - /// Instantiate a new interpreter - pub fn new() -> Interpreter { - Interpreter { - stack: Vec::new(), - return_buffer: Vec::new(), - } - } - - /// Intrepret a single instruction. - /// This is the main dispatching function of the interpreter. - pub fn instr( - &mut self, - sframe: &StackFrame, - instr: &Instr, - funcs: & FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore - ) -> IntResult { - use ast::Instr::*; - - // Note: passing VM components mutability is case by case - match *instr { - Unreachable => self.unreachable(), - Nop => self.nop(), - Block(ref result_type, ref instrs) => self.block(sframe, result_type, instrs, funcs, tables, globals, mems), - Loop(_, ref instrs) => self.loop_(sframe, instrs, funcs, tables, globals, mems), - If(ref result_type, ref if_instrs, ref else_instrs) => self.if_(sframe, result_type, if_instrs, else_instrs, funcs, tables, globals, mems), - Br(nesting_levels) => self.branch(nesting_levels), - BrIf(nesting_levels) => self.branch_cond(nesting_levels), - BrTable(ref all_levels, default_level) => self.branch_table(all_levels, default_level), - Return => self.return_(), - Call(idx) => { - let f_addr = sframe.module.as_ref().unwrap().func_addrs[idx as usize]; - self.call(f_addr, sframe, funcs, tables, globals, mems) - }, - CallIndirect(idx) => { - let mod_inst = sframe.module.as_ref().unwrap().clone(); - self.call_indirect(idx, - sframe, - funcs, - tables, - globals, - mems, - &mod_inst.table_addrs, - &mod_inst.types) - }, - Drop_ => self.drop(), - Select => self.select(), - GetLocal(idx) => self.get_local(idx, sframe.stack_idx), - SetLocal(idx) => self.set_local(idx, sframe.stack_idx), - TeeLocal(idx) => self.tee_local(idx, sframe.stack_idx), - GetGlobal(idx) => self.get_global(idx, globals, &sframe.module.as_ref().unwrap().global_addrs), - SetGlobal(idx) => self.set_global(idx, globals, &sframe.module.as_ref().unwrap().global_addrs), - Load(ref memop) => self.load(memop, mems, &sframe.module.as_ref().unwrap().mem_addrs), - Store(ref memop) => self.store(memop, mems, &sframe.module.as_ref().unwrap().mem_addrs), - CurrentMemory => self.current_memory(mems, &sframe.module.as_ref().unwrap().mem_addrs), - GrowMemory => self.grow_memory(mems, &sframe.module.as_ref().unwrap().mem_addrs), - Const(c) => self.const_(c), - IUnary(ref t, ref op) => self.iunary(t, op), - FUnary(ref t, ref op) => self.funary(t, op), - IBin(ref t, ref op) => self.ibin(t, op), - FBin(ref t, ref op) => self.fbin(t, op), - ITest(ref t, ref op) => self.itest(t, op), - IRel(ref t, ref op) => self.irel(t, op), - FRel(ref t, ref op) => self.frel(t, op), - Convert(ref op) => self.cvtop(op), - } - } - - /// Raises an unconditional trap - fn unreachable(&self) -> IntResult { - Err(Trap { origin: TrapOrigin::Unreachable }) - } - - /// Do nothing - fn nop(&self) -> IntResult { - Ok(Continue) - } - - /// Interpret a block - fn block( - &mut self, - sframe: &StackFrame, - result_type: &[types::Value], - instrs: &[Instr], - funcs: & FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore - ) -> IntResult { - let local_stack_begin = self.stack.len(); - - for instr in instrs { - match self.instr(sframe, instr, funcs, tables, globals, mems)? { - Branch { nesting_levels } => { - // If the instruction caused a branch, we need to exit the block early on. - // The way to do so depends if the current block is the target of the branch. - return Ok(if nesting_levels == 0 { - // We have reached the target block. - // Unwind values that could be left on the stack, except for the result, - // and resume normal execution. - let junk_end = self.stack.len() - result_type.len(); - self.stack.drain(local_stack_begin..junk_end); - Continue - } else { - // Keep traversing nesting levels - Branch { nesting_levels: nesting_levels - 1 } - }) - }, - Return => return Ok(Return), // Stack unwinding will be done by the caller - Continue => {}, - } - } - - Ok(Continue) - } - - /// Interpret a loop - fn loop_( - &mut self, - sframe: &StackFrame, - instrs: &[Instr], - funcs: &FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore, - ) -> IntResult { - let local_stack_begin = self.stack.len(); - - 'outer: loop { - for instr in instrs { - let res = self.instr(sframe, instr, funcs, tables, globals, mems)?; - - match res { - Branch { nesting_levels } => { - // If the instruction caused a branch, we need to exit or restart the loop - if nesting_levels == 0 { - // We have reached the target loop. - // Unwind all values that could be left on the stack and restart the loop - self.stack.truncate(local_stack_begin); - continue 'outer; - } else { - // Exit the loop and keep traversing nesting levels - return Ok(Branch { nesting_levels: nesting_levels - 1 }); - } - } - Return => return Ok(Return), - Continue => {} - } - } - - // loops that reach the end of the instruction sequence without branching terminate - return Ok(Continue) - } - } - - /// Perform a unconditional branch to the nesting_levels+1 surrouding block. - fn branch(&self, nesting_levels: u32) -> IntResult { - Ok(Branch { nesting_levels }) - } - - /// Perform a branch if the top of the stack is not null - fn branch_cond(&mut self, nesting_levels: u32) -> IntResult { - match self.stack.pop().unwrap() { - Value::I32(c) => Ok(if c != 0 { Branch { nesting_levels } } else { Continue }), - _ => unreachable!() - } - } - - /// Perform a branch using a vector of levels, or a default nesting level based on the top of the stack - fn branch_table(&mut self, all_levels: &[u32], default_level: u32) -> IntResult { - match self.stack.pop().unwrap() { - Value::I32(c) => Ok( - Branch { - nesting_levels: *all_levels.get(c as usize).unwrap_or(&default_level) - } - ), - _ => unreachable!() - } - } - - /// If/Else block (delegate to block) - fn if_( - &mut self, - sframe: &StackFrame, - result_type: &[types::Value], - if_instrs: &[Instr], - else_instrs: &[Instr], - funcs: &FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore, - ) -> IntResult { - let c = match self.stack.pop().unwrap() { - Value::I32(c) => c, - _ => unreachable!() - }; - - Ok( - if c != 0 { - self.block(sframe, result_type, if_instrs, funcs, tables, globals, mems)? - } else { - self.block(sframe, result_type, else_instrs, funcs, tables, globals, mems)? - } - ) - } - - - /// Drop a value from the stack - fn drop(&mut self) -> IntResult { - self.stack.pop(); - Ok(Continue) - } - - /// branchless conditional - fn select(&mut self) -> IntResult { - let b = self.stack.pop().unwrap(); - let (v1, v2) = self.pop2(); - - match b { - Value::I32(c) => self.stack.push(if c != 0 { v1 } else { v2 }), - _ => unreachable!() - } - - Ok(Continue) - } - - /// Push c to the stack - fn const_(&mut self, c: Value) -> IntResult { - self.stack.push(c); - Ok(Continue) - } - - /// Dispatch an IUnop - fn iunary(&mut self, _t: &types::Int, op: &IUnOp) -> IntResult { - // Validation should assert that the top of the stack exists and has the type t - let v = match self.stack.pop().unwrap() { - Value::I32(c) => Value::I32(self.type_iunary(c, op)), - Value::I64(c) => Value::I64(self.type_iunary(c, op)), - _ => unreachable!(), - }; - self.stack.push(v); - Ok(Continue) - } - - fn type_iunary(&self, v: T, op: &IUnOp) -> T - where T: IntOp - { - match *op { - IUnOp::Clz => v.leading_zeros(), - IUnOp::Ctz => v.trailing_zeros(), - IUnOp::Popcnt => v.count_ones(), - } - } - - /// Dispatch an FUnOp - fn funary(&mut self, _t: &types::Float, op: &FUnOp) -> IntResult { - // Validation should assert that the top of the stack exists and has the type t - let v = match self.stack.pop().unwrap() { - Value::F32(c) => Value::F32(self.type_funary(c, op)), - Value::F64(c) => Value::F64(self.type_funary(c, op)), - _ => unreachable!(), - }; - self.stack.push(v); - Ok(Continue) - } - - fn type_funary(&self, v: T, op: &FUnOp) -> T - where T: FloatOp - { - match *op { - FUnOp::Neg => v.neg(), - FUnOp::Abs => v.abs(), - FUnOp::Ceil => v.ceil(), - FUnOp::Floor => v.floor(), - FUnOp::Trunc => v.trunc(), - FUnOp::Nearest => v.nearest(), - FUnOp::Sqrt => v.sqrt(), - } - } - - /// Dispatch an IBinOp - fn ibin(&mut self, _t: &types::Int, op: &IBinOp) -> IntResult { - // Validation should assert that there are two values on top of the - // stack having the same integer type t - let res = match self.pop2() { - (Value::I32(c1), Value::I32(c2)) => self.type_ibin(c1, c2, op).map(Value::I32), - (Value::I64(c1), Value::I64(c2)) => self.type_ibin(c1, c2, op).map(Value::I64), - _ => unreachable!(), - }; - - if let Some(v) = res { - self.stack.push(v); - Ok(Continue) - } else { - Err(Trap { origin: TrapOrigin::UndefinedResult }) - } - } - - // type_ibin returns None if the result is undefined - fn type_ibin(&self, c1: T, c2: T, op: &IBinOp) -> Option - where T: IntOp - { - let res = match *op { - IBinOp::Add => c1.add(c2), - IBinOp::Sub => c1.sub(c2), - IBinOp::Mul => c1.mul(c2), - IBinOp::DivS => c1.divs(c2)?, - IBinOp::DivU => c1.divu(c2)?, - IBinOp::RemS => c1.rems(c2)?, - IBinOp::RemU => c1.remu(c2)?, - IBinOp::And => c1.and(c2), - IBinOp::Or => c1.or(c2), - IBinOp::Xor => c1.xor(c2), - IBinOp::Shl => c1.shl(c2), - IBinOp::ShrS => c1.shrs(c2), - IBinOp::ShrU => c1.shru(c2), - IBinOp::Rotr => c1.rotr(c2), - IBinOp::Rotl => c1.rotl(c2), - }; - Some(res) - } - - - /// Dispatch an FBinOp - fn fbin(&mut self, _t: &types::Float, op: &FBinOp) -> IntResult { - // Validation should assert that there are two values on top of the - // stack having the same type t - let res = match self.pop2() { - (Value::F32(c1), Value::F32(c2)) => Value::F32(self.type_fbin(c1, c2, op)), - (Value::F64(c1), Value::F64(c2)) => Value::F64(self.type_fbin(c1, c2, op)), - _ => unreachable!(), - }; - self.stack.push(res); - Ok(Continue) - } - - fn type_fbin(&self, c1: T, c2: T, op: &FBinOp) -> T - where T: FloatOp - { - match *op { - FBinOp::Add => c1.add(c2), - FBinOp::Sub => c1.sub(c2), - FBinOp::Mul => c1.mul(c2), - FBinOp::Div => c1.div(c2), - FBinOp::Min => c1.min(c2), - FBinOp::Max => c1.max(c2), - FBinOp::CopySign => c1.copysign(c2), - } - } - - /// Dispatch an ITestOp - fn itest(&mut self, _t: &types::Int, op: &ITestOp) -> IntResult { - // Validation should assert that the top of the stack exists and has the type t - let v = match self.stack.pop().unwrap() { - Value::I32(c) => Value::from_bool(self.type_itest(c, op)), - Value::I64(c) => Value::from_bool(self.type_itest(c, op)), - _ => unreachable!(), - }; - self.stack.push(v); - Ok(Continue) - } - - fn type_itest(&self, v: T, op: &ITestOp) -> bool - where T: IntOp - { - match *op { - ITestOp::Eqz => v.eqz(), - } - } - - /// Dispatch an IRelOp - fn irel(&mut self, _t: &types::Int, op: &IRelOp) -> IntResult { - // Validation should assert that there are two values on top of the - // stack having the same integer type t - let res = match self.pop2() { - (Value::I32(c1), Value::I32(c2)) => Value::from_bool(self.type_irel(c1, c2, op)), - (Value::I64(c1), Value::I64(c2)) => Value::from_bool(self.type_irel(c1, c2, op)), - _ => unreachable!(), - }; - self.stack.push(res); - Ok(Continue) - } - - fn type_irel(&self, c1: T, c2: T, op: &IRelOp) -> bool - where T: IntOp - { - match *op { - IRelOp::Eq_ => c1.eq(c2), - IRelOp::Ne => c1.ne(c2), - IRelOp::LtS => c1.lts(c2), - IRelOp::LtU => c1.ltu(c2), - IRelOp::GtS => c1.gts(c2), - IRelOp::GtU => c1.gtu(c2), - IRelOp::LeS => c1.les(c2), - IRelOp::LeU => c1.leu(c2), - IRelOp::GeS => c1.ges(c2), - IRelOp::GeU => c1.geu(c2), - } - } - - /// Dispatch an FRelOp - fn frel(&mut self, _t: &types::Float, op: &FRelOp) -> IntResult { - // Validation should assert that there are two values on top of the - // stack having the same integer type t - let res = match self.pop2() { - (Value::F32(c1), Value::F32(c2)) => Value::from_bool(self.type_frel(c1, c2, op)), - (Value::F64(c1), Value::F64(c2)) => Value::from_bool(self.type_frel(c1, c2, op)), - _ => unreachable!(), - }; - self.stack.push(res); - Ok(Continue) - } - - fn type_frel(&self, c1: T, c2: T, op: &FRelOp) -> bool - where T: FloatOp - { - match *op { - FRelOp::Eq_ => c1.eq(c2), - FRelOp::Ne => c1.ne(c2), - FRelOp::Lt => c1.lt(c2), - FRelOp::Gt => c1.gt(c2), - FRelOp::Le => c1.le(c2), - FRelOp::Ge => c1.ge(c2), - } - } - - /// Dispatch a ConvertOp - fn cvtop(&mut self, op: &ConvertOp) -> IntResult { - use types::{Int, Float}; - use types::Value as tv; - - let c = self.stack.pop().unwrap(); - let cls = |&op, &c| { - Some(match (op, c) { - (&ConvertOp::I32WrapI64, Value::I64(c)) => Value::I32(c.to_u32()), - (&ConvertOp::I64ExtendUI32, Value::I32(c)) => Value::I64(c.to_u64()), - (&ConvertOp::I64ExtendSI32, Value::I32(c)) => Value::from_i64(c.to_i64()), - - (&ConvertOp::Trunc { from: Float::F32, to: Int::I32, signed: false }, Value::F32(c)) => Value::I32(c.to_u32()?), - (&ConvertOp::Trunc { from: Float::F32, to: Int::I32, signed: true }, Value::F32(c)) => Value::from_i32(c.to_i32()?), - (&ConvertOp::Trunc { from: Float::F32, to: Int::I64, signed: false }, Value::F32(c)) => Value::I64(c.to_u64()?), - (&ConvertOp::Trunc { from: Float::F32, to: Int::I64, signed: true }, Value::F32(c)) => Value::from_i64(c.to_i64()?), - (&ConvertOp::Trunc { from: Float::F64, to: Int::I32, signed: false }, Value::F64(c)) => Value::I32(c.to_u32()?), - (&ConvertOp::Trunc { from: Float::F64, to: Int::I32, signed: true }, Value::F64(c)) => Value::from_i32(c.to_i32()?), - (&ConvertOp::Trunc { from: Float::F64, to: Int::I64, signed: false }, Value::F64(c)) => Value::I64(c.to_u64()?), - (&ConvertOp::Trunc { from: Float::F64, to: Int::I64, signed: true }, Value::F64(c)) => Value::from_i64(c.to_i64()?), - - (&ConvertOp::Convert { from: Int::I32, to: Float::F32, signed: false }, Value::I32(c)) => Value::F32(c.to_uf32()), - (&ConvertOp::Convert { from: Int::I32, to: Float::F32, signed: true }, Value::I32(c)) => Value::F32(c.to_if32()), - (&ConvertOp::Convert { from: Int::I32, to: Float::F64, signed: false }, Value::I32(c)) => Value::F64(c.to_uf64()), - (&ConvertOp::Convert { from: Int::I32, to: Float::F64, signed: true }, Value::I32(c)) => Value::F64(c.to_if64()), - (&ConvertOp::Convert { from: Int::I64, to: Float::F32, signed: false }, Value::I64(c)) => Value::F32(c.to_uf32()), - (&ConvertOp::Convert { from: Int::I64, to: Float::F32, signed: true }, Value::I64(c)) => Value::F32(c.to_if32()), - (&ConvertOp::Convert { from: Int::I64, to: Float::F64, signed: false }, Value::I64(c)) => Value::F64(c.to_uf64()), - (&ConvertOp::Convert { from: Int::I64, to: Float::F64, signed: true }, Value::I64(c)) => Value::F64(c.to_if64()), - - (&ConvertOp::Reinterpret { from: tv::Int(Int::I32), to: tv::Float(Float::F32) }, Value::I32(c)) => Value::F32(c.reinterpret()), - (&ConvertOp::Reinterpret { from: tv::Int(Int::I64), to: tv::Float(Float::F64) }, Value::I64(c)) => Value::F64(c.reinterpret()), - (&ConvertOp::Reinterpret { from: tv::Float(Float::F32), to: tv::Int(Int::I32) }, Value::F32(c)) => Value::I32(c.reinterpret()), - (&ConvertOp::Reinterpret { from: tv::Float(Float::F64), to: tv::Int(Int::I64) }, Value::F64(c)) => Value::I64(c.reinterpret()), - - (&ConvertOp::F32DemoteF64, Value::F64(c)) => Value::F32(c.demote()), - (&ConvertOp::F64PromoteF32, Value::F32(c)) => Value::F64(c.promote()), - _ => unreachable!() - }) - }; - - if let Some(v) = cls(&op, &c) { - self.stack.push(v); - Ok(Continue) - } else { - Err(Trap { origin: TrapOrigin::UndefinedResult }) - } - } - - /// GetGlobal - fn get_global(&mut self, idx: Index, globals: &GlobalInstStore, frame_globals: &[GlobalAddr]) -> IntResult { - self.stack.push(globals[frame_globals[idx as usize]].value); - Ok(Continue) - } - - /// SetGlobal - fn set_global(&mut self, idx: Index, globals: &mut GlobalInstStore, frame_globals: &[GlobalAddr]) -> IntResult { - // "Validation ensures that the global is, in fact, marked as mutable." - let val = self.stack.pop().unwrap(); - globals[frame_globals[idx as usize]].value = val; - Ok(Continue) - } - - /// Push local idx on the Stack - fn get_local(&mut self, idx: Index, stack_frame_idx: usize) -> IntResult { - let val = self.stack[stack_frame_idx + (idx as usize)]; - self.stack.push(val); - Ok(Continue) - } - - /// Update local idx based on the value poped from the stack - fn set_local(&mut self, idx: Index, stack_frame_idx: usize) -> IntResult { - self.stack[stack_frame_idx + (idx as usize)] = self.stack.pop().unwrap(); - Ok(Continue) - } - - /// Update the local idx without poping the top of the stack - fn tee_local(&mut self, idx: Index, stack_frame_idx: usize) -> IntResult { - self.stack[stack_frame_idx + (idx as usize)] = *self.stack.last().unwrap(); - Ok(Continue) - } - - fn call_module( - &mut self, - f_inst: &ModuleFuncInst, - sframe: &StackFrame, - funcs: & FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore - ) -> IntResult { - // Push locals - for l in &f_inst.code.locals { - match *l { - types::Value::Int(types::Int::I32) => self.stack.push(Value::I32(0)), - types::Value::Int(types::Int::I64) => self.stack.push(Value::I64(0)), - types::Value::Float(types::Float::F32) => self.stack.push(Value::F32(0.0)), - types::Value::Float(types::Float::F64) => self.stack.push(Value::F64(0.0)), - } - } - - // Push the frame - let frame_begin = self.stack.len() - f_inst.type_.args.len() - f_inst.code.locals.len(); - let new_frame = sframe.push(Some(f_inst.module.clone()), frame_begin).ok_or(Trap { origin: TrapOrigin::StackOverflow })?; - - // Execute the function inside a block - self.block(&new_frame, - &f_inst.type_.result, - &f_inst.code.body, - funcs, - tables, - globals, - mems)?; - - // Remove locals/args - let drain_start = frame_begin; - let drain_end = self.stack.len() - f_inst.type_.result.len(); - self.stack.drain(drain_start..drain_end); - Ok(Continue) - } - - fn call_host( - &mut self, - f_inst: &HostFuncInst, - _sframe: &StackFrame, - _funcs: &FuncInstStore, - _tables: &TableInstStore, - _globals: &mut GlobalInstStore, - _mems: &mut MemInstStore - ) -> IntResult { - let args_start = self.stack.len() - f_inst.type_.args.len(); - self.return_buffer.resize(f_inst.type_.result.len(), Value::false_()); - - if let Some(err) = (f_inst.hostcode)(&self.stack[args_start..], &mut self.return_buffer) { - return Err(Trap { origin: TrapOrigin::HostFunction(err) }); - } - - // since host functions cannot be type-checked, we make sure at runtime - // that return values have the correct types - for (val, type_) in self.return_buffer.iter().zip(f_inst.type_.result.iter()) { - match (val, type_) { - (&Value::I32(_), &types::Value::Int(types::Int::I32)) => (), - (&Value::I64(_), &types::Value::Int(types::Int::I64)) => (), - (&Value::F32(_), &types::Value::Float(types::Float::F32)) => (), - (&Value::F64(_), &types::Value::Float(types::Float::F64)) => (), - _ => { panic!("Invalid return value by host function."); }, - }; - } - - // replace args with returned values - self.stack.drain(args_start..); - self.stack.extend(&self.return_buffer); - - Ok(Continue) - } - - - /// Call a function directly - pub fn call( - &mut self, - f_addr: FuncAddr, - sframe: &StackFrame, - funcs: &FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore - ) -> IntResult { - // Idea: the new stack_idx is the base frame pointer, which point to the - // first argument of the called function. When calling call, all - // arguments should already be on the stack (thanks to validation). - match funcs[f_addr] { - FuncInst::Module(ref f_inst) => self.call_module(f_inst, sframe, funcs, tables, globals, mems)?, - FuncInst::Host(ref f_inst) => self.call_host(f_inst, sframe, funcs, tables, globals, mems)?, - }; - - Ok(Continue) - } - - /// Call a function indirectly - fn call_indirect( - &mut self, - idx: Index, - sframe: &StackFrame, - funcs: & FuncInstStore, - tables: &TableInstStore, - globals: &mut GlobalInstStore, - mems: &mut MemInstStore, - table_addrs: &[TableAddr], - types: &[types::Func] - ) -> IntResult { - // For the MVP, only the table at index 0 exists and is implicitly refered - let tab = &tables[table_addrs[0]]; - let type_ = &types[idx as usize]; - let indirect_idx = match self.stack.pop().unwrap() { - Value::I32(c) => c as usize, - _ => unreachable!() - }; - - if indirect_idx >= tab.elem.len() { - return Err(Trap { origin: TrapOrigin::CallIndirectElemNotFound }) - } - - let func_addr = match tab.elem[indirect_idx] { - Some(c) => c, - None => return Err(Trap { origin: TrapOrigin::CallIndirectElemUnitialized }) - }; - - let f = &funcs[func_addr]; - let f_type_ = match *f { - FuncInst::Module(ref f) => &f.type_, - FuncInst::Host(ref f) => &f.type_, - }; - if f_type_ != type_ { - return Err(Trap { origin: TrapOrigin::CallIndirectTypesDiffer }) - } - self.call(func_addr, sframe, funcs, tables, globals, mems) - } - - /// Return to the caller of the current function - fn return_(&self) -> IntResult { - Ok(Return) - } - - /// Get the size of the current memory - fn current_memory(&mut self, memories: &MemInstStore, frame_memories: &[MemAddr]) -> IntResult { - self.stack.push(Value::I32(memories.size(frame_memories[0]) as u32)); - Ok(Continue) - } - - /// Grow the memory - fn grow_memory(&mut self, memories: &mut MemInstStore, frame_memories: &[MemAddr]) -> IntResult { - let new_pages = match self.stack.pop().unwrap() { - Value::I32(c) => c as usize, - _ => unreachable!() - }; - if let Some(old_size) = memories.grow(frame_memories[0], new_pages) { - self.stack.push(Value::I32(old_size as u32)); - } else { - self.stack.push(Value::from_i32(-1)); - } - Ok(Continue) - } - - /// Load memory (dispatcher) - fn load(&mut self, memop: &LoadOp, memories: &MemInstStore, frame_memories: &[MemAddr]) -> IntResult { - use types::{Int, Float}; - use types::Value as Tv; - - let mem = &memories[frame_memories[0]]; - let offset = match self.stack.pop().unwrap() { - Value::I32(c) => c as usize + memop.offset as usize, - _ => unreachable!() - }; - let (size_in_bits, signed) = memop.opt.unwrap_or((memop.type_.bit_width(), false)); - let size_in_bytes: usize = (size_in_bits as usize) / 8; - - if offset + size_in_bytes > mem.data.len() { - return Err(Trap { origin: TrapOrigin::LoadOutOfMemory }); - } - let bits = &mem.data[offset .. (offset + size_in_bytes)]; - - let res = match (size_in_bits, signed, memop.type_) { - (8, false, Tv::Int(Int::I32)) => Value::I32(u8::from_bits(bits) as u32), - (8, true, Tv::Int(Int::I32)) => Value::I32(i8::from_bits(bits) as u32), - (16, false, Tv::Int(Int::I32)) => Value::I32(u16::from_bits(bits) as u32), - (16, true, Tv::Int(Int::I32)) => Value::I32(i16::from_bits(bits) as u32), - (32, false, Tv::Int(Int::I32)) => Value::I32(u32::from_bits(bits) as u32), - (32, true, Tv::Int(Int::I32)) => Value::I32(i32::from_bits(bits) as u32), - - (8, false, Tv::Int(Int::I64)) => Value::I64(u8::from_bits(bits) as u64), - (8, true, Tv::Int(Int::I64)) => Value::I64(i8::from_bits(bits) as u64), - (16, false, Tv::Int(Int::I64)) => Value::I64(u16::from_bits(bits) as u64), - (16, true, Tv::Int(Int::I64)) => Value::I64(i16::from_bits(bits) as u64), - (32, false, Tv::Int(Int::I64)) => Value::I64(u32::from_bits(bits) as u64), - (32, true, Tv::Int(Int::I64)) => Value::I64(i32::from_bits(bits) as u64), - (64, false, Tv::Int(Int::I64)) => Value::I64(u64::from_bits(bits) as u64), - (64, true, Tv::Int(Int::I64)) => Value::I64(i64::from_bits(bits) as u64), - - (32, false, Tv::Float(Float::F32)) => Value::F32(f32::from_bits(u32::from_bits(bits))), - (64, false, Tv::Float(Float::F64)) => Value::F64(f64::from_bits(u64::from_bits(bits))), - _ => unreachable!() - }; - self.stack.push(res); - Ok(Continue) - } - - /// Store memory (dispatcher) - fn store(&mut self, memop: &StoreOp, memories: &mut MemInstStore, frame_memories: &[MemAddr]) -> IntResult { - use types::{Int, Float}; - use types::Value as Tv; - - let mem = &mut memories[frame_memories[0]]; - let c = self.stack.pop().unwrap(); - let offset = match self.stack.pop().unwrap() { - Value::I32(c) => c as usize + memop.offset as usize, - _ => unreachable!() - }; - let size_in_bits = memop.opt.unwrap_or(memop.type_.bit_width()); - let size_in_bytes: usize = (size_in_bits as usize) / 8; - - if offset + size_in_bytes > mem.data.len() { - return Err(Trap { origin: TrapOrigin::StoreOutOfMemory }); - } - let bits = &mut mem.data[offset .. (offset + size_in_bytes)]; - match (size_in_bits, memop.type_, c) { - (8, Tv::Int(Int::I32), Value::I32(c)) => (c as u8).to_bits(bits), - (16, Tv::Int(Int::I32), Value::I32(c)) => (c as u16).to_bits(bits), - (32, Tv::Int(Int::I32), Value::I32(c)) => (c as u32).to_bits(bits), - - (8, Tv::Int(Int::I64), Value::I64(c)) => (c as u8).to_bits(bits), - (16, Tv::Int(Int::I64), Value::I64(c)) => (c as u16).to_bits(bits), - (32, Tv::Int(Int::I64), Value::I64(c)) => (c as u32).to_bits(bits), - (64, Tv::Int(Int::I64), Value::I64(c)) => (c as u64).to_bits(bits), - - (32, Tv::Float(Float::F32), Value::F32(c)) => (c as f32).to_bits().to_bits(bits), - (64, Tv::Float(Float::F64), Value::F64(c)) => (c as f64).to_bits().to_bits(bits), - _ => unreachable!() - }; - Ok(Continue) - } - - /// Pops two values from the stack, assuming that the stack is large enough to do so. - fn pop2(&mut self) -> (Value, Value) { - let b = self.stack.pop().unwrap(); - let a = self.stack.pop().unwrap(); - (a, b) - } + /// Instantiate a new interpreter + pub fn new() -> Interpreter { + Interpreter { + stack: Vec::new(), + return_buffer: Vec::new(), + } + } + + /// Intrepret a single instruction. + /// This is the main dispatching function of the interpreter. + pub fn instr( + &mut self, + sframe: &StackFrame, + instr: &Instr, + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + ) -> IntResult { + use ast::Instr::*; + + // Note: passing VM components mutability is case by case + match *instr { + Unreachable => self.unreachable(), + Nop => self.nop(), + Block(ref result_type, ref instrs) => { + self.block(sframe, result_type, instrs, funcs, tables, globals, mems) + } + Loop(_, ref instrs) => self.loop_(sframe, instrs, funcs, tables, globals, mems), + If(ref result_type, ref if_instrs, ref else_instrs) => self.if_( + sframe, + result_type, + if_instrs, + else_instrs, + funcs, + tables, + globals, + mems, + ), + Br(nesting_levels) => self.branch(nesting_levels), + BrIf(nesting_levels) => self.branch_cond(nesting_levels), + BrTable(ref all_levels, default_level) => self.branch_table(all_levels, default_level), + Return => self.return_(), + Call(idx) => { + let f_addr = sframe.module.as_ref().unwrap().func_addrs[idx as usize]; + self.call(f_addr, sframe, funcs, tables, globals, mems) + } + CallIndirect(idx) => { + let mod_inst = sframe.module.as_ref().unwrap().clone(); + self.call_indirect( + idx, + sframe, + funcs, + tables, + globals, + mems, + &mod_inst.table_addrs, + &mod_inst.types, + ) + } + Drop_ => self.drop(), + Select => self.select(), + GetLocal(idx) => self.get_local(idx, sframe.stack_idx), + SetLocal(idx) => self.set_local(idx, sframe.stack_idx), + TeeLocal(idx) => self.tee_local(idx, sframe.stack_idx), + GetGlobal(idx) => { + self.get_global(idx, globals, &sframe.module.as_ref().unwrap().global_addrs) + } + SetGlobal(idx) => { + self.set_global(idx, globals, &sframe.module.as_ref().unwrap().global_addrs) + } + Load(ref memop) => self.load(memop, mems, &sframe.module.as_ref().unwrap().mem_addrs), + Store(ref memop) => self.store(memop, mems, &sframe.module.as_ref().unwrap().mem_addrs), + CurrentMemory => self.current_memory(mems, &sframe.module.as_ref().unwrap().mem_addrs), + GrowMemory => self.grow_memory(mems, &sframe.module.as_ref().unwrap().mem_addrs), + Const(c) => self.const_(c), + IUnary(ref t, ref op) => self.iunary(t, op), + FUnary(ref t, ref op) => self.funary(t, op), + IBin(ref t, ref op) => self.ibin(t, op), + FBin(ref t, ref op) => self.fbin(t, op), + ITest(ref t, ref op) => self.itest(t, op), + IRel(ref t, ref op) => self.irel(t, op), + FRel(ref t, ref op) => self.frel(t, op), + Convert(ref op) => self.cvtop(op), + } + } + + /// Raises an unconditional trap + fn unreachable(&self) -> IntResult { + Err(Trap { + origin: TrapOrigin::Unreachable, + }) + } + + /// Do nothing + fn nop(&self) -> IntResult { + Ok(Continue) + } + + /// Interpret a block + #[allow(clippy::too_many_arguments)] + fn block( + &mut self, + sframe: &StackFrame, + result_type: &[types::Value], + instrs: &[Instr], + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + ) -> IntResult { + let local_stack_begin = self.stack.len(); + + for instr in instrs { + match self.instr(sframe, instr, funcs, tables, globals, mems)? { + Branch { nesting_levels } => { + // If the instruction caused a branch, we need to exit the block early on. + // The way to do so depends if the current block is the target of the branch. + return Ok(if nesting_levels == 0 { + // We have reached the target block. + // Unwind values that could be left on the stack, except for the result, + // and resume normal execution. + let junk_end = self.stack.len() - result_type.len(); + self.stack.drain(local_stack_begin..junk_end); + Continue + } else { + // Keep traversing nesting levels + Branch { + nesting_levels: nesting_levels - 1, + } + }); + } + Return => return Ok(Return), // Stack unwinding will be done by the caller + Continue => {} + } + } + + Ok(Continue) + } + + /// Interpret a loop + fn loop_( + &mut self, + sframe: &StackFrame, + instrs: &[Instr], + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + ) -> IntResult { + let local_stack_begin = self.stack.len(); + + 'outer: loop { + for instr in instrs { + let res = self.instr(sframe, instr, funcs, tables, globals, mems)?; + + match res { + Branch { nesting_levels } => { + // If the instruction caused a branch, we need to exit or restart the loop + if nesting_levels == 0 { + // We have reached the target loop. + // Unwind all values that could be left on the stack and restart the loop + self.stack.truncate(local_stack_begin); + continue 'outer; + } else { + // Exit the loop and keep traversing nesting levels + return Ok(Branch { + nesting_levels: nesting_levels - 1, + }); + } + } + Return => return Ok(Return), + Continue => {} + } + } + + // loops that reach the end of the instruction sequence without branching terminate + return Ok(Continue); + } + } + + /// Perform a unconditional branch to the nesting_levels+1 surrouding block. + fn branch(&self, nesting_levels: u32) -> IntResult { + Ok(Branch { nesting_levels }) + } + + /// Perform a branch if the top of the stack is not null + fn branch_cond(&mut self, nesting_levels: u32) -> IntResult { + match self.stack.pop().unwrap() { + Value::I32(c) => Ok(if c != 0 { + Branch { nesting_levels } + } else { + Continue + }), + _ => unreachable!(), + } + } + + /// Perform a branch using a vector of levels, or a default nesting level based on the top of the stack + fn branch_table(&mut self, all_levels: &[u32], default_level: u32) -> IntResult { + match self.stack.pop().unwrap() { + Value::I32(c) => Ok(Branch { + nesting_levels: *all_levels.get(c as usize).unwrap_or(&default_level), + }), + _ => unreachable!(), + } + } + #[allow(clippy::too_many_arguments)] + /// If/Else block (delegate to block) + fn if_( + &mut self, + sframe: &StackFrame, + result_type: &[types::Value], + if_instrs: &[Instr], + else_instrs: &[Instr], + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + ) -> IntResult { + let c = match self.stack.pop().unwrap() { + Value::I32(c) => c, + _ => unreachable!(), + }; + + Ok(if c != 0 { + self.block(sframe, result_type, if_instrs, funcs, tables, globals, mems)? + } else { + self.block( + sframe, + result_type, + else_instrs, + funcs, + tables, + globals, + mems, + )? + }) + } + + /// Drop a value from the stack + fn drop(&mut self) -> IntResult { + self.stack.pop(); + Ok(Continue) + } + + /// branchless conditional + fn select(&mut self) -> IntResult { + let b = self.stack.pop().unwrap(); + let (v1, v2) = self.pop2(); + + match b { + Value::I32(c) => self.stack.push(if c != 0 { v1 } else { v2 }), + _ => unreachable!(), + } + + Ok(Continue) + } + + /// Push c to the stack + fn const_(&mut self, c: Value) -> IntResult { + self.stack.push(c); + Ok(Continue) + } + + /// Dispatch an IUnop + fn iunary(&mut self, _t: &types::Int, op: &IUnOp) -> IntResult { + // Validation should assert that the top of the stack exists and has the type t + let v = match self.stack.pop().unwrap() { + Value::I32(c) => Value::I32(self.type_iunary(c, op)), + Value::I64(c) => Value::I64(self.type_iunary(c, op)), + _ => unreachable!(), + }; + self.stack.push(v); + Ok(Continue) + } + + fn type_iunary(&self, v: T, op: &IUnOp) -> T + where + T: IntOp, + { + match *op { + IUnOp::Clz => v.leading_zeros(), + IUnOp::Ctz => v.trailing_zeros(), + IUnOp::Popcnt => v.count_ones(), + } + } + + /// Dispatch an FUnOp + fn funary(&mut self, _t: &types::Float, op: &FUnOp) -> IntResult { + // Validation should assert that the top of the stack exists and has the type t + let v = match self.stack.pop().unwrap() { + Value::F32(c) => Value::F32(self.type_funary(c, op)), + Value::F64(c) => Value::F64(self.type_funary(c, op)), + _ => unreachable!(), + }; + self.stack.push(v); + Ok(Continue) + } + + fn type_funary(&self, v: T, op: &FUnOp) -> T + where + T: FloatOp, + { + match *op { + FUnOp::Neg => v.neg(), + FUnOp::Abs => v.abs(), + FUnOp::Ceil => v.ceil(), + FUnOp::Floor => v.floor(), + FUnOp::Trunc => v.trunc(), + FUnOp::Nearest => v.nearest(), + FUnOp::Sqrt => v.sqrt(), + } + } + + /// Dispatch an IBinOp + fn ibin(&mut self, _t: &types::Int, op: &IBinOp) -> IntResult { + // Validation should assert that there are two values on top of the + // stack having the same integer type t + let res = match self.pop2() { + (Value::I32(c1), Value::I32(c2)) => self.type_ibin(c1, c2, op).map(Value::I32), + (Value::I64(c1), Value::I64(c2)) => self.type_ibin(c1, c2, op).map(Value::I64), + _ => unreachable!(), + }; + + if let Some(v) = res { + self.stack.push(v); + Ok(Continue) + } else { + Err(Trap { + origin: TrapOrigin::UndefinedResult, + }) + } + } + + // type_ibin returns None if the result is undefined + fn type_ibin(&self, c1: T, c2: T, op: &IBinOp) -> Option + where + T: IntOp, + { + let res = match *op { + IBinOp::Add => c1.add(c2), + IBinOp::Sub => c1.sub(c2), + IBinOp::Mul => c1.mul(c2), + IBinOp::DivS => c1.divs(c2)?, + IBinOp::DivU => c1.divu(c2)?, + IBinOp::RemS => c1.rems(c2)?, + IBinOp::RemU => c1.remu(c2)?, + IBinOp::And => c1.and(c2), + IBinOp::Or => c1.or(c2), + IBinOp::Xor => c1.xor(c2), + IBinOp::Shl => c1.shl(c2), + IBinOp::ShrS => c1.shrs(c2), + IBinOp::ShrU => c1.shru(c2), + IBinOp::Rotr => c1.rotr(c2), + IBinOp::Rotl => c1.rotl(c2), + }; + Some(res) + } + + /// Dispatch an FBinOp + fn fbin(&mut self, _t: &types::Float, op: &FBinOp) -> IntResult { + // Validation should assert that there are two values on top of the + // stack having the same type t + let res = match self.pop2() { + (Value::F32(c1), Value::F32(c2)) => Value::F32(self.type_fbin(c1, c2, op)), + (Value::F64(c1), Value::F64(c2)) => Value::F64(self.type_fbin(c1, c2, op)), + _ => unreachable!(), + }; + self.stack.push(res); + Ok(Continue) + } + + fn type_fbin(&self, c1: T, c2: T, op: &FBinOp) -> T + where + T: FloatOp, + { + match *op { + FBinOp::Add => c1.add(c2), + FBinOp::Sub => c1.sub(c2), + FBinOp::Mul => c1.mul(c2), + FBinOp::Div => c1.div(c2), + FBinOp::Min => c1.min(c2), + FBinOp::Max => c1.max(c2), + FBinOp::CopySign => c1.copysign(c2), + } + } + + /// Dispatch an ITestOp + fn itest(&mut self, _t: &types::Int, op: &ITestOp) -> IntResult { + // Validation should assert that the top of the stack exists and has the type t + let v = match self.stack.pop().unwrap() { + Value::I32(c) => Value::from_bool(self.type_itest(c, op)), + Value::I64(c) => Value::from_bool(self.type_itest(c, op)), + _ => unreachable!(), + }; + self.stack.push(v); + Ok(Continue) + } + + fn type_itest(&self, v: T, op: &ITestOp) -> bool + where + T: IntOp, + { + match *op { + ITestOp::Eqz => v.eqz(), + } + } + + /// Dispatch an IRelOp + fn irel(&mut self, _t: &types::Int, op: &IRelOp) -> IntResult { + // Validation should assert that there are two values on top of the + // stack having the same integer type t + let res = match self.pop2() { + (Value::I32(c1), Value::I32(c2)) => Value::from_bool(self.type_irel(c1, c2, op)), + (Value::I64(c1), Value::I64(c2)) => Value::from_bool(self.type_irel(c1, c2, op)), + _ => unreachable!(), + }; + self.stack.push(res); + Ok(Continue) + } + + fn type_irel(&self, c1: T, c2: T, op: &IRelOp) -> bool + where + T: IntOp, + { + match *op { + IRelOp::Eq_ => c1.eq(c2), + IRelOp::Ne => c1.ne(c2), + IRelOp::LtS => c1.lts(c2), + IRelOp::LtU => c1.ltu(c2), + IRelOp::GtS => c1.gts(c2), + IRelOp::GtU => c1.gtu(c2), + IRelOp::LeS => c1.les(c2), + IRelOp::LeU => c1.leu(c2), + IRelOp::GeS => c1.ges(c2), + IRelOp::GeU => c1.geu(c2), + } + } + + /// Dispatch an FRelOp + fn frel(&mut self, _t: &types::Float, op: &FRelOp) -> IntResult { + // Validation should assert that there are two values on top of the + // stack having the same integer type t + let res = match self.pop2() { + (Value::F32(c1), Value::F32(c2)) => Value::from_bool(self.type_frel(c1, c2, op)), + (Value::F64(c1), Value::F64(c2)) => Value::from_bool(self.type_frel(c1, c2, op)), + _ => unreachable!(), + }; + self.stack.push(res); + Ok(Continue) + } + + fn type_frel(&self, c1: T, c2: T, op: &FRelOp) -> bool + where + T: FloatOp, + { + match *op { + FRelOp::Eq_ => c1.eq(c2), + FRelOp::Ne => c1.ne(c2), + FRelOp::Lt => c1.lt(c2), + FRelOp::Gt => c1.gt(c2), + FRelOp::Le => c1.le(c2), + FRelOp::Ge => c1.ge(c2), + } + } + + /// Dispatch a ConvertOp + fn cvtop(&mut self, op: &ConvertOp) -> IntResult { + use types::Value as tv; + use types::{Float, Int}; + + let c = self.stack.pop().unwrap(); + let cls = |&op, &c| { + Some(match (op, c) { + (&ConvertOp::I32WrapI64, Value::I64(c)) => Value::I32(c.to_u32()), + (&ConvertOp::I64ExtendUI32, Value::I32(c)) => Value::I64(c.to_u64()), + (&ConvertOp::I64ExtendSI32, Value::I32(c)) => Value::from_i64(c.to_i64()), + + ( + &ConvertOp::Trunc { + from: Float::F32, + to: Int::I32, + signed: false, + }, + Value::F32(c), + ) => Value::I32(c.to_u32()?), + ( + &ConvertOp::Trunc { + from: Float::F32, + to: Int::I32, + signed: true, + }, + Value::F32(c), + ) => Value::from_i32(c.to_i32()?), + ( + &ConvertOp::Trunc { + from: Float::F32, + to: Int::I64, + signed: false, + }, + Value::F32(c), + ) => Value::I64(c.to_u64()?), + ( + &ConvertOp::Trunc { + from: Float::F32, + to: Int::I64, + signed: true, + }, + Value::F32(c), + ) => Value::from_i64(c.to_i64()?), + ( + &ConvertOp::Trunc { + from: Float::F64, + to: Int::I32, + signed: false, + }, + Value::F64(c), + ) => Value::I32(c.to_u32()?), + ( + &ConvertOp::Trunc { + from: Float::F64, + to: Int::I32, + signed: true, + }, + Value::F64(c), + ) => Value::from_i32(c.to_i32()?), + ( + &ConvertOp::Trunc { + from: Float::F64, + to: Int::I64, + signed: false, + }, + Value::F64(c), + ) => Value::I64(c.to_u64()?), + ( + &ConvertOp::Trunc { + from: Float::F64, + to: Int::I64, + signed: true, + }, + Value::F64(c), + ) => Value::from_i64(c.to_i64()?), + + ( + &ConvertOp::Convert { + from: Int::I32, + to: Float::F32, + signed: false, + }, + Value::I32(c), + ) => Value::F32(c.to_uf32()), + ( + &ConvertOp::Convert { + from: Int::I32, + to: Float::F32, + signed: true, + }, + Value::I32(c), + ) => Value::F32(c.to_if32()), + ( + &ConvertOp::Convert { + from: Int::I32, + to: Float::F64, + signed: false, + }, + Value::I32(c), + ) => Value::F64(c.to_uf64()), + ( + &ConvertOp::Convert { + from: Int::I32, + to: Float::F64, + signed: true, + }, + Value::I32(c), + ) => Value::F64(c.to_if64()), + ( + &ConvertOp::Convert { + from: Int::I64, + to: Float::F32, + signed: false, + }, + Value::I64(c), + ) => Value::F32(c.to_uf32()), + ( + &ConvertOp::Convert { + from: Int::I64, + to: Float::F32, + signed: true, + }, + Value::I64(c), + ) => Value::F32(c.to_if32()), + ( + &ConvertOp::Convert { + from: Int::I64, + to: Float::F64, + signed: false, + }, + Value::I64(c), + ) => Value::F64(c.to_uf64()), + ( + &ConvertOp::Convert { + from: Int::I64, + to: Float::F64, + signed: true, + }, + Value::I64(c), + ) => Value::F64(c.to_if64()), + + ( + &ConvertOp::Reinterpret { + from: tv::Int(Int::I32), + to: tv::Float(Float::F32), + }, + Value::I32(c), + ) => Value::F32(c.reinterpret()), + ( + &ConvertOp::Reinterpret { + from: tv::Int(Int::I64), + to: tv::Float(Float::F64), + }, + Value::I64(c), + ) => Value::F64(c.reinterpret()), + ( + &ConvertOp::Reinterpret { + from: tv::Float(Float::F32), + to: tv::Int(Int::I32), + }, + Value::F32(c), + ) => Value::I32(c.reinterpret()), + ( + &ConvertOp::Reinterpret { + from: tv::Float(Float::F64), + to: tv::Int(Int::I64), + }, + Value::F64(c), + ) => Value::I64(c.reinterpret()), + + (&ConvertOp::F32DemoteF64, Value::F64(c)) => Value::F32(c.demote()), + (&ConvertOp::F64PromoteF32, Value::F32(c)) => Value::F64(c.promote()), + _ => unreachable!(), + }) + }; + + if let Some(v) = cls(&op, &c) { + self.stack.push(v); + Ok(Continue) + } else { + Err(Trap { + origin: TrapOrigin::UndefinedResult, + }) + } + } + + /// GetGlobal + fn get_global( + &mut self, + idx: Index, + globals: &GlobalInstStore, + frame_globals: &[GlobalAddr], + ) -> IntResult { + self.stack.push(globals[frame_globals[idx as usize]].value); + Ok(Continue) + } + + /// SetGlobal + fn set_global( + &mut self, + idx: Index, + globals: &mut GlobalInstStore, + frame_globals: &[GlobalAddr], + ) -> IntResult { + // "Validation ensures that the global is, in fact, marked as mutable." + let val = self.stack.pop().unwrap(); + globals[frame_globals[idx as usize]].value = val; + Ok(Continue) + } + + /// Push local idx on the Stack + fn get_local(&mut self, idx: Index, stack_frame_idx: usize) -> IntResult { + let val = self.stack[stack_frame_idx + (idx as usize)]; + self.stack.push(val); + Ok(Continue) + } + + /// Update local idx based on the value poped from the stack + fn set_local(&mut self, idx: Index, stack_frame_idx: usize) -> IntResult { + self.stack[stack_frame_idx + (idx as usize)] = self.stack.pop().unwrap(); + Ok(Continue) + } + + /// Update the local idx without poping the top of the stack + fn tee_local(&mut self, idx: Index, stack_frame_idx: usize) -> IntResult { + self.stack[stack_frame_idx + (idx as usize)] = *self.stack.last().unwrap(); + Ok(Continue) + } + + fn call_module( + &mut self, + f_inst: &ModuleFuncInst, + sframe: &StackFrame, + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + ) -> IntResult { + // Push locals + for l in &f_inst.code.locals { + match *l { + types::Value::Int(types::Int::I32) => self.stack.push(Value::I32(0)), + types::Value::Int(types::Int::I64) => self.stack.push(Value::I64(0)), + types::Value::Float(types::Float::F32) => self.stack.push(Value::F32(0.0)), + types::Value::Float(types::Float::F64) => self.stack.push(Value::F64(0.0)), + } + } + + // Push the frame + let frame_begin = self.stack.len() - f_inst.type_.args.len() - f_inst.code.locals.len(); + let new_frame = sframe + .push(Some(f_inst.module.clone()), frame_begin) + .ok_or(Trap { + origin: TrapOrigin::StackOverflow, + })?; + + // Execute the function inside a block + self.block( + &new_frame, + &f_inst.type_.result, + &f_inst.code.body, + funcs, + tables, + globals, + mems, + )?; + + // Remove locals/args + let drain_start = frame_begin; + let drain_end = self.stack.len() - f_inst.type_.result.len(); + self.stack.drain(drain_start..drain_end); + Ok(Continue) + } + + fn call_host( + &mut self, + f_inst: &HostFuncInst, + _sframe: &StackFrame, + _funcs: &FuncInstStore, + _tables: &TableInstStore, + _globals: &mut GlobalInstStore, + _mems: &mut MemInstStore, + ) -> IntResult { + let args_start = self.stack.len() - f_inst.type_.args.len(); + self.return_buffer + .resize(f_inst.type_.result.len(), Value::false_()); + + if let Some(err) = (f_inst.hostcode)(&self.stack[args_start..], &mut self.return_buffer) { + return Err(Trap { + origin: TrapOrigin::HostFunction(err), + }); + } + + // since host functions cannot be type-checked, we make sure at runtime + // that return values have the correct types + for (val, type_) in self.return_buffer.iter().zip(f_inst.type_.result.iter()) { + match (val, type_) { + (&Value::I32(_), &types::Value::Int(types::Int::I32)) => (), + (&Value::I64(_), &types::Value::Int(types::Int::I64)) => (), + (&Value::F32(_), &types::Value::Float(types::Float::F32)) => (), + (&Value::F64(_), &types::Value::Float(types::Float::F64)) => (), + _ => { + panic!("Invalid return value by host function."); + } + }; + } + + // replace args with returned values + self.stack.drain(args_start..); + self.stack.extend(&self.return_buffer); + + Ok(Continue) + } + + /// Call a function directly + pub fn call( + &mut self, + f_addr: FuncAddr, + sframe: &StackFrame, + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + ) -> IntResult { + // Idea: the new stack_idx is the base frame pointer, which point to the + // first argument of the called function. When calling call, all + // arguments should already be on the stack (thanks to validation). + match funcs[f_addr] { + FuncInst::Module(ref f_inst) => { + self.call_module(f_inst, sframe, funcs, tables, globals, mems)? + } + FuncInst::Host(ref f_inst) => { + self.call_host(f_inst, sframe, funcs, tables, globals, mems)? + } + }; + + Ok(Continue) + } + + /// Call a function indirectly + #[allow(clippy::too_many_arguments)] + fn call_indirect( + &mut self, + idx: Index, + sframe: &StackFrame, + funcs: &FuncInstStore, + tables: &TableInstStore, + globals: &mut GlobalInstStore, + mems: &mut MemInstStore, + table_addrs: &[TableAddr], + types: &[types::Func], + ) -> IntResult { + // For the MVP, only the table at index 0 exists and is implicitly refered + let tab = &tables[table_addrs[0]]; + let type_ = &types[idx as usize]; + let indirect_idx = match self.stack.pop().unwrap() { + Value::I32(c) => c as usize, + _ => unreachable!(), + }; + + if indirect_idx >= tab.elem.len() { + return Err(Trap { + origin: TrapOrigin::CallIndirectElemNotFound, + }); + } + + let func_addr = match tab.elem[indirect_idx] { + Some(c) => c, + None => { + return Err(Trap { + origin: TrapOrigin::CallIndirectElemUnitialized, + }) + } + }; + + let f = &funcs[func_addr]; + let f_type_ = match *f { + FuncInst::Module(ref f) => &f.type_, + FuncInst::Host(ref f) => &f.type_, + }; + if f_type_ != type_ { + return Err(Trap { + origin: TrapOrigin::CallIndirectTypesDiffer, + }); + } + self.call(func_addr, sframe, funcs, tables, globals, mems) + } + + /// Return to the caller of the current function + fn return_(&self) -> IntResult { + Ok(Return) + } + + /// Get the size of the current memory + fn current_memory(&mut self, memories: &MemInstStore, frame_memories: &[MemAddr]) -> IntResult { + self.stack + .push(Value::I32(memories.size(frame_memories[0]) as u32)); + Ok(Continue) + } + + /// Grow the memory + fn grow_memory( + &mut self, + memories: &mut MemInstStore, + frame_memories: &[MemAddr], + ) -> IntResult { + let new_pages = match self.stack.pop().unwrap() { + Value::I32(c) => c as usize, + _ => unreachable!(), + }; + if let Some(old_size) = memories.grow(frame_memories[0], new_pages) { + self.stack.push(Value::I32(old_size as u32)); + } else { + self.stack.push(Value::from_i32(-1)); + } + Ok(Continue) + } + + /// Load memory (dispatcher) + fn load( + &mut self, + memop: &LoadOp, + memories: &MemInstStore, + frame_memories: &[MemAddr], + ) -> IntResult { + use types::Value as Tv; + use types::{Float, Int}; + + let mem = &memories[frame_memories[0]]; + let offset = match self.stack.pop().unwrap() { + Value::I32(c) => c as usize + memop.offset as usize, + _ => unreachable!(), + }; + let (size_in_bits, signed) = memop.opt.unwrap_or((memop.type_.bit_width(), false)); + let size_in_bytes: usize = (size_in_bits as usize) / 8; + + if offset + size_in_bytes > mem.data.len() { + return Err(Trap { + origin: TrapOrigin::LoadOutOfMemory, + }); + } + let bits = &mem.data[offset..(offset + size_in_bytes)]; + + let res = match (size_in_bits, signed, memop.type_) { + (8, false, Tv::Int(Int::I32)) => Value::I32(u8::from_bits(bits) as u32), + (8, true, Tv::Int(Int::I32)) => Value::I32(i8::from_bits(bits) as u32), + (16, false, Tv::Int(Int::I32)) => Value::I32(u16::from_bits(bits) as u32), + (16, true, Tv::Int(Int::I32)) => Value::I32(i16::from_bits(bits) as u32), + (32, false, Tv::Int(Int::I32)) => Value::I32(u32::from_bits(bits)), + (32, true, Tv::Int(Int::I32)) => Value::I32(i32::from_bits(bits) as u32), + + (8, false, Tv::Int(Int::I64)) => Value::I64(u8::from_bits(bits) as u64), + (8, true, Tv::Int(Int::I64)) => Value::I64(i8::from_bits(bits) as u64), + (16, false, Tv::Int(Int::I64)) => Value::I64(u16::from_bits(bits) as u64), + (16, true, Tv::Int(Int::I64)) => Value::I64(i16::from_bits(bits) as u64), + (32, false, Tv::Int(Int::I64)) => Value::I64(u32::from_bits(bits) as u64), + (32, true, Tv::Int(Int::I64)) => Value::I64(i32::from_bits(bits) as u64), + (64, false, Tv::Int(Int::I64)) => Value::I64(u64::from_bits(bits)), + (64, true, Tv::Int(Int::I64)) => Value::I64(i64::from_bits(bits) as u64), + + (32, false, Tv::Float(Float::F32)) => Value::F32(f32::from_bits(u32::from_bits(bits))), + (64, false, Tv::Float(Float::F64)) => Value::F64(f64::from_bits(u64::from_bits(bits))), + _ => unreachable!(), + }; + self.stack.push(res); + Ok(Continue) + } + + /// Store memory (dispatcher) + fn store( + &mut self, + memop: &StoreOp, + memories: &mut MemInstStore, + frame_memories: &[MemAddr], + ) -> IntResult { + use types::Value as Tv; + use types::{Float, Int}; + + let mem = &mut memories[frame_memories[0]]; + let c = self.stack.pop().unwrap(); + let offset = match self.stack.pop().unwrap() { + Value::I32(c) => c as usize + memop.offset as usize, + _ => unreachable!(), + }; + let size_in_bits = memop.opt.unwrap_or(memop.type_.bit_width()); + let size_in_bytes: usize = (size_in_bits as usize) / 8; + + if offset + size_in_bytes > mem.data.len() { + return Err(Trap { + origin: TrapOrigin::StoreOutOfMemory, + }); + } + let bits = &mut mem.data[offset..(offset + size_in_bytes)]; + match (size_in_bits, memop.type_, c) { + (8, Tv::Int(Int::I32), Value::I32(c)) => (c as u8).to_bits(bits), + (16, Tv::Int(Int::I32), Value::I32(c)) => (c as u16).to_bits(bits), + (32, Tv::Int(Int::I32), Value::I32(c)) => c.to_bits(bits), + + (8, Tv::Int(Int::I64), Value::I64(c)) => (c as u8).to_bits(bits), + (16, Tv::Int(Int::I64), Value::I64(c)) => (c as u16).to_bits(bits), + (32, Tv::Int(Int::I64), Value::I64(c)) => (c as u32).to_bits(bits), + (64, Tv::Int(Int::I64), Value::I64(c)) => c.to_bits(bits), + + (32, Tv::Float(Float::F32), Value::F32(c)) => c.to_bits().to_bits(bits), + (64, Tv::Float(Float::F64), Value::F64(c)) => c.to_bits().to_bits(bits), + _ => unreachable!(), + }; + Ok(Continue) + } + + /// Pops two values from the stack, assuming that the stack is large enough to do so. + fn pop2(&mut self) -> (Value, Value) { + let b = self.stack.pop().unwrap(); + let a = self.stack.pop().unwrap(); + (a, b) + } } /// Evaluate a constant expression (sequence of a single instruction) and return its value @@ -843,14 +1072,18 @@ impl Interpreter { /// mode, this version works with a more limited context and less allocations. /// /// Panic if called with a sequence of instruction that is not a constant expression. -pub fn eval_const_expr(globals: &GlobalInstStore, mod_globals: &[GlobalAddr], expr: &[Instr]) -> Value { - if expr.len() != 1 { - panic!("contant expressions must have exactly only one instruction"); - } - - match expr[0] { - Instr::Const(c) => c, - Instr::GetGlobal(idx) => globals[mod_globals[idx as usize]].value, - _ => panic!("not a constant expression"), - } +pub fn eval_const_expr( + globals: &GlobalInstStore, + mod_globals: &[GlobalAddr], + expr: &[Instr], +) -> Value { + if expr.len() != 1 { + panic!("contant expressions must have exactly only one instruction"); + } + + match expr[0] { + Instr::Const(c) => c, + Instr::GetGlobal(idx) => globals[mod_globals[idx as usize]].value, + _ => panic!("not a constant expression"), + } } diff --git a/src/lib.rs b/src/lib.rs index 3b1efa5..d9a6983 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(core_intrinsics)] /// Provides the public Embedding interface /// Note: the API does not take a Store and returns the new Store as stated in /// the spec but takes a (possibly mutable) reference when needed @@ -6,176 +5,174 @@ extern crate core; #[macro_use] mod interpreter; -mod ops; -mod valid; mod binary; +mod ops; mod runtime; +mod valid; pub mod ast; -pub mod values; pub mod types; +pub mod values; pub use runtime::{ - ModuleInst, - ExternVal, - FuncAddr, - TableAddr, - MemAddr, - GlobalAddr, - HostFunc, - PAGE_SIZE + ExternVal, FuncAddr, GlobalAddr, HostFunc, MemAddr, ModuleInst, TableAddr, PAGE_SIZE, }; -use runtime::*; use interpreter::{eval_const_expr, Trap, TrapOrigin}; +use runtime::*; -use std::rc::Rc; -use std::io::{Read, Seek}; use std::collections::HashMap; - +use std::io::{Read, Seek}; +use std::rc::Rc; // Do not publish internal fields of the Store struct pub struct Store { - funcs: FuncInstStore, - tables: TableInstStore, - mems: MemInstStore, - globals: GlobalInstStore, + funcs: FuncInstStore, + tables: TableInstStore, + mems: MemInstStore, + globals: GlobalInstStore, - types_map: TypeHashMap, + types_map: TypeHashMap, } #[derive(Debug, PartialEq)] pub enum Error { - DecodeModuleFailed, - NotEnoughExternVal, - UnknownImport, - ImportTypeMismatch, - ElemOffsetTooLarge(usize), - DataOffsetTooLarge(usize), - NotEnoughArgument, - ArgumentTypeMismatch, - CodeTrapped(Trap), - InvalidModule, - ExportNotFound, - InvalidTableRead, - InvalidTableWrite, - InvalidMemoryRead, - InvalidMemoryWrite, - GlobalImmutable, - GrowMemoryFailed, - StackOverflow + DecodeModuleFailed, + NotEnoughExternVal, + UnknownImport, + ImportTypeMismatch, + ElemOffsetTooLarge(usize), + DataOffsetTooLarge(usize), + NotEnoughArgument, + ArgumentTypeMismatch, + CodeTrapped(Trap), + InvalidModule, + ExportNotFound, + InvalidTableRead, + InvalidTableWrite, + InvalidMemoryRead, + InvalidMemoryWrite, + GlobalImmutable, + GrowMemoryFailed, + StackOverflow, } /// Return the empty store pub fn init_store() -> Store { - Store { - funcs: FuncInstStore::new(), - tables: TableInstStore::new(), - mems: MemInstStore::new(), - globals: GlobalInstStore::new(), - - types_map: HashMap::new(), - } + Store { + funcs: FuncInstStore::new(), + tables: TableInstStore::new(), + mems: MemInstStore::new(), + globals: GlobalInstStore::new(), + + types_map: HashMap::new(), + } } /// Decode a binary module pub fn decode_module(reader: R) -> Result { - binary::decode(reader).map_err(|_| Error::DecodeModuleFailed) + binary::decode(reader).map_err(|_| Error::DecodeModuleFailed) } /// Validate a module pub fn validate_module(module: &ast::Module) -> Option { - if valid::is_valid(module) { - None - } else { - Some(Error::InvalidModule) - } + if valid::is_valid(module) { + None + } else { + Some(Error::InvalidModule) + } } /// List module imports with their types -pub fn module_imports<'a>(module: &'a ast::Module) -> impl Iterator + 'a { - assert!(valid::is_valid(module)); - - module.imports.iter().map(move |import| (import.module.as_str(), import.name.as_str(), import.type_(module))) +pub fn module_imports( + module: & ast::Module, +) -> impl Iterator { + assert!(valid::is_valid(module)); + + module.imports.iter().map(move |import| { + ( + import.module.as_str(), + import.name.as_str(), + import.type_(module), + ) + }) } /// List module exports with their types -pub fn module_exports<'a>(module: &'a ast::Module) -> impl Iterator + 'a { - assert!(valid::is_valid(module)); - - // Imports can be exported - // "The index space for functions, tables, memories and globals includes respective imports declared in the same module." - // https://webassembly.github.io/spec/syntax/modules.html#indices - let mut func_import_types = Vec::new(); - let mut table_import_types = Vec::new(); - let mut mem_import_types = Vec::new(); - let mut global_import_types = Vec::new(); - for import in &module.imports { - use ast::*; - match import.desc { - ImportDesc::Func(idx) => - func_import_types.push(module.types[idx as usize].clone()), - ImportDesc::Table(ref type_) => - table_import_types.push(type_.clone()), - ImportDesc::Memory(ref type_) => - mem_import_types.push(type_.clone()), - ImportDesc::Global(ref type_) => - global_import_types.push(type_.clone()), - }; - } - - module.exports.iter().map(move |export| { - use types::*; - use ast::*; - let export_type = match export.desc { - ExportDesc::Func(idx) => { - let len = func_import_types.len(); - let idx = idx as usize; - if idx < len { - Extern::Func(func_import_types[idx].clone()) - } else { - Extern::Func(module.types[module.funcs[(idx - len)].type_index as usize].clone()) - } - }, - ExportDesc::Table(idx) => { - let len = table_import_types.len(); - let idx = idx as usize; - if idx < len { - Extern::Table(table_import_types[idx].clone()) - } else { - Extern::Table(module.tables[idx - len].type_.clone()) - } - }, - ExportDesc::Memory(idx) => { - let len = mem_import_types.len(); - let idx = idx as usize; - if idx < len { - Extern::Memory(mem_import_types[idx].clone()) - } else { - Extern::Memory(module.memories[idx - len].type_.clone()) - } - }, - ExportDesc::Global(idx) => { - let len = global_import_types.len(); - let idx = idx as usize; - if idx < len { - Extern::Global(global_import_types[idx].clone()) - } else { - Extern::Global(module.globals[idx - len].type_.clone()) - } - }, - }; - (export.name.as_ref(), export_type) - }) +pub fn module_exports( + module: & ast::Module, +) -> impl Iterator + { + assert!(valid::is_valid(module)); + + // Imports can be exported + // "The index space for functions, tables, memories and globals includes respective imports declared in the same module." + // https://webassembly.github.io/spec/syntax/modules.html#indices + let mut func_import_types = Vec::new(); + let mut table_import_types = Vec::new(); + let mut mem_import_types = Vec::new(); + let mut global_import_types = Vec::new(); + for import in &module.imports { + use ast::*; + match import.desc { + ImportDesc::Func(idx) => func_import_types.push(module.types[idx as usize].clone()), + ImportDesc::Table(ref type_) => table_import_types.push(type_.clone()), + ImportDesc::Memory(ref type_) => mem_import_types.push(type_.clone()), + ImportDesc::Global(ref type_) => global_import_types.push(type_.clone()), + }; + } + + module.exports.iter().map(move |export| { + use ast::*; + use types::*; + let export_type = match export.desc { + ExportDesc::Func(idx) => { + let len = func_import_types.len(); + let idx = idx as usize; + if idx < len { + Extern::Func(func_import_types[idx].clone()) + } else { + Extern::Func(module.types[module.funcs[idx - len].type_index as usize].clone()) + } + } + ExportDesc::Table(idx) => { + let len = table_import_types.len(); + let idx = idx as usize; + if idx < len { + Extern::Table(table_import_types[idx].clone()) + } else { + Extern::Table(module.tables[idx - len].type_.clone()) + } + } + ExportDesc::Memory(idx) => { + let len = mem_import_types.len(); + let idx = idx as usize; + if idx < len { + Extern::Memory(mem_import_types[idx].clone()) + } else { + Extern::Memory(module.memories[idx - len].type_.clone()) + } + } + ExportDesc::Global(idx) => { + let len = global_import_types.len(); + let idx = idx as usize; + if idx < len { + Extern::Global(global_import_types[idx].clone()) + } else { + Extern::Global(module.globals[idx - len].type_.clone()) + } + } + }; + (export.name.as_ref(), export_type) + }) } /// Get an externval value according to the exported name pub fn get_export(inst: &ModuleInst, name: &str) -> Result { - for export in &inst.exports { - if export.name == name { - return Ok(export.value); - } - } - Err(Error::ExportNotFound) + for export in &inst.exports { + if export.name == name { + return Ok(export.value); + } + } + Err(Error::ExportNotFound) } /// Allocate a host function. @@ -194,386 +191,451 @@ pub fn get_export(inst: &ModuleInst, name: &str) -> Result { /// let mut store = init_store(); /// let func_type = types::Func { args: vec![types::I32, types::F64], result: vec![types::I64] }; /// let print_count_args = |args: &[values::Value], ret: &mut[values::Value]| { -/// println!("{:?}", args); -/// ret[0] = values::Value::I64(args.len() as u64); -/// None +/// println!("{:?}", args); +/// ret[0] = values::Value::I64(args.len() as u64); +/// None /// }; /// let func_addr = alloc_func(&mut store, &func_type, Box::new(print_count_args)); /// ``` pub fn alloc_func(store: &mut Store, functype: &types::Func, hostfunc: HostFunc) -> FuncAddr { - store.funcs.alloc_host(&mut store.types_map, functype, hostfunc) + store + .funcs + .alloc_host(&mut store.types_map, functype, hostfunc) } /// Get the type of a function pub fn type_func(store: &Store, funcaddr: FuncAddr) -> types::Func { - assert!(store.funcs.contains(funcaddr)); - match store.types_map.get(&TypeKey { extern_val: ExternVal::Func(funcaddr) }) { - Some(&types::Extern::Func(ref type_)) => type_.clone(), - _ => unreachable!(), - } + assert!(store.funcs.contains(funcaddr)); + match store.types_map.get(&TypeKey { + extern_val: ExternVal::Func(funcaddr), + }) { + Some(types::Extern::Func(ref type_)) => type_.clone(), + _ => unreachable!(), + } } /// Invoke a function -pub fn invoke_func(store: &mut Store, funcaddr: FuncAddr, args: Vec) -> Result, Error> { - assert!(store.funcs.contains(funcaddr)); - let funcinst = &store.funcs[funcaddr]; - let functype = match *funcinst { - FuncInst::Module(ref f) => &f.type_, - FuncInst::Host(ref f) => &f.type_, - }; - - if functype.args.len() != args.len() { - return Err(Error::NotEnoughArgument); - } - - // typecheck arguments - if !args.iter().zip(&functype.args).all(|(val, &type_)| val.type_() == type_) { - return Err(Error::ArgumentTypeMismatch); - } - - let mut int = interpreter::Interpreter::new(); - int.stack.extend(args); - - let sframe = interpreter::StackFrame::new(None); - match int.call(funcaddr, &sframe, &store.funcs, &store.tables, &mut store.globals, &mut store.mems) { - Err(Trap { origin: TrapOrigin::StackOverflow }) => Err(Error::StackOverflow), - Err(err) => Err(Error::CodeTrapped(err)), - _ => { - let end_drain = int.stack.len() - functype.result.len(); - int.stack.drain(0..end_drain); - Ok(int.stack) - } - } +pub fn invoke_func( + store: &mut Store, + funcaddr: FuncAddr, + args: Vec, +) -> Result, Error> { + assert!(store.funcs.contains(funcaddr)); + let funcinst = &store.funcs[funcaddr]; + let functype = match *funcinst { + FuncInst::Module(ref f) => &f.type_, + FuncInst::Host(ref f) => &f.type_, + }; + + if functype.args.len() != args.len() { + return Err(Error::NotEnoughArgument); + } + + // typecheck arguments + if !args + .iter() + .zip(&functype.args) + .all(|(val, &type_)| val.type_() == type_) + { + return Err(Error::ArgumentTypeMismatch); + } + + let mut int = interpreter::Interpreter::new(); + int.stack.extend(args); + + let sframe = interpreter::StackFrame::new(None); + match int.call( + funcaddr, + &sframe, + &store.funcs, + &store.tables, + &mut store.globals, + &mut store.mems, + ) { + Err(Trap { + origin: TrapOrigin::StackOverflow, + }) => Err(Error::StackOverflow), + Err(err) => Err(Error::CodeTrapped(err)), + _ => { + let end_drain = int.stack.len() - functype.result.len(); + int.stack.drain(0..end_drain); + Ok(int.stack) + } + } } /// Allocate a table pub fn alloc_table(store: &mut Store, tabletype: &types::Table) -> TableAddr { - store.tables.alloc(&mut store.types_map, tabletype) + store.tables.alloc(&mut store.types_map, tabletype) } /// Get the type of a table pub fn type_table(store: &Store, tableaddr: TableAddr) -> types::Table { - assert!(store.tables.contains(tableaddr)); - match store.types_map.get(&TypeKey { extern_val: ExternVal::Table(tableaddr) }) { - Some(&types::Extern::Table(ref type_)) => type_.clone(), - _ => unreachable!(), - } + assert!(store.tables.contains(tableaddr)); + match store.types_map.get(&TypeKey { + extern_val: ExternVal::Table(tableaddr), + }) { + Some(types::Extern::Table(ref type_)) => type_.clone(), + _ => unreachable!(), + } } /// Read the content of a table at a given address -pub fn read_table(store: &Store, tableaddr: TableAddr, addr: usize) -> Result, Error> { - assert!(store.tables.contains(tableaddr)); - let ti = &store.tables[tableaddr]; - if addr >= ti.elem.len() { - Err(Error::InvalidTableRead) - } else { - Ok(ti.elem[addr]) - } +pub fn read_table( + store: &Store, + tableaddr: TableAddr, + addr: usize, +) -> Result, Error> { + assert!(store.tables.contains(tableaddr)); + let ti = &store.tables[tableaddr]; + if addr >= ti.elem.len() { + Err(Error::InvalidTableRead) + } else { + Ok(ti.elem[addr]) + } } /// Write AnyFunc to a specific table at a given address -pub fn write_table(store: &mut Store, tableaddr: TableAddr, addr: usize, funcaddr: Option) -> Option { - assert!(store.tables.contains(tableaddr)); - let ti = &mut store.tables[tableaddr]; - if addr >= ti.elem.len() { - Some(Error::InvalidTableWrite) - } else { - ti.elem[addr] = funcaddr; - None - } +pub fn write_table( + store: &mut Store, + tableaddr: TableAddr, + addr: usize, + funcaddr: Option, +) -> Option { + assert!(store.tables.contains(tableaddr)); + let ti = &mut store.tables[tableaddr]; + if addr >= ti.elem.len() { + Some(Error::InvalidTableWrite) + } else { + ti.elem[addr] = funcaddr; + None + } } /// Get the size of a table pub fn size_table(store: &Store, tableaddr: TableAddr) -> usize { - assert!(store.tables.contains(tableaddr)); - store.tables[tableaddr].elem.len() + assert!(store.tables.contains(tableaddr)); + store.tables[tableaddr].elem.len() } /// Grow a table by new elements pub fn grow_table(store: &mut Store, tableaddr: TableAddr, new: usize) -> Option { - assert!(store.tables.contains(tableaddr)); - let table = &mut store.tables[tableaddr].elem; - let sz = table.len(); - table.resize(sz + new, None); - None + assert!(store.tables.contains(tableaddr)); + let table = &mut store.tables[tableaddr].elem; + let sz = table.len(); + table.resize(sz + new, None); + None } /// Allocate a memory pub fn alloc_mem(store: &mut Store, memtype: &types::Memory) -> MemAddr { - store.mems.alloc(&mut store.types_map, memtype) + store.mems.alloc(&mut store.types_map, memtype) } /// Get the type of a memory pub fn type_mem(store: &Store, memaddr: MemAddr) -> types::Memory { - assert!(store.mems.contains(memaddr)); - match store.types_map.get(&TypeKey { extern_val: ExternVal::Memory(memaddr) }) { - Some(&types::Extern::Memory(ref type_)) => type_.clone(), - _ => unreachable!(), - } + assert!(store.mems.contains(memaddr)); + match store.types_map.get(&TypeKey { + extern_val: ExternVal::Memory(memaddr), + }) { + Some(types::Extern::Memory(ref type_)) => type_.clone(), + _ => unreachable!(), + } } /// Read a byte of a memory at a given address pub fn read_mem(store: &Store, memaddr: MemAddr, addr: usize) -> Result { - assert!(store.mems.contains(memaddr)); - let mi = &store.mems[memaddr]; - if addr >= mi.data.len() { - Err(Error::InvalidMemoryRead) - } else { - Ok(mi.data[addr]) - } + assert!(store.mems.contains(memaddr)); + let mi = &store.mems[memaddr]; + if addr >= mi.data.len() { + Err(Error::InvalidMemoryRead) + } else { + Ok(mi.data[addr]) + } } /// Write a byte to a memory at a given address pub fn write_mem(store: &mut Store, memaddr: MemAddr, addr: usize, byte: u8) -> Option { - assert!(store.mems.contains(memaddr)); - let mi = &mut store.mems[memaddr]; - if addr >= mi.data.len() { - Some(Error::InvalidMemoryWrite) - } else { - mi.data[addr] = byte; - None - } + assert!(store.mems.contains(memaddr)); + let mi = &mut store.mems[memaddr]; + if addr >= mi.data.len() { + Some(Error::InvalidMemoryWrite) + } else { + mi.data[addr] = byte; + None + } } /// Get the size of a memory pub fn size_mem(store: &Store, memaddr: MemAddr) -> usize { - assert!(store.mems.contains(memaddr)); - store.mems.size(memaddr) + assert!(store.mems.contains(memaddr)); + store.mems.size(memaddr) } /// Grow a memory by new pages pub fn grow_mem(store: &mut Store, memaddr: MemAddr, new: usize) -> Option { - assert!(store.mems.contains(memaddr)); - match store.mems.grow(memaddr, new) { - Some(_) => None, - None => Some(Error::GrowMemoryFailed) - } + assert!(store.mems.contains(memaddr)); + match store.mems.grow(memaddr, new) { + Some(_) => None, + None => Some(Error::GrowMemoryFailed), + } } /// Allocate a new global -pub fn alloc_global(store: &mut Store, globaltype: &types::Global, val: values::Value) -> GlobalAddr { - store.globals.alloc(&mut store.types_map, globaltype, val) +pub fn alloc_global( + store: &mut Store, + globaltype: &types::Global, + val: values::Value, +) -> GlobalAddr { + store.globals.alloc(&mut store.types_map, globaltype, val) } /// Get the type of a global pub fn type_global(store: &Store, globaladdr: GlobalAddr) -> types::Global { - assert!(store.globals.contains(globaladdr)); - match store.types_map.get(&TypeKey { extern_val: ExternVal::Global(globaladdr) }) { - Some(&types::Extern::Global(ref type_)) => type_.clone(), - _ => unreachable!(), - } + assert!(store.globals.contains(globaladdr)); + match store.types_map.get(&TypeKey { + extern_val: ExternVal::Global(globaladdr), + }) { + Some(types::Extern::Global(ref type_)) => type_.clone(), + _ => unreachable!(), + } } /// Read a global pub fn read_global(store: &Store, globaladdr: GlobalAddr) -> values::Value { - assert!(store.globals.contains(globaladdr)); - let gi = &store.globals[globaladdr]; - gi.value + assert!(store.globals.contains(globaladdr)); + let gi = &store.globals[globaladdr]; + gi.value } /// Write a global -pub fn write_global(store: &mut Store, globaladdr: GlobalAddr, val: values::Value) -> Option { - assert!(store.globals.contains(globaladdr)); - let gi = &mut store.globals[globaladdr]; - if !gi.mutable { - Some(Error::GlobalImmutable) - } else { - gi.value = val; - None - } +pub fn write_global( + store: &mut Store, + globaladdr: GlobalAddr, + val: values::Value, +) -> Option { + assert!(store.globals.contains(globaladdr)); + let gi = &mut store.globals[globaladdr]; + if !gi.mutable { + Some(Error::GlobalImmutable) + } else { + gi.value = val; + None + } } - /// Instantiate a module -pub fn instantiate_module(store: &mut Store, module: ast::Module, extern_vals: &[ExternVal]) -> Result, Error> { - // fail if module is invalid - if !valid::is_valid(&module) { - return Err(Error::InvalidModule) - } - - // ensure that the number of provided exports matches the number of imports - if extern_vals.len() != module.imports.len() { - return Err(Error::NotEnoughExternVal) - } - - // resolve imports, type-cheking them in the process - let mut imported_funcs = Vec::new(); - let mut imported_tables = Vec::new(); - let mut imported_memories = Vec::new(); - let mut imported_globals = Vec::new(); - - for (&extern_val, import) in extern_vals.iter().zip(module.imports.iter()) { - let ext_type = store.types_map - .get(&TypeKey { extern_val: extern_val }) - .ok_or(Error::UnknownImport)?; - if !ext_type.matches(&import.type_(&module)) { - return Err(Error::ImportTypeMismatch); - } - match extern_val { - ExternVal::Func(addr) => imported_funcs.push(addr), - ExternVal::Table(addr) => imported_tables.push(addr), - ExternVal::Memory(addr) => imported_memories.push(addr), - ExternVal::Global(addr) => imported_globals.push(addr), - } - } - - // compute initial values for globals - let global_vals = module.globals.iter().map(|g| { - eval_const_expr(&store.globals, &imported_globals, &g.value) - }).collect(); - - // check that the module does not try to init too many elements - let mut elem_offsets = Vec::new(); - for elem in &module.elems { - let offset = match eval_const_expr(&store.globals, &imported_globals, &elem.offset) { - values::Value::I32(c) => c as usize, - _ => unreachable!(), - }; - elem_offsets.push(offset); - - let table_size = { - let is_imported = (elem.index as usize) < imported_tables.len(); - if is_imported { - store.tables[imported_tables[elem.index as usize]].elem.len() - } else { - let module_index = elem.index as usize - imported_tables.len(); - module.tables[module_index].type_.limits.min as usize - } - }; - - if offset + elem.init.len() > table_size { - return Err(Error::ElemOffsetTooLarge(elem.index as usize)); - } - } - - // check that the module does not try to init too much memory - let mut data_offsets = Vec::new(); - for data in &module.data { - let offset = match eval_const_expr(&store.globals, &imported_globals, &data.offset) { - values::Value::I32(c) => c as usize, - _ => unreachable!(), - }; - data_offsets.push(offset); - - let memory_size = { - let is_imported = (data.index as usize) < imported_memories.len(); - if is_imported { - store.mems[imported_memories[data.index as usize]].data.len() - } else { - let module_index = data.index as usize - imported_memories.len(); - module.memories[module_index].type_.limits.min as usize * PAGE_SIZE - } - }; - - if offset + data.init.len() > memory_size { - return Err(Error::DataOffsetTooLarge(data.index as usize)); - } - } - - // everything is correct, allocate and initialize the module - allocate_and_init_module( - store, - module, - imported_funcs, - imported_tables, - imported_memories, - imported_globals, - global_vals, - elem_offsets, - data_offsets, - ) +pub fn instantiate_module( + store: &mut Store, + module: ast::Module, + extern_vals: &[ExternVal], +) -> Result, Error> { + // fail if module is invalid + if !valid::is_valid(&module) { + return Err(Error::InvalidModule); + } + + // ensure that the number of provided exports matches the number of imports + if extern_vals.len() != module.imports.len() { + return Err(Error::NotEnoughExternVal); + } + + // resolve imports, type-cheking them in the process + let mut imported_funcs = Vec::new(); + let mut imported_tables = Vec::new(); + let mut imported_memories = Vec::new(); + let mut imported_globals = Vec::new(); + + for (&extern_val, import) in extern_vals.iter().zip(module.imports.iter()) { + let ext_type = store + .types_map + .get(&TypeKey { + extern_val, + }) + .ok_or(Error::UnknownImport)?; + if !ext_type.matches(&import.type_(&module)) { + return Err(Error::ImportTypeMismatch); + } + match extern_val { + ExternVal::Func(addr) => imported_funcs.push(addr), + ExternVal::Table(addr) => imported_tables.push(addr), + ExternVal::Memory(addr) => imported_memories.push(addr), + ExternVal::Global(addr) => imported_globals.push(addr), + } + } + + // compute initial values for globals + let global_vals = module + .globals + .iter() + .map(|g| eval_const_expr(&store.globals, &imported_globals, &g.value)) + .collect(); + + // check that the module does not try to init too many elements + let mut elem_offsets = Vec::new(); + for elem in &module.elems { + let offset = match eval_const_expr(&store.globals, &imported_globals, &elem.offset) { + values::Value::I32(c) => c as usize, + _ => unreachable!(), + }; + elem_offsets.push(offset); + + let table_size = { + let is_imported = (elem.index as usize) < imported_tables.len(); + if is_imported { + store.tables[imported_tables[elem.index as usize]] + .elem + .len() + } else { + let module_index = elem.index as usize - imported_tables.len(); + module.tables[module_index].type_.limits.min as usize + } + }; + + if offset + elem.init.len() > table_size { + return Err(Error::ElemOffsetTooLarge(elem.index as usize)); + } + } + + // check that the module does not try to init too much memory + let mut data_offsets = Vec::new(); + for data in &module.data { + let offset = match eval_const_expr(&store.globals, &imported_globals, &data.offset) { + values::Value::I32(c) => c as usize, + _ => unreachable!(), + }; + data_offsets.push(offset); + + let memory_size = { + let is_imported = (data.index as usize) < imported_memories.len(); + if is_imported { + store.mems[imported_memories[data.index as usize]] + .data + .len() + } else { + let module_index = data.index as usize - imported_memories.len(); + module.memories[module_index].type_.limits.min as usize * PAGE_SIZE + } + }; + + if offset + data.init.len() > memory_size { + return Err(Error::DataOffsetTooLarge(data.index as usize)); + } + } + + // everything is correct, allocate and initialize the module + allocate_and_init_module( + store, + module, + imported_funcs, + imported_tables, + imported_memories, + imported_globals, + global_vals, + elem_offsets, + data_offsets, + ) } +#[allow(clippy::too_many_arguments)] fn allocate_and_init_module( - store: &mut Store, - module: ast::Module, - extern_funcs: Vec, - extern_tables: Vec, - extern_memories: Vec, - extern_globals: Vec, - vals: Vec, - elem_offsets: Vec, - data_offsets: Vec, + store: &mut Store, + module: ast::Module, + extern_funcs: Vec, + extern_tables: Vec, + extern_memories: Vec, + extern_globals: Vec, + vals: Vec, + elem_offsets: Vec, + data_offsets: Vec, ) -> Result, Error> { - let mut inst = ModuleInst::new(); - - // init types - inst.types = module.types; - - // init imports - inst.func_addrs.extend(extern_funcs); - inst.table_addrs.extend(extern_tables); - inst.mem_addrs.extend(extern_memories); - inst.global_addrs.extend(extern_globals); - - // functions allocation - // only allocate indices; initialization comes when the module is fully instantiated - let fsi_min = store.funcs.len(); - let fsi_max = fsi_min + module.funcs.len(); - for addr in fsi_min..fsi_max { - inst.func_addrs.push(FuncAddr::new(addr)); - } - - // tables allocation - for tab in module.tables { - inst.table_addrs.push(store.tables.alloc(&mut store.types_map, &tab.type_)); - } - - // tables initialization with elem segments - assert_eq!(module.elems.len(), elem_offsets.len()); - for (elem, offset) in module.elems.iter().zip(elem_offsets.into_iter()) { - for i in 0..elem.init.len() { - let funcidx = elem.init[i] as usize; - let funcaddr = inst.func_addrs[funcidx]; - store.tables[inst.table_addrs[elem.index as usize]].elem[offset + i] = Some(funcaddr); - } - } - - // memories allocation - for mem in module.memories { - inst.mem_addrs.push(store.mems.alloc(&mut store.types_map, &mem.type_)); - } - - // memories initialization with data segments - assert_eq!(module.data.len(), data_offsets.len()); - for (data, offset) in module.data.iter().zip(data_offsets.into_iter()) { - let mem = &mut store.mems[inst.mem_addrs[data.index as usize]]; - mem.data[offset..offset + data.init.len()].copy_from_slice(&data.init); - } - - // globals allocation - assert_eq!(module.globals.len(), vals.len()); - for (global, val) in module.globals.iter().zip(vals.into_iter()) { - inst.global_addrs.push(store.globals.alloc(&mut store.types_map, &global.type_, val)); - } - - // init exports - for export in module.exports { - let extern_val = match export.desc { - ast::ExportDesc::Func(idx) => ExternVal::Func(inst.func_addrs[idx as usize]), - ast::ExportDesc::Table(idx) => ExternVal::Table(inst.table_addrs[idx as usize]), - ast::ExportDesc::Memory(idx) => ExternVal::Memory(inst.mem_addrs[idx as usize]), - ast::ExportDesc::Global(idx) => ExternVal::Global(inst.global_addrs[idx as usize]), - }; - inst.exports.push(ExportInst { - name: export.name, - value: extern_val, - }); - } - - // now that the module is fully instantiated, we can initialize the functions and put - // them into the store - let inst = Rc::new(inst); - for func in module.funcs { - let type_ = &inst.types[func.type_index as usize]; - let _ = store.funcs.alloc_module(&mut store.types_map, type_, &inst, func); - } - - // call the start function if it exists - if let Some(idx) = module.start { - let func_addr = inst.func_addrs[idx as usize]; - invoke_func(store, func_addr, Vec::new())?; - } - - Ok(inst) + let mut inst = ModuleInst::new(); + + // init types + inst.types = module.types; + + // init imports + inst.func_addrs.extend(extern_funcs); + inst.table_addrs.extend(extern_tables); + inst.mem_addrs.extend(extern_memories); + inst.global_addrs.extend(extern_globals); + + // functions allocation + // only allocate indices; initialization comes when the module is fully instantiated + let fsi_min = store.funcs.len(); + let fsi_max = fsi_min + module.funcs.len(); + for addr in fsi_min..fsi_max { + inst.func_addrs.push(FuncAddr::new(addr)); + } + + // tables allocation + for tab in module.tables { + inst.table_addrs + .push(store.tables.alloc(&mut store.types_map, &tab.type_)); + } + + // tables initialization with elem segments + assert_eq!(module.elems.len(), elem_offsets.len()); + for (elem, offset) in module.elems.iter().zip(elem_offsets.into_iter()) { + for i in 0..elem.init.len() { + let funcidx = elem.init[i] as usize; + let funcaddr = inst.func_addrs[funcidx]; + store.tables[inst.table_addrs[elem.index as usize]].elem[offset + i] = Some(funcaddr); + } + } + + // memories allocation + for mem in module.memories { + inst.mem_addrs + .push(store.mems.alloc(&mut store.types_map, &mem.type_)); + } + + // memories initialization with data segments + assert_eq!(module.data.len(), data_offsets.len()); + for (data, offset) in module.data.iter().zip(data_offsets.into_iter()) { + let mem = &mut store.mems[inst.mem_addrs[data.index as usize]]; + mem.data[offset..offset + data.init.len()].copy_from_slice(&data.init); + } + + // globals allocation + assert_eq!(module.globals.len(), vals.len()); + for (global, val) in module.globals.iter().zip(vals.into_iter()) { + inst.global_addrs.push( + store + .globals + .alloc(&mut store.types_map, &global.type_, val), + ); + } + + // init exports + for export in module.exports { + let extern_val = match export.desc { + ast::ExportDesc::Func(idx) => ExternVal::Func(inst.func_addrs[idx as usize]), + ast::ExportDesc::Table(idx) => ExternVal::Table(inst.table_addrs[idx as usize]), + ast::ExportDesc::Memory(idx) => ExternVal::Memory(inst.mem_addrs[idx as usize]), + ast::ExportDesc::Global(idx) => ExternVal::Global(inst.global_addrs[idx as usize]), + }; + inst.exports.push(ExportInst { + name: export.name, + value: extern_val, + }); + } + + // now that the module is fully instantiated, we can initialize the functions and put + // them into the store + let inst = Rc::new(inst); + for func in module.funcs { + let type_ = &inst.types[func.type_index as usize]; + let _ = store + .funcs + .alloc_module(&mut store.types_map, type_, &inst, func); + } + + // call the start function if it exists + if let Some(idx) = module.start { + let func_addr = inst.func_addrs[idx as usize]; + invoke_func(store, func_addr, Vec::new())?; + } + + Ok(inst) } diff --git a/src/ops.rs b/src/ops.rs index 81e02ef..9571a2e 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -3,306 +3,299 @@ use std::ops::*; use core::ptr::copy_nonoverlapping; -pub trait IntOp { - type FloatType; - - // IUnOp - fn leading_zeros(self) -> Self; - fn trailing_zeros(self) -> Self; - fn count_ones(self) -> Self; - - // IBinOp - fn add(self, rhs: Self) -> Self; - fn sub(self, rhs: Self) -> Self; - fn mul(self, rhs: Self) -> Self; - fn divs(self, rhs: Self) -> Option; - fn divu(self, rhs: Self) -> Option; - fn rems(self, rhs: Self) -> Option; - fn remu(self, rhs: Self) -> Option; - fn and(self, rhs: Self) -> Self; - fn or(self, rhs: Self) -> Self; - fn xor(self, rhs: Self) -> Self; - fn shl(self, rhs: Self) -> Self; - fn shrs(self, rhs: Self) -> Self; - fn shru(self, rhs: Self) -> Self; - fn rotr(self, rhs: Self) -> Self; - fn rotl(self, rhs: Self) -> Self; - - // ITestOp - fn eqz(self) -> bool; - - // IRelOp - fn eq(self, rhs: Self) -> bool; - fn ne(self, rhs: Self) -> bool; - fn lts(self, rhs: Self) -> bool; - fn ltu(self, rhs: Self) -> bool; - fn gts(self, rhs: Self) -> bool; - fn gtu(self, rhs: Self) -> bool; - fn les(self, rhs: Self) -> bool; - fn leu(self, rhs: Self) -> bool; - fn ges(self, rhs: Self) -> bool; - fn geu(self, rhs: Self) -> bool; - - // ConvertOp - fn to_u32(self) -> u32; - fn to_u64(self) -> u64; - fn to_i64(self) -> i64; - fn to_uf32(self) -> f32; // Unsigned convert to f32 - fn to_if32(self) -> f32; // Signed convert to f32 - fn to_uf64(self) -> f64; - fn to_if64(self) -> f64; - fn reinterpret(self) -> Self::FloatType; +pub trait IntOp { + type FloatType; + + // IUnOp + fn leading_zeros(self) -> Self; + fn trailing_zeros(self) -> Self; + fn count_ones(self) -> Self; + + // IBinOp + fn add(self, rhs: Self) -> Self; + fn sub(self, rhs: Self) -> Self; + fn mul(self, rhs: Self) -> Self; + fn divs(self, rhs: Self) -> Option; + fn divu(self, rhs: Self) -> Option; + fn rems(self, rhs: Self) -> Option; + fn remu(self, rhs: Self) -> Option; + fn and(self, rhs: Self) -> Self; + fn or(self, rhs: Self) -> Self; + fn xor(self, rhs: Self) -> Self; + fn shl(self, rhs: Self) -> Self; + fn shrs(self, rhs: Self) -> Self; + fn shru(self, rhs: Self) -> Self; + fn rotr(self, rhs: Self) -> Self; + fn rotl(self, rhs: Self) -> Self; + + // ITestOp + fn eqz(self) -> bool; + + // IRelOp + fn eq(self, rhs: Self) -> bool; + fn ne(self, rhs: Self) -> bool; + fn lts(self, rhs: Self) -> bool; + fn ltu(self, rhs: Self) -> bool; + fn gts(self, rhs: Self) -> bool; + fn gtu(self, rhs: Self) -> bool; + fn les(self, rhs: Self) -> bool; + fn leu(self, rhs: Self) -> bool; + fn ges(self, rhs: Self) -> bool; + fn geu(self, rhs: Self) -> bool; + + // ConvertOp + fn to_u32(self) -> u32; + fn to_u64(self) -> u64; + fn to_i64(self) -> i64; + fn to_uf32(self) -> f32; // Unsigned convert to f32 + fn to_if32(self) -> f32; // Signed convert to f32 + fn to_uf64(self) -> f64; + fn to_if64(self) -> f64; + fn reinterpret(self) -> Self::FloatType; } macro_rules! impl_int_op { - ($T:ty, $S:ty, $U:ty, $F:ty) => ( - impl IntOp for $T { - type FloatType=$F; - - #[inline] - fn leading_zeros(self) -> $T { - <$T>::leading_zeros(self) as $T - } - - #[inline] - fn trailing_zeros(self) -> $T { - <$T>::trailing_zeros(self) as $T - } - - #[inline] - fn count_ones(self) -> $T { - <$T>::count_ones(self) as $T - } - - #[inline] - fn add(self, rhs: $T) -> $T { - <$T>::wrapping_add(self, rhs) - } - - #[inline] - fn sub(self, rhs: $T) -> $T { - <$T>::wrapping_sub(self, rhs) - } - - #[inline] - fn mul(self, rhs: $T) -> $T { - <$T>::wrapping_mul(self, rhs) - } - - #[inline] - fn divs(self, rhs: $T) -> Option<$T> { - let s_self = self as $S; - let s_rhs = rhs as $S; - match <$S>::checked_div(s_self, s_rhs) { - Some(c) => Some(c as $T), - None => None - } - } - - #[inline] - fn divu(self, rhs: $T) -> Option<$T> { - <$T>::checked_div(self, rhs) - } - - #[inline] - fn rems(self, rhs: $T) -> Option<$T> { - if rhs == 0 { - return None - } - let s_self = self as $S; - let s_rhs = rhs as $S; - Some(<$S>::wrapping_rem(s_self, s_rhs) as $T) - } - - #[inline] - fn remu(self, rhs: $T) -> Option<$T> { - <$T>::checked_rem(self, rhs) - } - - #[inline] - fn and(self, rhs: $T) -> $T { - <$T>::bitand(self, rhs) - } - - #[inline] - fn or(self, rhs: $T) -> $T { - <$T>::bitor(self, rhs) - } - - #[inline] - fn xor(self, rhs: $T) -> $T { - <$T>::bitxor(self, rhs) - } - - #[inline] - fn shl(self, rhs: $T) -> $T { - <$T>::wrapping_shl(self, rhs as u32) - } - - #[inline] - fn shrs(self, rhs: $T) -> $T { - let s_self = self as $S; - <$S>::wrapping_shr(s_self, rhs as u32) as $T - } - - #[inline] - fn shru(self, rhs: $T) -> $T { - <$T>::wrapping_shr(self, rhs as u32) - } - - #[inline] - fn rotr(self, rhs: $T) -> $T { - <$T>::rotate_right(self, rhs as u32) - } - - #[inline] - fn rotl(self, rhs: $T) -> $T { - <$T>::rotate_left(self, rhs as u32) - } - - #[inline] - fn eqz(self) -> bool { - self == 0 - } - - #[inline] - fn eq(self, rhs: $T) -> bool { - self == rhs - } - - #[inline] - fn ne(self, rhs: $T) -> bool { - self != rhs - } - - #[inline] - fn lts(self, rhs: $T) -> bool { - (self as $S) < (rhs as $S) - } - - #[inline] - fn ltu(self, rhs: $T) -> bool { - self < rhs - } - - #[inline] - fn gts(self, rhs: $T) -> bool { - (self as $S) > (rhs as $S) - } - - #[inline] - fn gtu(self, rhs: $T) -> bool { - self > rhs - } - - #[inline] - fn les(self, rhs: $T) -> bool { - (self as $S) <= (rhs as $S) - } - - #[inline] - fn leu(self, rhs: $T) -> bool { - self <= rhs - } - - #[inline] - fn ges(self, rhs: $T) -> bool { - (self as $S) >= (rhs as $S) - } - - #[inline] - fn geu(self, rhs: $T) -> bool { - self >= rhs - } - - #[inline] - fn to_u32(self) -> u32 { - self as u32 - } - - #[inline] - fn to_u64(self) -> u64 { - self as u64 - } - - #[inline] - fn to_i64(self) -> i64 { - (self as $S) as i64 - } - - #[inline] - fn to_uf32(self) -> f32 { - self as f32 - } - - #[inline] - fn to_if32(self) -> f32 { - (self as $S) as f32 - } - - #[inline] - fn to_uf64(self) -> f64 { - self as f64 - } - - #[inline] - fn to_if64(self) -> f64 { - (self as $S) as f64 - } - - #[inline] - fn reinterpret(self) -> $F { - <$F>::from_bits(self) - } - } - ) + ($T:ty, $S:ty, $U:ty, $F:ty) => { + impl IntOp for $T { + type FloatType = $F; + + #[inline] + fn leading_zeros(self) -> $T { + <$T>::leading_zeros(self) as $T + } + + #[inline] + fn trailing_zeros(self) -> $T { + <$T>::trailing_zeros(self) as $T + } + + #[inline] + fn count_ones(self) -> $T { + <$T>::count_ones(self) as $T + } + + #[inline] + fn add(self, rhs: $T) -> $T { + <$T>::wrapping_add(self, rhs) + } + + #[inline] + fn sub(self, rhs: $T) -> $T { + <$T>::wrapping_sub(self, rhs) + } + + #[inline] + fn mul(self, rhs: $T) -> $T { + <$T>::wrapping_mul(self, rhs) + } + + #[inline] + fn divs(self, rhs: $T) -> Option<$T> { + let s_self = self as $S; + let s_rhs = rhs as $S; + match <$S>::checked_div(s_self, s_rhs) { + Some(c) => Some(c as $T), + None => None, + } + } + + #[inline] + fn divu(self, rhs: $T) -> Option<$T> { + <$T>::checked_div(self, rhs) + } + + #[inline] + fn rems(self, rhs: $T) -> Option<$T> { + if rhs == 0 { + return None; + } + let s_self = self as $S; + let s_rhs = rhs as $S; + Some(<$S>::wrapping_rem(s_self, s_rhs) as $T) + } + + #[inline] + fn remu(self, rhs: $T) -> Option<$T> { + <$T>::checked_rem(self, rhs) + } + + #[inline] + fn and(self, rhs: $T) -> $T { + <$T>::bitand(self, rhs) + } + + #[inline] + fn or(self, rhs: $T) -> $T { + <$T>::bitor(self, rhs) + } + + #[inline] + fn xor(self, rhs: $T) -> $T { + <$T>::bitxor(self, rhs) + } + + #[inline] + fn shl(self, rhs: $T) -> $T { + <$T>::wrapping_shl(self, rhs as u32) + } + + #[inline] + fn shrs(self, rhs: $T) -> $T { + let s_self = self as $S; + <$S>::wrapping_shr(s_self, rhs as u32) as $T + } + + #[inline] + fn shru(self, rhs: $T) -> $T { + <$T>::wrapping_shr(self, rhs as u32) + } + + #[inline] + fn rotr(self, rhs: $T) -> $T { + <$T>::rotate_right(self, rhs as u32) + } + + #[inline] + fn rotl(self, rhs: $T) -> $T { + <$T>::rotate_left(self, rhs as u32) + } + + #[inline] + fn eqz(self) -> bool { + self == 0 + } + + #[inline] + fn eq(self, rhs: $T) -> bool { + self == rhs + } + + #[inline] + fn ne(self, rhs: $T) -> bool { + self != rhs + } + + #[inline] + fn lts(self, rhs: $T) -> bool { + (self as $S) < (rhs as $S) + } + + #[inline] + fn ltu(self, rhs: $T) -> bool { + self < rhs + } + + #[inline] + fn gts(self, rhs: $T) -> bool { + (self as $S) > (rhs as $S) + } + + #[inline] + fn gtu(self, rhs: $T) -> bool { + self > rhs + } + + #[inline] + fn les(self, rhs: $T) -> bool { + (self as $S) <= (rhs as $S) + } + + #[inline] + fn leu(self, rhs: $T) -> bool { + self <= rhs + } + + #[inline] + fn ges(self, rhs: $T) -> bool { + (self as $S) >= (rhs as $S) + } + + #[inline] + fn geu(self, rhs: $T) -> bool { + self >= rhs + } + + #[inline] + fn to_u32(self) -> u32 { + self as u32 + } + + #[inline] + fn to_u64(self) -> u64 { + self as u64 + } + + #[inline] + fn to_i64(self) -> i64 { + (self as $S) as i64 + } + + #[inline] + fn to_uf32(self) -> f32 { + self as f32 + } + + #[inline] + fn to_if32(self) -> f32 { + (self as $S) as f32 + } + + #[inline] + fn to_uf64(self) -> f64 { + self as f64 + } + + #[inline] + fn to_if64(self) -> f64 { + (self as $S) as f64 + } + + #[inline] + fn reinterpret(self) -> $F { + <$F>::from_bits(self) + } + } + }; } impl_int_op!(u32, i32, u32, f32); impl_int_op!(u64, i64, u64, f64); // From crate byteorder macro_rules! impl_bits_ops { - ($ty:ty, $size:expr) => { - impl BitsOp for $ty { - - #[inline] - fn from_bits(src: &[u8]) -> $ty { - assert!($size == std::mem::size_of::<$ty>()); - assert!($size <= src.len()); - let mut data: $ty = 0; - unsafe { - copy_nonoverlapping( - src.as_ptr(), - &mut data as *mut $ty as *mut u8, - $size); - } - data.to_le() - } - - #[inline] - fn to_bits(self, dst: &mut [u8]) { - assert!($size == std::mem::size_of::<$ty>()); - assert_eq!($size, dst.len()); - - unsafe { - copy_nonoverlapping( - [self].as_ptr() as *const u8, - dst.as_mut_ptr(), - dst.len()); - } - } - } - } + ($ty:ty, $size:expr) => { + impl BitsOp for $ty { + #[inline] + fn from_bits(src: &[u8]) -> $ty { + assert!($size == std::mem::size_of::<$ty>()); + assert!($size <= src.len()); + let mut data: $ty = 0; + unsafe { + copy_nonoverlapping(src.as_ptr(), &mut data as *mut $ty as *mut u8, $size); + } + data.to_le() + } + + #[inline] + fn to_bits(self, dst: &mut [u8]) { + assert!($size == std::mem::size_of::<$ty>()); + assert_eq!($size, dst.len()); + + unsafe { + copy_nonoverlapping([self].as_ptr() as *const u8, dst.as_mut_ptr(), dst.len()); + } + } + } + }; } pub trait BitsOp { - // MemOp - fn from_bits(src: &[u8]) -> Self; - fn to_bits(self, dst: &mut [u8]); + // MemOp + fn from_bits(src: &[u8]) -> Self; + fn to_bits(self, dst: &mut [u8]); } -impl_bits_ops!(u8, 1); -impl_bits_ops!(i8, 1); +impl_bits_ops!(u8, 1); +impl_bits_ops!(i8, 1); impl_bits_ops!(u16, 2); impl_bits_ops!(i16, 2); impl_bits_ops!(u32, 4); @@ -311,234 +304,238 @@ impl_bits_ops!(u64, 8); impl_bits_ops!(i64, 8); pub trait FloatOp { - type IntType; - - // FUnOp - fn neg(self) -> Self; - fn abs(self) -> Self; - fn ceil(self) -> Self; - fn floor(self) -> Self; - fn trunc(self) -> Self; - /// round-to-nearest ties-to-even - fn nearest(self) -> Self; - fn sqrt(self) -> Self; - - // FBinOp - fn add(self, rhs: Self) -> Self; - fn sub(self, rhs: Self) -> Self; - fn mul(self, rhs: Self) -> Self; - fn div(self, rhs: Self) -> Self; - fn min(self, rhs: Self) -> Self; - fn max(self, rhs: Self) -> Self; - fn copysign(self, rhs: Self) -> Self; - - // FRelOp - fn eq(self, rhs: Self) -> bool; - fn ne(self, rhs: Self) -> bool; - fn lt(self, rhs: Self) -> bool; - fn gt(self, rhs: Self) -> bool; - fn le(self, rhs: Self) -> bool; - fn ge(self, rhs: Self) -> bool; - - // Convert - fn to_i32(self) -> Option; - fn to_i64(self) -> Option; - fn to_u32(self) -> Option; - fn to_u64(self) -> Option; - fn reinterpret(self) -> Self::IntType; - - // Canonical NaN - fn is_canonical_nan(self) -> bool; + type IntType; + + // FUnOp + fn neg(self) -> Self; + fn abs(self) -> Self; + fn ceil(self) -> Self; + fn floor(self) -> Self; + fn trunc(self) -> Self; + /// round-to-nearest ties-to-even + fn nearest(self) -> Self; + fn sqrt(self) -> Self; + + // FBinOp + fn add(self, rhs: Self) -> Self; + fn sub(self, rhs: Self) -> Self; + fn mul(self, rhs: Self) -> Self; + fn div(self, rhs: Self) -> Self; + fn min(self, rhs: Self) -> Self; + fn max(self, rhs: Self) -> Self; + fn copysign(self, rhs: Self) -> Self; + + // FRelOp + fn eq(self, rhs: Self) -> bool; + fn ne(self, rhs: Self) -> bool; + fn lt(self, rhs: Self) -> bool; + fn gt(self, rhs: Self) -> bool; + fn le(self, rhs: Self) -> bool; + fn ge(self, rhs: Self) -> bool; + + // Convert + fn to_i32(self) -> Option; + fn to_i64(self) -> Option; + fn to_u32(self) -> Option; + fn to_u64(self) -> Option; + fn reinterpret(self) -> Self::IntType; + + // Canonical NaN + fn is_canonical_nan(self) -> bool; } macro_rules! impl_convert_float_s { - ($T:ty, $U:ty, $N:ident) => ( - #[inline] - fn $N(self) -> Option<$U> { - if self.is_nan() { - None - } else { - if self >= -(<$U>::min_value() as $T) || self < <$U>::min_value() as $T { - None - } else { - Some(self.trunc() as $U) - } - } - } - ) + ($T:ty, $U:ty, $N:ident) => { + #[inline] + fn $N(self) -> Option<$U> { + if self.is_nan() { + None + } else { + if self >= -(<$U>::min_value() as $T) || self < <$U>::min_value() as $T { + None + } else { + Some(self.trunc() as $U) + } + } + } + }; } macro_rules! impl_convert_float_u { - ($T:ty, $U:ty, $S:ty, $N:ident) => ( - #[inline] - fn $N(self) -> Option<$U> { - if self.is_nan() { - None - } else { - if self >= -(<$S>::min_value() as $T) * 2.0 || self <= -1.0 { - None - } else { - Some(self.trunc() as $U) - } - } - } - ) + ($T:ty, $U:ty, $S:ty, $N:ident) => { + #[inline] + fn $N(self) -> Option<$U> { + if self.is_nan() { + None + } else { + if self >= -(<$S>::min_value() as $T) * 2.0 || self <= -1.0 { + None + } else { + Some(self.trunc() as $U) + } + } + } + }; } macro_rules! impl_float_op { - ($T:ty, $I:ty, $SB:expr, $NAN:expr) => ( - impl FloatOp for $T { - type IntType = $I; - - #[inline] - fn neg(self) -> $T { - std::ops::Neg::neg(self) - } - - #[inline] - fn abs(self) -> $T { - <$T>::abs(self) - } - - #[inline] - fn ceil(self) -> $T { - <$T>::ceil(self) - } - - #[inline] - fn floor(self) -> $T { - <$T>::floor(self) - } - - #[inline] - fn trunc(self) -> $T { - <$T>::trunc(self) - } - - #[inline] - fn nearest(self) -> $T { - // Implementation from - // https://github.com/WebAssembly/spec/blob/fb7e7e1e381ffc283c923a87fdfea5ebbd213737/interpreter/exec/float.ml#L148 - - // preserve the sign of 0 - if self == 0.0 { - return self; - } - if self.is_nan() { - return $NAN - } - let u = self.ceil(); - let d = self.floor(); - let um = (self - u).abs(); - let dm = (self - d).abs(); - let half_u = u / 2.0; - if um < dm || um == dm && half_u.floor() == half_u { u } else { d } - } - - #[inline] - fn sqrt(self) -> $T { - <$T>::sqrt(self) - } - - #[inline] - fn add(self, rhs: $T) -> $T { - self + rhs - } - - #[inline] - fn sub(self, rhs: $T) -> $T { - self - rhs - } - - #[inline] - fn mul(self, rhs: $T) -> $T { - self * rhs - } - - #[inline] - fn div(self, rhs: $T) -> $T { - self / rhs - } - - #[inline] - fn min(self, rhs: $T) -> $T { - // min(-0.0, 0.0) == -0.0 - if self == rhs { - (self.to_bits() | rhs.to_bits()).reinterpret() - } else if self < rhs { - self - } else if self > rhs { - rhs - } else { - $NAN - } - } - - #[inline] - fn max(self, rhs: $T) -> $T { - // max(-0.0, 0.0) == 0.0 - if self == rhs { - (self.to_bits() & rhs.to_bits()).reinterpret() - } else if self > rhs { - self - } else if self < rhs { - rhs - } else { - $NAN - } - } - - #[inline] - fn copysign(self, rhs: $T) -> $T { - self.copysign(rhs) - } - - #[inline] - fn eq(self, rhs: $T) -> bool { - self == rhs - } - - #[inline] - fn ne(self, rhs: $T) -> bool { - self != rhs - } - - #[inline] - fn lt(self, rhs: $T) -> bool { - self < rhs - } - - #[inline] - fn gt(self, rhs: $T) -> bool { - self > rhs - } - - #[inline] - fn le(self, rhs: $T) -> bool { - self <= rhs - } - - #[inline] - fn ge(self, rhs: $T) -> bool { - self >= rhs - } - - impl_convert_float_s!($T, i32, to_i32); - impl_convert_float_s!($T, i64, to_i64); - impl_convert_float_u!($T, u32, i32, to_u32); - impl_convert_float_u!($T, u64, i64, to_u64); - - #[inline] - fn reinterpret(self) -> $I { - self.to_bits() - } - - #[inline] - fn is_canonical_nan(self) -> bool { - self.to_bits() == $NAN.to_bits() || self.to_bits() == (-$NAN).to_bits() - } - } - ) + ($T:ty, $I:ty, $SB:expr, $NAN:expr) => { + impl FloatOp for $T { + type IntType = $I; + + #[inline] + fn neg(self) -> $T { + std::ops::Neg::neg(self) + } + + #[inline] + fn abs(self) -> $T { + <$T>::abs(self) + } + + #[inline] + fn ceil(self) -> $T { + <$T>::ceil(self) + } + + #[inline] + fn floor(self) -> $T { + <$T>::floor(self) + } + + #[inline] + fn trunc(self) -> $T { + <$T>::trunc(self) + } + + #[inline] + fn nearest(self) -> $T { + // Implementation from + // https://github.com/WebAssembly/spec/blob/fb7e7e1e381ffc283c923a87fdfea5ebbd213737/interpreter/exec/float.ml#L148 + + // preserve the sign of 0 + if self == 0.0 { + return self; + } + if self.is_nan() { + return $NAN; + } + let u = self.ceil(); + let d = self.floor(); + let um = (self - u).abs(); + let dm = (self - d).abs(); + let half_u = u / 2.0; + if um < dm || um == dm && half_u.floor() == half_u { + u + } else { + d + } + } + + #[inline] + fn sqrt(self) -> $T { + <$T>::sqrt(self) + } + + #[inline] + fn add(self, rhs: $T) -> $T { + self + rhs + } + + #[inline] + fn sub(self, rhs: $T) -> $T { + self - rhs + } + + #[inline] + fn mul(self, rhs: $T) -> $T { + self * rhs + } + + #[inline] + fn div(self, rhs: $T) -> $T { + self / rhs + } + + #[inline] + fn min(self, rhs: $T) -> $T { + // min(-0.0, 0.0) == -0.0 + if self == rhs { + (self.to_bits() | rhs.to_bits()).reinterpret() + } else if self < rhs { + self + } else if self > rhs { + rhs + } else { + $NAN + } + } + + #[inline] + fn max(self, rhs: $T) -> $T { + // max(-0.0, 0.0) == 0.0 + if self == rhs { + (self.to_bits() & rhs.to_bits()).reinterpret() + } else if self > rhs { + self + } else if self < rhs { + rhs + } else { + $NAN + } + } + + #[inline] + fn copysign(self, rhs: $T) -> $T { + self.copysign(rhs) + } + + #[inline] + fn eq(self, rhs: $T) -> bool { + self == rhs + } + + #[inline] + fn ne(self, rhs: $T) -> bool { + self != rhs + } + + #[inline] + fn lt(self, rhs: $T) -> bool { + self < rhs + } + + #[inline] + fn gt(self, rhs: $T) -> bool { + self > rhs + } + + #[inline] + fn le(self, rhs: $T) -> bool { + self <= rhs + } + + #[inline] + fn ge(self, rhs: $T) -> bool { + self >= rhs + } + + impl_convert_float_s!($T, i32, to_i32); + impl_convert_float_s!($T, i64, to_i64); + impl_convert_float_u!($T, u32, i32, to_u32); + impl_convert_float_u!($T, u64, i64, to_u64); + + #[inline] + fn reinterpret(self) -> $I { + self.to_bits() + } + + #[inline] + fn is_canonical_nan(self) -> bool { + self.to_bits() == $NAN.to_bits() || self.to_bits() == (-$NAN).to_bits() + } + } + }; } impl_float_op!(f32, u32, 32, std::f32::NAN); @@ -546,23 +543,23 @@ impl_float_op!(f64, u64, 64, std::f64::NAN); // Promote/Demote are only available in one way pub trait FloatPromoteOp { - fn promote(self) -> f64; + fn promote(self) -> f64; } pub trait FloatDemoteOp { - fn demote(self) -> f32; + fn demote(self) -> f32; } impl FloatPromoteOp for f32 { - #[inline] - fn promote(self) -> f64 { - self as f64 - } + #[inline] + fn promote(self) -> f64 { + self as f64 + } } impl FloatDemoteOp for f64 { - #[inline] - fn demote(self) -> f32 { - self as f32 - } + #[inline] + fn demote(self) -> f32 { + self as f32 + } } diff --git a/src/runtime.rs b/src/runtime.rs index a4741b1..540ba56 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -2,67 +2,68 @@ use ast; use types; use values; -use std::rc::Rc; -use std::ops::{Index, IndexMut}; use std::collections::HashMap; +use std::ops::{Index, IndexMut}; +use std::rc::Rc; // Use a map for types to answer type_{func, table, memory, global} #[derive(PartialEq, Eq, Hash)] pub struct TypeKey { - pub extern_val: ExternVal, + pub extern_val: ExternVal, } pub type TypeHashMap = HashMap; // Instances of a Module/Func/Table/Memory/Global pub struct ModuleInst { - pub(crate) types: Vec, - pub(crate) func_addrs: Vec, - pub(crate) table_addrs: Vec, - pub(crate) mem_addrs: Vec, - pub(crate) global_addrs: Vec, - pub(crate) exports: Vec, + pub(crate) types: Vec, + pub(crate) func_addrs: Vec, + pub(crate) table_addrs: Vec, + pub(crate) mem_addrs: Vec, + pub(crate) global_addrs: Vec, + pub(crate) exports: Vec, } pub struct MemInst { - pub data: Vec, - pub max: Option, + pub data: Vec, + pub max: Option, } pub struct GlobalInst { - pub value: values::Value, - pub mutable: bool, + pub value: values::Value, + pub mutable: bool, } pub type HostFunctionError = String; -pub type HostFunc = Box Option>; +pub type HostFunc = + Box Option>; pub struct HostFuncInst { - pub type_: types::Func, - pub hostcode: HostFunc, + pub type_: types::Func, + pub hostcode: HostFunc, } pub struct ModuleFuncInst { - pub type_: types::Func, - pub module: Rc, - pub code: ast::Func, + pub type_: types::Func, + pub module: Rc, + pub code: ast::Func, } pub enum FuncInst { - Module(ModuleFuncInst), - Host(HostFuncInst), + Module(ModuleFuncInst), + Host(HostFuncInst), } type FuncElem = Option; pub struct TableInst { - pub elem: Vec, - pub max: Option, + pub elem: Vec, + pub max: Option, } pub struct ExportInst { - pub name: String, - pub value: ExternVal, + pub name: String, + pub value: ExternVal, } pub struct FuncInstStore(Vec); @@ -83,10 +84,10 @@ pub struct GlobalAddr(Addr); #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum ExternVal { - Func(FuncAddr), - Table(TableAddr), - Memory(MemAddr), - Global(GlobalAddr), + Func(FuncAddr), + Table(TableAddr), + Memory(MemAddr), + Global(GlobalAddr), } // Constants @@ -94,53 +95,59 @@ pub const PAGE_SIZE: usize = 65536; // Traits impl ModuleInst { - pub fn new() -> ModuleInst { - ModuleInst { - types: Vec::new(), - func_addrs: Vec::new(), - table_addrs: Vec::new(), - mem_addrs: Vec::new(), - global_addrs: Vec::new(), - exports: Vec::new(), - } - } + pub fn new() -> ModuleInst { + ModuleInst { + types: Vec::new(), + func_addrs: Vec::new(), + table_addrs: Vec::new(), + mem_addrs: Vec::new(), + global_addrs: Vec::new(), + exports: Vec::new(), + } + } +} + +impl Default for ModuleInst { + fn default() -> Self { + Self::new() + } } macro_rules! impl_inst_store { - ($StoreType:tt, $InnerType:ty, $AddrType:tt) => ( - impl $StoreType { - pub fn new() -> Self { - Self { 0: Vec::new() } - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn contains(&self, addr: $AddrType) -> bool { - self.0.len() >= addr.0 - } - } - - impl Index<$AddrType> for $StoreType { - type Output = $InnerType; - fn index(&self, idx: $AddrType) -> &$InnerType { - self.0.get(idx.0).unwrap() - } - } - - impl IndexMut<$AddrType> for $StoreType { - fn index_mut(&mut self, idx: $AddrType) -> &mut $InnerType { - self.0.get_mut(idx.0).unwrap() - } - } - - impl $AddrType { - pub fn new(addr: Addr) -> $AddrType { - $AddrType { 0: addr } - } - } - ) + ($StoreType:tt, $InnerType:ty, $AddrType:tt) => { + impl $StoreType { + pub fn new() -> Self { + Self { 0: Vec::new() } + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn contains(&self, addr: $AddrType) -> bool { + self.0.len() >= addr.0 + } + } + + impl Index<$AddrType> for $StoreType { + type Output = $InnerType; + fn index(&self, idx: $AddrType) -> &$InnerType { + self.0.get(idx.0).unwrap() + } + } + + impl IndexMut<$AddrType> for $StoreType { + fn index_mut(&mut self, idx: $AddrType) -> &mut $InnerType { + self.0.get_mut(idx.0).unwrap() + } + } + + impl $AddrType { + pub fn new(addr: Addr) -> $AddrType { + $AddrType { 0: addr } + } + } + }; } impl_inst_store!(FuncInstStore, FuncInst, FuncAddr); @@ -150,96 +157,138 @@ impl_inst_store!(MemInstStore, MemInst, MemAddr); // Per trait functions impl FuncInstStore { - pub(crate) fn alloc_module(&mut self, types_map: &mut TypeHashMap, functype: &types::Func, minst: &Rc, code: ast::Func) -> FuncAddr { - self.alloc(types_map, - FuncInst::Module( - ModuleFuncInst { - type_: functype.clone(), - module: Rc::clone(minst), - code: code, - } - ), - functype - ) - } - - pub(crate) fn alloc_host(&mut self, types_map: &mut TypeHashMap, functype: &types::Func, hostfunc: HostFunc) -> FuncAddr { - self.alloc(types_map, - FuncInst::Host( - HostFuncInst { - type_: functype.clone(), - hostcode: hostfunc, - } - ), - functype - ) - } - - fn alloc(&mut self, types_map: &mut TypeHashMap, inst: FuncInst, functype: &types::Func) -> FuncAddr { - self.0.push(inst); - let addr = FuncAddr::new(self.len() - 1); - types_map.insert(TypeKey { extern_val: ExternVal::Func(addr) }, - types::Extern::Func(functype.clone())); - addr - } - + pub(crate) fn alloc_module( + &mut self, + types_map: &mut TypeHashMap, + functype: &types::Func, + minst: &Rc, + code: ast::Func, + ) -> FuncAddr { + self.alloc( + types_map, + FuncInst::Module(ModuleFuncInst { + type_: functype.clone(), + module: Rc::clone(minst), + code, + }), + functype, + ) + } + + pub(crate) fn alloc_host( + &mut self, + types_map: &mut TypeHashMap, + functype: &types::Func, + hostfunc: HostFunc, + ) -> FuncAddr { + self.alloc( + types_map, + FuncInst::Host(HostFuncInst { + type_: functype.clone(), + hostcode: hostfunc, + }), + functype, + ) + } + + fn alloc( + &mut self, + types_map: &mut TypeHashMap, + inst: FuncInst, + functype: &types::Func, + ) -> FuncAddr { + self.0.push(inst); + let addr = FuncAddr::new(self.len() - 1); + types_map.insert( + TypeKey { + extern_val: ExternVal::Func(addr), + }, + types::Extern::Func(functype.clone()), + ); + addr + } } impl MemInstStore { - pub(crate) fn alloc(&mut self, types_map: &mut TypeHashMap, memtype: &types::Memory) -> MemAddr { - self.0.push(MemInst { - data: vec![0; (memtype.limits.min as usize) * PAGE_SIZE], - max: memtype.limits.max, - }); - let addr = MemAddr::new(self.len() - 1); - types_map.insert(TypeKey { extern_val: ExternVal::Memory(addr) }, - types::Extern::Memory(memtype.clone())); - addr - } - - pub(crate) fn grow(&mut self, memaddr: MemAddr, new: usize) -> Option { - let mem = &mut self[memaddr]; - let sz = mem.data.len() / PAGE_SIZE; - if let Some(max) = mem.max { - if (max as usize) < sz + new { - return None - } - } - // Can't allocate more than 4GB since its a 32-bits machine - if sz + new > ((1u64 << 32) / PAGE_SIZE as u64) as usize { - return None - } - mem.data.resize((sz + new) * PAGE_SIZE, 0); - Some(sz) - } - - pub(crate) fn size(&self, memaddr: MemAddr) -> usize { - self[memaddr].data.len() / PAGE_SIZE - } + pub(crate) fn alloc( + &mut self, + types_map: &mut TypeHashMap, + memtype: &types::Memory, + ) -> MemAddr { + self.0.push(MemInst { + data: vec![0; (memtype.limits.min as usize) * PAGE_SIZE], + max: memtype.limits.max, + }); + let addr = MemAddr::new(self.len() - 1); + types_map.insert( + TypeKey { + extern_val: ExternVal::Memory(addr), + }, + types::Extern::Memory(memtype.clone()), + ); + addr + } + + pub(crate) fn grow(&mut self, memaddr: MemAddr, new: usize) -> Option { + let mem = &mut self[memaddr]; + let sz = mem.data.len() / PAGE_SIZE; + if let Some(max) = mem.max { + if (max as usize) < sz + new { + return None; + } + } + // Can't allocate more than 4GB since its a 32-bits machine + if sz + new > ((1u64 << 32) / PAGE_SIZE as u64) as usize { + return None; + } + mem.data.resize((sz + new) * PAGE_SIZE, 0); + Some(sz) + } + + pub(crate) fn size(&self, memaddr: MemAddr) -> usize { + self[memaddr].data.len() / PAGE_SIZE + } } impl TableInstStore { - pub(crate) fn alloc(&mut self, types_map: &mut TypeHashMap, tabletype: &types::Table) -> TableAddr { - self.0.push(TableInst { - elem: vec![None; tabletype.limits.min as usize], - max: tabletype.limits.max, - }); - let addr = TableAddr::new(self.len() - 1); - types_map.insert(TypeKey { extern_val: ExternVal::Table(addr) }, - types::Extern::Table(tabletype.clone())); - addr - } + pub(crate) fn alloc( + &mut self, + types_map: &mut TypeHashMap, + tabletype: &types::Table, + ) -> TableAddr { + self.0.push(TableInst { + elem: vec![None; tabletype.limits.min as usize], + max: tabletype.limits.max, + }); + let addr = TableAddr::new(self.len() - 1); + types_map.insert( + TypeKey { + extern_val: ExternVal::Table(addr), + }, + types::Extern::Table(tabletype.clone()), + ); + addr + } } impl GlobalInstStore { - pub(crate) fn alloc(&mut self, types_map: &mut TypeHashMap, globaltype: &types::Global, val: values::Value) -> GlobalAddr { - self.0.push(GlobalInst { - value: val, - mutable: globaltype.mutable, - }); - let addr = GlobalAddr::new(self.len() - 1); - types_map.insert(TypeKey { extern_val: ExternVal::Global(addr) }, - types::Extern::Global(globaltype.clone())); - addr - } + pub(crate) fn alloc( + &mut self, + types_map: &mut TypeHashMap, + globaltype: &types::Global, + val: values::Value, + ) -> GlobalAddr { + self.0.push(GlobalInst { + value: val, + mutable: globaltype.mutable, + }); + let addr = GlobalAddr::new(self.len() - 1); + types_map.insert( + TypeKey { + extern_val: ExternVal::Global(addr), + }, + types::Extern::Global(globaltype.clone()), + ); + addr + } } diff --git a/src/types.rs b/src/types.rs index 240ffec..f1ae4d7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,28 +1,28 @@ #[derive(Debug, PartialEq, Clone, Copy)] pub enum Float { - F32, - F64, + F32, + F64, } #[derive(Debug, PartialEq, Clone, Copy)] pub enum Int { - I32, - I64, + I32, + I64, } #[derive(Debug, PartialEq, Clone, Copy)] pub enum Value { - Int(Int), - Float(Float), + Int(Int), + Float(Float), } impl Value { - pub fn bit_width(&self) -> u32 { - match *self { - Value::Int(Int::I32) | Value::Float(Float::F32) => 32, - Value::Int(Int::I64) | Value::Float(Float::F64) => 64, - } - } + pub fn bit_width(&self) -> u32 { + match *self { + Value::Int(Int::I32) | Value::Float(Float::F32) => 32, + Value::Int(Int::I64) | Value::Float(Float::F64) => 64, + } + } } // Useful aliases for value types @@ -31,73 +31,73 @@ pub static I64: Value = Value::Int(Int::I64); pub static F32: Value = Value::Float(Float::F32); pub static F64: Value = Value::Float(Float::F64); - #[derive(Debug, Clone, PartialEq)] pub enum Elem { - AnyFunc, + AnyFunc, } #[derive(Debug, Clone, PartialEq)] pub struct Func { - pub args: Vec, - pub result: Vec, + pub args: Vec, + pub result: Vec, } // Note: Do not implement PartialEq on Limits, Limits comparison is specified and not straightforward #[derive(Debug, Clone)] pub struct Limits { - pub min: u32, - pub max: Option, + pub min: u32, + pub max: Option, } impl Limits { - /// Check if a limit matches another according to import matching rule on limits - fn matches(&self, l2: &Limits) -> bool { - self.min >= l2.min && (l2.max.is_none() || (self.max.is_some() && self.max.unwrap() <= l2.max.unwrap())) - } + /// Check if a limit matches another according to import matching rule on limits + fn matches(&self, l2: &Limits) -> bool { + self.min >= l2.min + && (l2.max.is_none() || (self.max.is_some() && self.max.unwrap() <= l2.max.unwrap())) + } } #[derive(Debug, Clone)] pub struct Table { - pub limits: Limits, - pub elem: Elem, + pub limits: Limits, + pub elem: Elem, } #[derive(Debug, Clone)] pub struct Memory { - pub limits: Limits, + pub limits: Limits, } #[derive(Debug, Clone, PartialEq)] pub struct Global { - pub value: Value, - pub mutable: bool, + pub value: Value, + pub mutable: bool, } /// Types of elements defined externally to the module #[derive(Debug)] pub enum Extern { - Func(Func), - Table(Table), - Memory(Memory), - Global(Global), + Func(Func), + Table(Table), + Memory(Memory), + Global(Global), } impl Extern { - /// Check if an external type matches another. - /// - /// When instantiating a module, external values must be provided whose types - /// are matched against the respective external types classifying each import. - /// In some cases, this allows for a simple form of subtyping. - pub fn matches(&self, other: &Extern) -> bool { - use self::Extern::*; + /// Check if an external type matches another. + /// + /// When instantiating a module, external values must be provided whose types + /// are matched against the respective external types classifying each import. + /// In some cases, this allows for a simple form of subtyping. + pub fn matches(&self, other: &Extern) -> bool { + use self::Extern::*; - match (self, other) { - (Func(f1), Func(f2)) => f1 == f2, - (Table(t1), Table(t2)) => t1.elem == t2.elem && t1.limits.matches(&t2.limits), - (Memory(m1), Memory(m2)) => m1.limits.matches(&m2.limits), - (Global(g1), Global(g2)) => g1 == g2, - _ => false, - } - } + match (self, other) { + (Func(f1), Func(f2)) => f1 == f2, + (Table(t1), Table(t2)) => t1.elem == t2.elem && t1.limits.matches(&t2.limits), + (Memory(m1), Memory(m2)) => m1.limits.matches(&m2.limits), + (Global(g1), Global(g2)) => g1 == g2, + _ => false, + } + } } diff --git a/src/valid.rs b/src/valid.rs index c0491bd..7e215a4 100644 --- a/src/valid.rs +++ b/src/valid.rs @@ -1,53 +1,53 @@ use ast; use runtime::PAGE_SIZE; use types; -use types::Value::*; -use types::Int::*; use types::Float::*; +use types::Int::*; +use types::Value::*; use std::collections::HashSet; static EMPTY_TYPE: [types::Value; 0] = []; pub fn is_valid(module: &ast::Module) -> bool { - check_module(module).is_some() + check_module(module).is_some() } #[derive(PartialEq, Clone, Copy)] /// Represent the type of an operand on the stack of the machine. enum Operand { - /// Operand with any type. This is used by polymorphic instructions. - Any, - /// Operand with an exact type. - Exact(types::Value), + /// Operand with any type. This is used by polymorphic instructions. + Any, + /// Operand with an exact type. + Exact(types::Value), } /// A `Frame` record an entered block. struct Frame<'a> { - /// Type of label associated to the block - label_type: &'a[types::Value], - /// Result type of the block - end_type: &'a[types::Value], - /// Height of the operand stack at the start of the block - init_len: usize, - /// Whether the rest of the block is unreachable (used to handle stack-polymorphic - /// typing after branches) - unreachable: bool, + /// Type of label associated to the block + label_type: &'a [types::Value], + /// Result type of the block + end_type: &'a [types::Value], + /// Height of the operand stack at the start of the block + init_len: usize, + /// Whether the rest of the block is unreachable (used to handle stack-polymorphic + /// typing after branches) + unreachable: bool, } /// A typing context for a module struct ModContext<'a> { - types: &'a [types::Func], - funcs: Vec<&'a types::Func>, - tables: Vec<&'a types::Table>, - memories: Vec<&'a types::Memory>, - globals: Vec<&'a types::Global>, + types: &'a [types::Func], + funcs: Vec<&'a types::Func>, + tables: Vec<&'a types::Table>, + memories: Vec<&'a types::Memory>, + globals: Vec<&'a types::Global>, } /// A typing context for a function struct FuncContext { - locals: Vec, - return_type: Vec, + locals: Vec, + return_type: Vec, } // Instead of indicating the validity of a component through a boolean, we use @@ -56,535 +56,581 @@ struct FuncContext { // up any error encountered along the way. fn require(b: bool) -> Option<()> { - if b { Some(()) } else { None } + if b { + Some(()) + } else { + None + } } fn pop_operand(operands: &mut Vec, frames: &Vec) -> Option { - debug_assert!( - !frames.is_empty(), - "validation of instructions should always happen in a frame" - ); - let curr_frame = frames.last().unwrap(); - - if curr_frame.unreachable && operands.len() == curr_frame.init_len { - // If the block is marked unreachable, the current operand stack is polymorphic. - // Therefore any value will do if the operand stack of the frame is empty. - return Some(Operand::Any); - } - - if operands.len() <= curr_frame.init_len { - None - } else { - operands.pop() - } + debug_assert!( + !frames.is_empty(), + "validation of instructions should always happen in a frame" + ); + let curr_frame = frames.last().unwrap(); + + if curr_frame.unreachable && operands.len() == curr_frame.init_len { + // If the block is marked unreachable, the current operand stack is polymorphic. + // Therefore any value will do if the operand stack of the frame is empty. + return Some(Operand::Any); + } + + if operands.len() <= curr_frame.init_len { + None + } else { + operands.pop() + } } fn pop_expected( - operands: &mut Vec, - frames: &Vec, - expected: Operand + operands: &mut Vec, + frames: &Vec, + expected: Operand, ) -> Option { - let actual = pop_operand(operands, frames)?; - - match (actual, expected) { - (Operand::Any, _) => Some(expected), // can fit any expectation - (_, Operand::Any) => Some(actual), // no constraint - (Operand::Exact(t1), Operand::Exact(t2)) if t1 == t2 => Some(actual), - _ => None, - } + let actual = pop_operand(operands, frames)?; + + match (actual, expected) { + (Operand::Any, _) => Some(expected), // can fit any expectation + (_, Operand::Any) => Some(actual), // no constraint + (Operand::Exact(t1), Operand::Exact(t2)) if t1 == t2 => Some(actual), + _ => None, + } } fn exact_step( - operands: &mut Vec, - frames: &Vec, - from: &[types::Value], - to: &[types::Value], + operands: &mut Vec, + frames: &Vec, + from: &[types::Value], + to: &[types::Value], ) -> Option<()> { - for expected in from.iter().rev() { - let _ = pop_expected(operands, frames, Operand::Exact(*expected))?; - } + for expected in from.iter().rev() { + let _ = pop_expected(operands, frames, Operand::Exact(*expected))?; + } - for new in to.iter() { - operands.push(Operand::Exact(*new)); - } + for new in to.iter() { + operands.push(Operand::Exact(*new)); + } - Some(()) + Some(()) } fn push_frame<'a>( - frames: &mut Vec>, - label_type: &'a [types::Value], - end_type: &'a [types::Value], - operands_len: usize, + frames: &mut Vec>, + label_type: &'a [types::Value], + end_type: &'a [types::Value], + operands_len: usize, ) { - frames.push(Frame { - label_type: label_type, - end_type: end_type, - init_len: operands_len, - unreachable: false, - }); + frames.push(Frame { + label_type, + end_type, + init_len: operands_len, + unreachable: false, + }); } fn pop_frame(frames: &mut Vec, operands: &mut Vec) -> Option<()> { - debug_assert!( - !frames.is_empty(), - "validation of instructions should always happen in a frame" - ); - - let end_type_len = { - let end_type = &frames.last().unwrap().end_type[..]; - exact_step(operands, frames, end_type, end_type)?; - end_type.len() - }; - let frame = frames.pop().unwrap(); - require(frame.init_len == operands.len() - end_type_len) + debug_assert!( + !frames.is_empty(), + "validation of instructions should always happen in a frame" + ); + + let end_type_len = { + let end_type = frames.last().unwrap().end_type; + exact_step(operands, frames, end_type, end_type)?; + end_type.len() + }; + let frame = frames.pop().unwrap(); + require(frame.init_len == operands.len() - end_type_len) } fn unreachable(frames: &mut Vec, operands: &mut Vec) { - debug_assert!( - !frames.is_empty(), - "validation of instructions should always happen in a frame" - ); - let curr_frame = frames.last_mut().unwrap(); - - operands.truncate(curr_frame.init_len); - curr_frame.unreachable = true; + debug_assert!( + !frames.is_empty(), + "validation of instructions should always happen in a frame" + ); + let curr_frame = frames.last_mut().unwrap(); + + operands.truncate(curr_frame.init_len); + curr_frame.unreachable = true; } fn get_label<'a>(frames: &Vec>, nesting_levels: u32) -> Option<&'a [types::Value]> { - debug_assert!( - !frames.is_empty(), - "validation of instructions should always happen in a frame" - ); - - if (nesting_levels as usize) < frames.len() { - Some(frames[frames.len() - nesting_levels as usize - 1].label_type) - } else { - None - } + debug_assert!( + !frames.is_empty(), + "validation of instructions should always happen in a frame" + ); + + if (nesting_levels as usize) < frames.len() { + Some(frames[frames.len() - nesting_levels as usize - 1].label_type) + } else { + None + } } -fn check_const_expr(mod_ctx: &ModContext, instrs: &[ast::Instr], result: types::Value) -> Option<()> { - // Right now constant expressions are limited to Const and GetGlobal. A - // direct consequence is that to be valid with type `result`, a constant - // expression must have a single instruction that leaves a value of type - // `result` on the operand stack. - - require(instrs.len() == 1)?; - - match instrs[0] { - ast::Instr::Const(v) => require(v.type_() == result), - ast::Instr::GetGlobal(x) => { - let global = mod_ctx.globals.get(x as usize)?; - require(!global.mutable && global.value == result) - } - _ => None, - } +fn check_const_expr( + mod_ctx: &ModContext, + instrs: &[ast::Instr], + result: types::Value, +) -> Option<()> { + // Right now constant expressions are limited to Const and GetGlobal. A + // direct consequence is that to be valid with type `result`, a constant + // expression must have a single instruction that leaves a value of type + // `result` on the operand stack. + + require(instrs.len() == 1)?; + + match instrs[0] { + ast::Instr::Const(v) => require(v.type_() == result), + ast::Instr::GetGlobal(x) => { + let global = mod_ctx.globals.get(x as usize)?; + require(!global.mutable && global.value == result) + } + _ => None, + } } /// Check that the instruction sequence `instrs` is valid and has type `end_type`. /// The result is left on the stack. fn check_expr<'a>( - mod_ctx: &ModContext, - func_ctx: &FuncContext, - operands: &mut Vec, - frames: &mut Vec>, - label_type: &'a [types::Value], - end_type: &'a [types::Value], - instrs: &'a [ast::Instr], + mod_ctx: &ModContext, + func_ctx: &FuncContext, + operands: &mut Vec, + frames: &mut Vec>, + label_type: &'a [types::Value], + end_type: &'a [types::Value], + instrs: &'a [ast::Instr], ) -> Option<()> { - push_frame(frames, label_type, end_type, operands.len()); + push_frame(frames, label_type, end_type, operands.len()); - for instr in instrs { - check_instr(mod_ctx, func_ctx, operands, frames, instr)?; - } + for instr in instrs { + check_instr(mod_ctx, func_ctx, operands, frames, instr)?; + } - pop_frame(frames, operands) + pop_frame(frames, operands) } fn check_instr<'a>( - mod_ctx: &ModContext, - func_ctx: &FuncContext, - operands: &mut Vec, - frames: &mut Vec>, - instr: &'a ast::Instr, + mod_ctx: &ModContext, + func_ctx: &FuncContext, + operands: &mut Vec, + frames: &mut Vec>, + instr: &'a ast::Instr, ) -> Option<()> { - use ast::Instr::*; - - match *instr { - Nop => {} - - Unreachable => { - unreachable(frames, operands); - } - - Block(ref result_type, ref instrs) => { - check_expr(mod_ctx, func_ctx, operands, frames, &result_type[..], &result_type[..], instrs)?; - } - - Loop(ref result_type, ref instrs) => { - check_expr(mod_ctx, func_ctx, operands, frames, &EMPTY_TYPE[..], &result_type[..], instrs)?; - } - - If(ref result_type, ref instrs_then, ref instrs_else) => { - pop_expected(operands, frames, Operand::Exact(Int(I32)))?; - check_expr(mod_ctx, func_ctx, operands, frames, &result_type[..], &result_type[..], instrs_then)?; - // if the "if" part has a return type, the else part must be checked, even if it is empty - if !instrs_else.is_empty() || !result_type.is_empty(){ - // if there is an "else", we need to remove the result of the "then" part first - for _ in result_type { - let _ = pop_operand(operands, frames)?; - } - check_expr(mod_ctx, func_ctx, operands, frames, &result_type[..], &result_type[..], instrs_else)?; - } - } - - Br(nesting_levels) => { - { - let label_type = get_label(frames, nesting_levels)?; - exact_step(operands, frames, label_type, &[])?; - } - unreachable(frames, operands); - } - - BrIf(nesting_levels) => { - pop_expected(operands, frames, Operand::Exact(Int(I32)))?; - let label_type = get_label(frames, nesting_levels)?; - exact_step(operands, frames, label_type, label_type)?; - } - - BrTable(ref choices, default) => { - { - pop_expected(operands, frames, Operand::Exact(Int(I32)))?; - // all labels must have the same type - let label_type = get_label(frames, default)?; - for choice in choices { - require(get_label(frames, *choice)? == &label_type[..])?; - } - exact_step(operands, frames, label_type, &[])?; - } - unreachable(frames, operands); - } - - Return => { - exact_step(operands, frames, &func_ctx.return_type[..], &[])?; - unreachable(frames, operands); - } - - Call(func_index) => { - let func = mod_ctx.funcs.get(func_index as usize)?; - exact_step(operands, frames, &func.args[..], &func.result[..])?; - } - - CallIndirect(index) => { - let _ = mod_ctx.tables.get(0)?; - let func = mod_ctx.types.get(index as usize)?; - pop_expected(operands, frames, Operand::Exact(Int(I32)))?; - exact_step(operands, frames, &func.args[..], &func.result[..])?; - } - - Drop_ => { - let _ = pop_operand(operands, frames)?; - } - - Select => { - pop_expected(operands, frames, Operand::Exact(Int(I32)))?; - let t = pop_operand(operands, frames)?; - let _ = pop_expected(operands, frames, t)?; - operands.push(t); - } - - GetLocal(x) => { - let t = *func_ctx.locals.get(x as usize)?; - exact_step(operands, frames, &[], &[t])?; - } - - SetLocal(x) => { - let t = *func_ctx.locals.get(x as usize)?; - exact_step(operands, frames, &[t], &[])?; - } - - TeeLocal(x) => { - let t = *func_ctx.locals.get(x as usize)?; - exact_step(operands, frames, &[t], &[t])?; - } - - GetGlobal(x) => { - let t = mod_ctx.globals.get(x as usize)?.value; - exact_step(operands, frames, &[], &[t])?; - } - - SetGlobal(x) => { - let t = { - let global = mod_ctx.globals.get(x as usize)?; - require(global.mutable)?; - global.value - }; - exact_step(operands, frames, &[t], &[])?; - } - - Load(ref load_op) => { - check_mem_op(mod_ctx, load_op, |&(size, _)| size)?; - exact_step(operands, frames, &[Int(I32)], &[load_op.type_])?; - } - - Store(ref store_op) => { - check_mem_op(mod_ctx, store_op, |&size| size)?; - exact_step(operands, frames, &[Int(I32), store_op.type_], &[])?; - } - - CurrentMemory => { - require(!mod_ctx.memories.is_empty())?; - exact_step(operands, frames, &[], &[Int(I32)])?; - } - - GrowMemory => { - require(!mod_ctx.memories.is_empty())?; - exact_step(operands, frames, &[Int(I32)], &[Int(I32)])?; - } - - Const(v) => operands.push(Operand::Exact(v.type_())), - - ITest(t, _) => { - exact_step(operands, frames, &[Int(t)], &[Int(I32)])?; - } - - IUnary(t, _) => { - exact_step(operands, frames, &[Int(t)], &[Int(t)])?; - } - - FUnary(t, _) => { - exact_step(operands, frames, &[Float(t)], &[Float(t)])?; - } - - IBin(t, _) => { - exact_step(operands, frames, &[Int(t), Int(t)], &[Int(t)])?; - } - - FBin(t, _) => { - exact_step(operands, frames, &[Float(t), Float(t)], &[Float(t)])?; - } - - IRel(t, _) => { - exact_step(operands, frames, &[Int(t), Int(t)], &[Int(I32)])?; - } - - FRel(t, _) => { - exact_step(operands, frames, &[Float(t), Float(t)], &[Int(I32)])?; - } - - Convert(ref convert_op) => { - check_convert_op(operands, frames, convert_op)?; - } - } - - Some(()) + use ast::Instr::*; + + match *instr { + Nop => {} + + Unreachable => { + unreachable(frames, operands); + } + + Block(ref result_type, ref instrs) => { + check_expr( + mod_ctx, + func_ctx, + operands, + frames, + &result_type[..], + &result_type[..], + instrs, + )?; + } + + Loop(ref result_type, ref instrs) => { + check_expr( + mod_ctx, + func_ctx, + operands, + frames, + &EMPTY_TYPE[..], + &result_type[..], + instrs, + )?; + } + + If(ref result_type, ref instrs_then, ref instrs_else) => { + pop_expected(operands, frames, Operand::Exact(Int(I32)))?; + check_expr( + mod_ctx, + func_ctx, + operands, + frames, + &result_type[..], + &result_type[..], + instrs_then, + )?; + // if the "if" part has a return type, the else part must be checked, even if it is empty + if !instrs_else.is_empty() || !result_type.is_empty() { + // if there is an "else", we need to remove the result of the "then" part first + for _ in result_type { + let _ = pop_operand(operands, frames)?; + } + check_expr( + mod_ctx, + func_ctx, + operands, + frames, + &result_type[..], + &result_type[..], + instrs_else, + )?; + } + } + + Br(nesting_levels) => { + { + let label_type = get_label(frames, nesting_levels)?; + exact_step(operands, frames, label_type, &[])?; + } + unreachable(frames, operands); + } + + BrIf(nesting_levels) => { + pop_expected(operands, frames, Operand::Exact(Int(I32)))?; + let label_type = get_label(frames, nesting_levels)?; + exact_step(operands, frames, label_type, label_type)?; + } + + BrTable(ref choices, default) => { + { + pop_expected(operands, frames, Operand::Exact(Int(I32)))?; + // all labels must have the same type + let label_type = get_label(frames, default)?; + for choice in choices { + require(get_label(frames, *choice)? == label_type)?; + } + exact_step(operands, frames, label_type, &[])?; + } + unreachable(frames, operands); + } + + Return => { + exact_step(operands, frames, &func_ctx.return_type[..], &[])?; + unreachable(frames, operands); + } + + Call(func_index) => { + let func = mod_ctx.funcs.get(func_index as usize)?; + exact_step(operands, frames, &func.args[..], &func.result[..])?; + } + + CallIndirect(index) => { + let _ = mod_ctx.tables.get(0)?; + let func = mod_ctx.types.get(index as usize)?; + pop_expected(operands, frames, Operand::Exact(Int(I32)))?; + exact_step(operands, frames, &func.args[..], &func.result[..])?; + } + + Drop_ => { + let _ = pop_operand(operands, frames)?; + } + + Select => { + pop_expected(operands, frames, Operand::Exact(Int(I32)))?; + let t = pop_operand(operands, frames)?; + let _ = pop_expected(operands, frames, t)?; + operands.push(t); + } + + GetLocal(x) => { + let t = *func_ctx.locals.get(x as usize)?; + exact_step(operands, frames, &[], &[t])?; + } + + SetLocal(x) => { + let t = *func_ctx.locals.get(x as usize)?; + exact_step(operands, frames, &[t], &[])?; + } + + TeeLocal(x) => { + let t = *func_ctx.locals.get(x as usize)?; + exact_step(operands, frames, &[t], &[t])?; + } + + GetGlobal(x) => { + let t = mod_ctx.globals.get(x as usize)?.value; + exact_step(operands, frames, &[], &[t])?; + } + + SetGlobal(x) => { + let t = { + let global = mod_ctx.globals.get(x as usize)?; + require(global.mutable)?; + global.value + }; + exact_step(operands, frames, &[t], &[])?; + } + + Load(ref load_op) => { + check_mem_op(mod_ctx, load_op, |&(size, _)| size)?; + exact_step(operands, frames, &[Int(I32)], &[load_op.type_])?; + } + + Store(ref store_op) => { + check_mem_op(mod_ctx, store_op, |&size| size)?; + exact_step(operands, frames, &[Int(I32), store_op.type_], &[])?; + } + + CurrentMemory => { + require(!mod_ctx.memories.is_empty())?; + exact_step(operands, frames, &[], &[Int(I32)])?; + } + + GrowMemory => { + require(!mod_ctx.memories.is_empty())?; + exact_step(operands, frames, &[Int(I32)], &[Int(I32)])?; + } + + Const(v) => operands.push(Operand::Exact(v.type_())), + + ITest(t, _) => { + exact_step(operands, frames, &[Int(t)], &[Int(I32)])?; + } + + IUnary(t, _) => { + exact_step(operands, frames, &[Int(t)], &[Int(t)])?; + } + + FUnary(t, _) => { + exact_step(operands, frames, &[Float(t)], &[Float(t)])?; + } + + IBin(t, _) => { + exact_step(operands, frames, &[Int(t), Int(t)], &[Int(t)])?; + } + + FBin(t, _) => { + exact_step(operands, frames, &[Float(t), Float(t)], &[Float(t)])?; + } + + IRel(t, _) => { + exact_step(operands, frames, &[Int(t), Int(t)], &[Int(I32)])?; + } + + FRel(t, _) => { + exact_step(operands, frames, &[Float(t), Float(t)], &[Int(I32)])?; + } + + Convert(ref convert_op) => { + check_convert_op(operands, frames, convert_op)?; + } + } + + Some(()) } fn check_mem_op(mod_ctx: &ModContext, load_op: &ast::MemOp, get_size: F) -> Option<()> - where F: Fn(&T) -> u32 +where + F: Fn(&T) -> u32, { - require(!mod_ctx.memories.is_empty())?; + require(!mod_ctx.memories.is_empty())?; - let size = match load_op.opt { - Some(ref opt) => get_size(opt), - None => load_op.type_.bit_width(), - }; + let size = match load_op.opt { + Some(ref opt) => get_size(opt), + None => load_op.type_.bit_width(), + }; - if 1 << load_op.align > size / 8 { - return None; - } + if 1 << load_op.align > size / 8 { + return None; + } - Some(()) + Some(()) } fn check_convert_op( - operands: &mut Vec, - frames: &mut Vec, - convert_op: &ast::ConvertOp, + operands: &mut Vec, + frames: &mut Vec, + convert_op: &ast::ConvertOp, ) -> Option<()> { - use ast::ConvertOp::*; - - match *convert_op { - I32WrapI64 => { - exact_step(operands, frames, &[Int(I64)], &[Int(I32)]) - } - I64ExtendUI32 | I64ExtendSI32 => { - exact_step(operands, frames, &[Int(I32)], &[Int(I64)]) - } - F32DemoteF64 => { - exact_step(operands, frames, &[Float(F64)], &[Float(F32)]) - } - F64PromoteF32 => { - exact_step(operands, frames, &[Float(F32)], &[Float(F64)]) - } - Reinterpret { from, to, .. } => { - exact_step(operands, frames, &[from], &[to]) - } - Trunc { from, to, .. } => { - exact_step(operands, frames, &[Float(from)], &[Int(to)]) - } - Convert { from, to, .. } => { - exact_step(operands, frames, &[Int(from)], &[Float(to)]) - } - } + use ast::ConvertOp::*; + + match *convert_op { + I32WrapI64 => exact_step(operands, frames, &[Int(I64)], &[Int(I32)]), + I64ExtendUI32 | I64ExtendSI32 => exact_step(operands, frames, &[Int(I32)], &[Int(I64)]), + F32DemoteF64 => exact_step(operands, frames, &[Float(F64)], &[Float(F32)]), + F64PromoteF32 => exact_step(operands, frames, &[Float(F32)], &[Float(F64)]), + Reinterpret { from, to, .. } => exact_step(operands, frames, &[from], &[to]), + Trunc { from, to, .. } => exact_step(operands, frames, &[Float(from)], &[Int(to)]), + Convert { from, to, .. } => exact_step(operands, frames, &[Int(from)], &[Float(to)]), + } } fn check_func(mod_ctx: &ModContext, func: &ast::Func) -> Option<()> { - // TODO: cache those vectors to reuse allocated memory - let mut frames = Vec::new(); - let mut operands = Vec::new(); - - let t = mod_ctx.types.get(func.type_index as usize)?; - - let mut locals = t.args.clone(); - locals.extend(func.locals.iter().cloned()); - let return_type = t.result.clone(); - - let func_ctx = FuncContext { locals, return_type }; - - check_expr(mod_ctx, &func_ctx, &mut operands, &mut frames, &t.result[..], &t.result[..], &func.body[..]) + // TODO: cache those vectors to reuse allocated memory + let mut frames = Vec::new(); + let mut operands = Vec::new(); + + let t = mod_ctx.types.get(func.type_index as usize)?; + + let mut locals = t.args.clone(); + locals.extend(func.locals.iter().cloned()); + let return_type = t.result.clone(); + + let func_ctx = FuncContext { + locals, + return_type, + }; + + check_expr( + mod_ctx, + &func_ctx, + &mut operands, + &mut frames, + &t.result[..], + &t.result[..], + &func.body[..], + ) } fn check_type(type_: &types::Func) -> Option<()> { - require(type_.result.len() <= 1) // May be lifted in future versions + require(type_.result.len() <= 1) // May be lifted in future versions } fn check_limits(limits: &types::Limits) -> Option<()> { - match limits.max { - Some(max) if limits.min > max => None, - _ => Some(()), - } + match limits.max { + Some(max) if limits.min > max => None, + _ => Some(()), + } } fn check_table(table: &ast::Table) -> Option<()> { - check_limits(&table.type_.limits) + check_limits(&table.type_.limits) } fn check_memory(mem: &ast::Memory) -> Option<()> { - // Can't allocate more than 4GB since its a 32-bits machine - let max = (1u64 << 32) / PAGE_SIZE as u64; - if mem.type_.limits.min as u64 > max || - (mem.type_.limits.max.is_some() && - mem.type_.limits.max.unwrap() as u64 > max) { - return None - } - check_limits(&mem.type_.limits) + // Can't allocate more than 4GB since its a 32-bits machine + let max = (1u64 << 32) / PAGE_SIZE as u64; + if mem.type_.limits.min as u64 > max + || (mem.type_.limits.max.is_some() && mem.type_.limits.max.unwrap() as u64 > max) + { + return None; + } + check_limits(&mem.type_.limits) } fn check_global(mod_ctx: &ModContext, global: &ast::Global) -> Option<()> { - check_const_expr(mod_ctx, &global.value, global.type_.value) + check_const_expr(mod_ctx, &global.value, global.type_.value) } fn check_elem(mod_ctx: &ModContext, elem: &ast::Segment) -> Option<()> { - let _ = mod_ctx.tables.get(elem.index as usize)?; - for index in &elem.init { - let _ = mod_ctx.funcs.get(*index as usize)?; - } - check_const_expr(mod_ctx, &elem.offset, Int(I32)) + let _ = mod_ctx.tables.get(elem.index as usize)?; + for index in &elem.init { + let _ = mod_ctx.funcs.get(*index as usize)?; + } + check_const_expr(mod_ctx, &elem.offset, Int(I32)) } fn check_data(mod_ctx: &ModContext, data: &ast::Segment) -> Option<()> { - let _ = mod_ctx.memories.get(data.index as usize)?; - check_const_expr(mod_ctx, &data.offset, Int(I32)) + let _ = mod_ctx.memories.get(data.index as usize)?; + check_const_expr(mod_ctx, &data.offset, Int(I32)) } fn check_start(mod_ctx: &ModContext, start: &ast::Index) -> Option<()> { - let func = mod_ctx.funcs.get(*start as usize)?; - require(func.args.is_empty() && func.result.is_empty()) + let func = mod_ctx.funcs.get(*start as usize)?; + require(func.args.is_empty() && func.result.is_empty()) } fn check_export(mod_ctx: &ModContext, export: &ast::Export) -> Option<()> { - use ast::ExportDesc::*; - - match export.desc { - Func(x) => require((x as usize) < mod_ctx.funcs.len()), - Table(x) => require((x as usize) < mod_ctx.tables.len()), - Memory(x) => require((x as usize) < mod_ctx.memories.len()), - Global(x) => require((x as usize) < mod_ctx.globals.len()), - } + use ast::ExportDesc::*; + + match export.desc { + Func(x) => require((x as usize) < mod_ctx.funcs.len()), + Table(x) => require((x as usize) < mod_ctx.tables.len()), + Memory(x) => require((x as usize) < mod_ctx.memories.len()), + Global(x) => require((x as usize) < mod_ctx.globals.len()), + } } /// Validate an import and insert it into the context of the module fn check_import<'a>(ctx: &mut ModContext<'a>, import: &'a ast::Import) -> Option<()> { - use ast::ImportDesc::*; - - match import.desc { - Func(x) => { - ctx.funcs.push(ctx.types.get(x as usize)?); - } - Table(ref table_type) => { - check_limits(&table_type.limits)?; - ctx.tables.push(table_type); - } - Memory(ref mem_type) => { - check_limits(&mem_type.limits)?; - ctx.memories.push(mem_type); - } - Global(ref global_type) => { - ctx.globals.push(global_type); - } - } - - Some(()) + use ast::ImportDesc::*; + + match import.desc { + Func(x) => { + ctx.funcs.push(ctx.types.get(x as usize)?); + } + Table(ref table_type) => { + check_limits(&table_type.limits)?; + ctx.tables.push(table_type); + } + Memory(ref mem_type) => { + check_limits(&mem_type.limits)?; + ctx.memories.push(mem_type); + } + Global(ref global_type) => { + ctx.globals.push(global_type); + } + } + + Some(()) } fn check_module(module: &ast::Module) -> Option<()> { - // create an empty context with only the types defined in the module - let mut mod_ctx = ModContext { - types: &module.types, - funcs: Vec::new(), - tables: Vec::new(), - memories: Vec::new(), - globals: Vec::new(), - }; - - // first resolve imports from the module - for import in &module.imports { - check_import(&mut mod_ctx, import)?; - } - - // then extend the context with funcs, tables and memories from the module - for func in &module.funcs { - mod_ctx.funcs.push(mod_ctx.types.get(func.type_index as usize)?); - } - mod_ctx.tables.extend(module.tables.iter().map(|table| &table.type_)); - mod_ctx.memories.extend(module.memories.iter().map(|mem| &mem.type_)); - - // check globals before adding them to the context to prevent recursivity - for global in &module.globals { - check_global(&mod_ctx, global)?; - } - mod_ctx.globals.extend(module.globals.iter().map(|global| &global.type_)); - - // finaly check everything else - for type_ in &module.types { - check_type(type_)?; - } - for func in &module.funcs { - check_func(&mod_ctx, func)?; - } - for table in &module.tables { - check_table(table)?; - } - for mem in &module.memories { - check_memory(mem)?; - } - for elem in &module.elems { - check_elem(&mod_ctx, elem)?; - } - for data in &module.data { - check_data(&mod_ctx, data)?; - } - if let Some(ref func) = module.start { - check_start(&mod_ctx, func)?; - } - let mut unique_exports = HashSet::new(); - for export in &module.exports { - check_export(&mod_ctx, export)?; - require(!unique_exports.contains(&export.name))?; - unique_exports.insert(&export.name); - } - - require(mod_ctx.tables.len() <= 1 && mod_ctx.memories.len() <= 1) + // create an empty context with only the types defined in the module + let mut mod_ctx = ModContext { + types: &module.types, + funcs: Vec::new(), + tables: Vec::new(), + memories: Vec::new(), + globals: Vec::new(), + }; + + // first resolve imports from the module + for import in &module.imports { + check_import(&mut mod_ctx, import)?; + } + + // then extend the context with funcs, tables and memories from the module + for func in &module.funcs { + mod_ctx + .funcs + .push(mod_ctx.types.get(func.type_index as usize)?); + } + mod_ctx + .tables + .extend(module.tables.iter().map(|table| &table.type_)); + mod_ctx + .memories + .extend(module.memories.iter().map(|mem| &mem.type_)); + + // check globals before adding them to the context to prevent recursivity + for global in &module.globals { + check_global(&mod_ctx, global)?; + } + mod_ctx + .globals + .extend(module.globals.iter().map(|global| &global.type_)); + + // finaly check everything else + for type_ in &module.types { + check_type(type_)?; + } + for func in &module.funcs { + check_func(&mod_ctx, func)?; + } + for table in &module.tables { + check_table(table)?; + } + for mem in &module.memories { + check_memory(mem)?; + } + for elem in &module.elems { + check_elem(&mod_ctx, elem)?; + } + for data in &module.data { + check_data(&mod_ctx, data)?; + } + if let Some(ref func) = module.start { + check_start(&mod_ctx, func)?; + } + let mut unique_exports = HashSet::new(); + for export in &module.exports { + check_export(&mod_ctx, export)?; + require(!unique_exports.contains(&export.name))?; + unique_exports.insert(&export.name); + } + + require(mod_ctx.tables.len() <= 1 && mod_ctx.memories.len() <= 1) } diff --git a/src/values.rs b/src/values.rs index 8e2d962..e168641 100644 --- a/src/values.rs +++ b/src/values.rs @@ -2,58 +2,58 @@ use types; #[derive(Debug, Clone, Copy)] pub enum Value { - I32(u32), - I64(u64), - F32(f32), - F64(f64), + I32(u32), + I64(u64), + F32(f32), + F64(f64), } impl PartialEq for Value { - /// Two values are equals if they have the same type and they are bitwise equals. - fn eq(&self, other: &Value) -> bool { - use self::Value::*; - - match (*self, *other) { - (I32(a), I32(b)) => a == b, - (I64(a), I64(b)) => a == b, - (F32(a), F32(b)) => a.to_bits() == b.to_bits(), - (F64(a), F64(b)) => a.to_bits() == b.to_bits(), - _ => false, - } - } + /// Two values are equals if they have the same type and they are bitwise equals. + fn eq(&self, other: &Value) -> bool { + use self::Value::*; + + match (*self, *other) { + (I32(a), I32(b)) => a == b, + (I64(a), I64(b)) => a == b, + (F32(a), F32(b)) => a.to_bits() == b.to_bits(), + (F64(a), F64(b)) => a.to_bits() == b.to_bits(), + _ => false, + } + } } impl Value { - pub fn from_i32(v: i32) -> Value { - Value::I32(v as u32) - } - - pub fn from_i64(v: i64) -> Value { - Value::I64(v as u64) - } - - pub fn from_bool(v: bool) -> Value { - if v { - Value::true_() - } else { - Value::false_() - } - } - - pub fn false_() -> Value { - Value::I32(0) - } - - pub fn true_() -> Value { - Value::I32(1) - } - - pub fn type_(&self) -> types::Value { - match *self { - Value::I32(_) => types::I32, - Value::I64(_) => types::I64, - Value::F32(_) => types::F32, - Value::F64(_) => types::F64, - } - } + pub fn from_i32(v: i32) -> Value { + Value::I32(v as u32) + } + + pub fn from_i64(v: i64) -> Value { + Value::I64(v as u64) + } + + pub fn from_bool(v: bool) -> Value { + if v { + Value::true_() + } else { + Value::false_() + } + } + + pub fn false_() -> Value { + Value::I32(0) + } + + pub fn true_() -> Value { + Value::I32(1) + } + + pub fn type_(&self) -> types::Value { + match *self { + Value::I32(_) => types::I32, + Value::I64(_) => types::I64, + Value::F32(_) => types::F32, + Value::F64(_) => types::F64, + } + } } diff --git a/tests/run_suite.rs b/tests/run_suite.rs index 8e0127b..e012050 100644 --- a/tests/run_suite.rs +++ b/tests/run_suite.rs @@ -1,5 +1,5 @@ -extern crate rust_wasm; extern crate hexf_parse; +extern crate rust_wasm; mod script; diff --git a/tests/script/mod.rs b/tests/script/mod.rs index e40e8fd..a282c00 100644 --- a/tests/script/mod.rs +++ b/tests/script/mod.rs @@ -7,33 +7,43 @@ pub use self::run::*; #[derive(Debug)] pub enum Cmd { - ModuleSource(ModuleSource), - Register { name: String, mod_ref: Option }, - Action(Action), - Assertion(Assertion), + ModuleSource(ModuleSource), + Register { + name: String, + mod_ref: Option, + }, + Action(Action), + Assertion(Assertion), } #[derive(Debug)] pub enum ModuleSource { - Binary(Option, Vec), - Quoted(Option, Vec), + Binary(Option, Vec), + Quoted(Option, Vec), } #[derive(Debug)] pub enum Assertion { - Return(Action, Vec), - ReturnCanonicalNan(Action), - ReturnArithmeticNan(Action), - TrapAction(Action, String), - TrapInstantiate(ModuleSource, String), - Exhaustion(Action, String), - Invalid(ModuleSource, String), - Malformed(ModuleSource, String), - Unlinkable(ModuleSource, String), + Return(Action, Vec), + ReturnCanonicalNan(Action), + ReturnArithmeticNan(Action), + TrapAction(Action, String), + TrapInstantiate(ModuleSource, String), + Exhaustion(Action, String), + Invalid(ModuleSource, String), + Malformed(ModuleSource, String), + Unlinkable(ModuleSource, String), } #[derive(Debug)] pub enum Action { - Invoke { mod_ref: Option, func: String, args: Vec }, - Get { mod_ref: Option, global: String }, + Invoke { + mod_ref: Option, + func: String, + args: Vec, + }, + Get { + mod_ref: Option, + global: String, + }, } diff --git a/tests/script/parser.rs b/tests/script/parser.rs index 72f19b4..efc70d0 100644 --- a/tests/script/parser.rs +++ b/tests/script/parser.rs @@ -1,367 +1,386 @@ -use std::convert::TryFrom; -use std::iter::Peekable; -use std::str::{FromStr, CharIndices}; -use std::{f32, f64}; use hexf_parse::{parse_hexf32, parse_hexf64}; use rust_wasm::values::Value; use script::*; +use std::convert::TryFrom; +use std::iter::Peekable; +use std::str::{CharIndices, FromStr}; +use std::{f32, f64}; struct Lexer<'a> { - src: &'a str, - chars: Peekable>, + src: &'a str, + chars: Peekable>, } impl<'a> Lexer<'a> { - fn new(src: &str) -> Lexer { - Lexer { - src: src, - chars: src.char_indices().peekable(), - } - } + fn new(src: &str) -> Lexer { + Lexer { + src, + chars: src.char_indices().peekable(), + } + } } impl<'a> Iterator for Lexer<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option { - loop { - match self.chars.next() { - Some((_, c)) if c.is_whitespace() => {}, - Some((start, '(')) | Some((start, ')')) => return Some(&self.src[start..start+1]), - Some((start, '"')) => { // string - loop { - match self.chars.next() { - Some((i, '\"')) => { - return Some(&self.src[start..i+1]); - } - Some((_, '\\')) => { - let _ = self.chars.next(); - } - Some(_) => {} - None => panic!("unexpected eof"), - } - } - - } - Some((start, _)) => { // symbol - loop { - match self.chars.peek() { - Some(&(i, c)) if c.is_whitespace() => return Some(&self.src[start..i]), - Some(&(i, '(')) | Some(&(i, ')')) | Some(&(i, '"')) => { - return Some(&self.src[start..i]); - } - None => return Some(&self.src[start..]), - _ => {} - } - self.chars.next(); - } - }, - None => return None, - } - } - } + type Item = &'a str; + + fn next(&mut self) -> Option { + loop { + match self.chars.next() { + Some((_, c)) if c.is_whitespace() => {} + Some((start, '(')) | Some((start, ')')) => { + return Some(&self.src[start..start + 1]) + } + Some((start, '"')) => { + // string + loop { + match self.chars.next() { + Some((i, '\"')) => { + return Some(&self.src[start..i + 1]); + } + Some((_, '\\')) => { + let _ = self.chars.next(); + } + Some(_) => {} + None => panic!("unexpected eof"), + } + } + } + Some((start, _)) => { + // symbol + loop { + match self.chars.peek() { + Some(&(i, c)) if c.is_whitespace() => return Some(&self.src[start..i]), + Some(&(i, '(')) | Some(&(i, ')')) | Some(&(i, '"')) => { + return Some(&self.src[start..i]); + } + None => return Some(&self.src[start..]), + _ => {} + } + self.chars.next(); + } + } + None => return None, + } + } + } } - pub struct Parser<'a> { - lexer: Peekable>, + lexer: Peekable>, } impl<'a> Iterator for Parser<'a> { - type Item = Cmd; - - fn next(&mut self) -> Option { - if self.lexer.peek().is_none() { - None - } else { - Some(self.cmd()) - } - } + type Item = Cmd; + + fn next(&mut self) -> Option { + if self.lexer.peek().is_none() { + None + } else { + Some(self.cmd()) + } + } } impl<'a> Parser<'a> { - pub fn new(src: &'a str) -> Parser { - Parser { lexer: Lexer::new(src).peekable() } - } - - fn next_token(&mut self) -> &'a str { - self.lexer.next().expect("unexpected eof") - } - - fn open(&mut self) { - match self.next_token() { - "(" => {}, - s => panic!(expected(s, &["("])) - } - } - - fn close(&mut self) { - match self.next_token() { - ")" => {}, - s => panic!(expected(s, &[")"])) - } - } - - fn cmd(&mut self) -> Cmd { - use script::Assertion::*; - - self.open(); - let cmd = match self.next_token() { - "module" => Cmd::ModuleSource(self.module_src()), - - "assert_return" => Cmd::Assertion(Return(self.action(), self.values())), - "assert_trap" => Cmd::Assertion(self.assert_trap()), - "assert_return_canonical_nan" => Cmd::Assertion(ReturnCanonicalNan(self.action())), - "assert_return_arithmetic_nan" => Cmd::Assertion(ReturnArithmeticNan(self.action())), - "assert_exhaustion" => Cmd::Assertion(Exhaustion(self.action(), self.string())), - "assert_invalid" => Cmd::Assertion(Invalid(self.module(), self.string())), - "assert_malformed" => Cmd::Assertion(Malformed(self.module(), self.string())), - "assert_unlinkable" => Cmd::Assertion(Unlinkable(self.module(), self.string())), - - "register" => Cmd::Register { name: self.string(), mod_ref: self.opt_id() }, - - "invoke" => Cmd::Action(self.invoke()), - "get" => Cmd::Action(self.get()), - - s => panic!("invalid cmd `{}`", s), - }; - self.close(); - cmd - } - - fn module(&mut self) -> ModuleSource { - self.open(); - let s = self.next_token(); - if s != "module" { - panic!(expected(s, &["module"])); - } - let mod_src = self.module_src(); - self.close(); - mod_src - } - - fn module_src(&mut self) -> ModuleSource { - let name = self.opt_id(); - - match self.next_token() { - "binary" => ModuleSource::Binary(name, self.bytestrings()), - "quote" => ModuleSource::Quoted(name, self.bytestrings()), - s => panic!(expected(s, &["binary", "quote"])), - } - } - - fn assert_trap(&mut self) -> Assertion { - self.open(); - let result = match self.next_token() { - "invoke" => { - let action = self.invoke(); - self.close(); - Assertion::TrapAction(action, self.string()) - } - "get" => { - let action = self.get(); - self.close(); - Assertion::TrapAction(action, self.string()) - } - "module" => { - let mod_src = self.module_src(); - self.close(); - Assertion::TrapInstantiate(mod_src, self.string()) - } - s => panic!(expected(s, &["invoke", "get", "module"])), - }; - result - } - - fn action(&mut self) -> Action { - self.open(); - let action = match self.next_token() { - "invoke" => self.invoke(), - "get" => self.get(), - s => panic!(expected(s, &["invoke", "get"])), - }; - self.close(); - action - } - - fn invoke(&mut self) -> Action { - Action::Invoke { - mod_ref: self.opt_id(), - func: self.string(), - args: self.values(), - } - } - - fn get(&mut self) -> Action { - Action::Get { - mod_ref: self.opt_id(), - global: self.string(), - } - } - - fn values(&mut self) -> Vec { - let mut values = Vec::new(); - let next_lit = |p: &mut Parser| p.next_token().replace("_", ""); - - loop { - match self.lexer.peek() { - Some(&"(") => {}, - _ => return values - } - self.open(); - values.push(match self.next_token() { - "i32.const" => Value::I32(i32::from_str(&next_lit(self)).expect("invalid i32 literal") as u32), - "i64.const" => Value::I64(i64::from_str(&next_lit(self)).expect("invalid i64 literal") as u64), - "f32.const" => Value::F32(parse_f32(&next_lit(self))), - "f64.const" => Value::F64(parse_f64(&next_lit(self))), - s => panic!(expected(s, &["i32.const", "i64.const", "f32.const", "f64.const"])) - }); - self.close(); - } - } - - fn opt_id(&mut self) -> Option { - if &self.lexer.peek()?[0..1] != "$" { - return None; - } - - Some(self.next_token()[1..].to_owned()) - } - - fn string(&mut self) -> String { - let mut vec = Vec::new(); - let s = self.next_token(); - unescape_in(&mut vec, &s[1..s.len()-1]); - String::from_utf8(vec).expect("invalid UTF-8") - } - - fn bytestrings(&mut self) -> Vec { - let mut vec = Vec::new(); - - loop { - match self.lexer.peek() { - Some(s) if s.starts_with("\"") => {}, - _ => return vec, - } - - let s = self.next_token(); - unescape_in(&mut vec, &s[1..s.len()-1]) - } - } + pub fn new(src: &'a str) -> Parser { + Parser { + lexer: Lexer::new(src).peekable(), + } + } + + fn next_token(&mut self) -> &'a str { + self.lexer.next().expect("unexpected eof") + } + + fn open(&mut self) { + match self.next_token() { + "(" => {} + s => panic!("{}", expected(s, &["("])), + } + } + + fn close(&mut self) { + match self.next_token() { + ")" => {} + s => panic!("{}", expected(s, &[")"])), + } + } + + fn cmd(&mut self) -> Cmd { + use script::Assertion::*; + + self.open(); + let cmd = match self.next_token() { + "module" => Cmd::ModuleSource(self.module_src()), + + "assert_return" => Cmd::Assertion(Return(self.action(), self.values())), + "assert_trap" => Cmd::Assertion(self.assert_trap()), + "assert_return_canonical_nan" => Cmd::Assertion(ReturnCanonicalNan(self.action())), + "assert_return_arithmetic_nan" => Cmd::Assertion(ReturnArithmeticNan(self.action())), + "assert_exhaustion" => Cmd::Assertion(Exhaustion(self.action(), self.string())), + "assert_invalid" => Cmd::Assertion(Invalid(self.module(), self.string())), + "assert_malformed" => Cmd::Assertion(Malformed(self.module(), self.string())), + "assert_unlinkable" => Cmd::Assertion(Unlinkable(self.module(), self.string())), + + "register" => Cmd::Register { + name: self.string(), + mod_ref: self.opt_id(), + }, + + "invoke" => Cmd::Action(self.invoke()), + "get" => Cmd::Action(self.get()), + + s => panic!("invalid cmd `{}`", s), + }; + self.close(); + cmd + } + + fn module(&mut self) -> ModuleSource { + self.open(); + let s = self.next_token(); + if s != "module" { + panic!("{}", expected(s, &["module"])); + } + let mod_src = self.module_src(); + self.close(); + mod_src + } + + fn module_src(&mut self) -> ModuleSource { + let name = self.opt_id(); + + match self.next_token() { + "binary" => ModuleSource::Binary(name, self.bytestrings()), + "quote" => ModuleSource::Quoted(name, self.bytestrings()), + s => panic!("{}", expected(s, &["binary", "quote"])), + } + } + + fn assert_trap(&mut self) -> Assertion { + self.open(); + let result = match self.next_token() { + "invoke" => { + let action = self.invoke(); + self.close(); + Assertion::TrapAction(action, self.string()) + } + "get" => { + let action = self.get(); + self.close(); + Assertion::TrapAction(action, self.string()) + } + "module" => { + let mod_src = self.module_src(); + self.close(); + Assertion::TrapInstantiate(mod_src, self.string()) + } + s => panic!("{}", expected(s, &["invoke", "get", "module"])), + }; + result + } + + fn action(&mut self) -> Action { + self.open(); + let action = match self.next_token() { + "invoke" => self.invoke(), + "get" => self.get(), + s => panic!("{}", expected(s, &["invoke", "get"])), + }; + self.close(); + action + } + + fn invoke(&mut self) -> Action { + Action::Invoke { + mod_ref: self.opt_id(), + func: self.string(), + args: self.values(), + } + } + + fn get(&mut self) -> Action { + Action::Get { + mod_ref: self.opt_id(), + global: self.string(), + } + } + + fn values(&mut self) -> Vec { + let mut values = Vec::new(); + let next_lit = |p: &mut Parser| p.next_token().replace('_', ""); + + loop { + match self.lexer.peek() { + Some(&"(") => {} + _ => return values, + } + self.open(); + values.push(match self.next_token() { + "i32.const" => { + Value::I32(i32::from_str(&next_lit(self)).expect("invalid i32 literal") as u32) + } + "i64.const" => { + Value::I64(i64::from_str(&next_lit(self)).expect("invalid i64 literal") as u64) + } + "f32.const" => Value::F32(parse_f32(&next_lit(self))), + "f64.const" => Value::F64(parse_f64(&next_lit(self))), + s => panic!("{}", expected( + s, + &["i32.const", "i64.const", "f32.const", "f64.const"] + )), + }); + self.close(); + } + } + + fn opt_id(&mut self) -> Option { + if &self.lexer.peek()?[0..1] != "$" { + return None; + } + + Some(self.next_token()[1..].to_owned()) + } + + fn string(&mut self) -> String { + let mut vec = Vec::new(); + let s = self.next_token(); + unescape_in(&mut vec, &s[1..s.len() - 1]); + String::from_utf8(vec).expect("invalid UTF-8") + } + + fn bytestrings(&mut self) -> Vec { + let mut vec = Vec::new(); + + loop { + match self.lexer.peek() { + Some(s) if s.starts_with('\"') => {} + _ => return vec, + } + + let s = self.next_token(); + unescape_in(&mut vec, &s[1..s.len() - 1]) + } + } } -fn unescape_in<'a>(vec: &mut Vec, s: &'a str) { - enum State { - Normal, - Esc, - Unicode, - Codepoint(u32), - Byte(u8), - } - - let mut state = State::Normal; - - for c in s.bytes() { - match state { - State::Normal => match c { - b'\\' => state = State::Esc, - _ => vec.push(c), - }, - State::Byte(h) => { - if !c.is_ascii_hexdigit() { - panic!("expected hex digit, found `{}`", c); - } - let l = parse_hex_digit(c); - vec.push(h << 4 | l); - state = State::Normal; - }, - State::Esc => match c { - b't' => { - vec.push(b'\t'); - state = State::Normal; - } - b'n' => { - vec.push(b'\n'); - state = State::Normal; - } - b'r' => { - vec.push(b'\r'); - state = State::Normal; - } - b'\"' | b'\'' | b'\\' => { - vec.push(c); - state = State::Normal; - } - b'u' => { - state = State::Unicode; - } - c if c.is_ascii_hexdigit() => { - state = State::Byte(parse_hex_digit(c)); - } - _ => panic!("unexpected escape `{}`", c), - }, - State::Unicode => match c { - b'{' => { - state = State::Codepoint(0); - } - _ => panic!("expected `{{`, found `{}`", c), - } - State::Codepoint(v) => match c { - b'}' => { - let u: char = char::try_from(v).expect(&format!("invalid unicode point {:x}", v)).into(); - let mut s = [0u8; 4]; - let _ = u.encode_utf8(&mut s); - vec.extend_from_slice(&s[..u.len_utf8()]); - state = State::Normal; - } - c if c.is_ascii_hexdigit() => { - let hb = parse_hex_digit(c); - state = State::Codepoint(v << 4 | hb as u32); - } - _ => panic!("expected hex digit"), - } - } - } +fn unescape_in(vec: &mut Vec, s: &str) { + enum State { + Normal, + Esc, + Unicode, + Codepoint(u32), + Byte(u8), + } + + let mut state = State::Normal; + + for c in s.bytes() { + match state { + State::Normal => match c { + b'\\' => state = State::Esc, + _ => vec.push(c), + }, + State::Byte(h) => { + if !c.is_ascii_hexdigit() { + panic!("expected hex digit, found `{}`", c); + } + let l = parse_hex_digit(c); + vec.push(h << 4 | l); + state = State::Normal; + } + State::Esc => match c { + b't' => { + vec.push(b'\t'); + state = State::Normal; + } + b'n' => { + vec.push(b'\n'); + state = State::Normal; + } + b'r' => { + vec.push(b'\r'); + state = State::Normal; + } + b'\"' | b'\'' | b'\\' => { + vec.push(c); + state = State::Normal; + } + b'u' => { + state = State::Unicode; + } + c if c.is_ascii_hexdigit() => { + state = State::Byte(parse_hex_digit(c)); + } + _ => panic!("unexpected escape `{}`", c), + }, + State::Unicode => match c { + b'{' => { + state = State::Codepoint(0); + } + _ => panic!("expected `{{`, found `{}`", c), + }, + State::Codepoint(v) => match c { + b'}' => { + let u: char = char::try_from(v) + .unwrap_or_else(|_| panic!("invalid unicode point {:x}", v)); + let mut s = [0u8; 4]; + let _ = u.encode_utf8(&mut s); + vec.extend_from_slice(&s[..u.len_utf8()]); + state = State::Normal; + } + c if c.is_ascii_hexdigit() => { + let hb = parse_hex_digit(c); + state = State::Codepoint(v << 4 | hb as u32); + } + _ => panic!("expected hex digit"), + }, + } + } } fn parse_hex_digit(c: u8) -> u8 { - match c { - b'0'..=b'9' => c - b'0', - b'a'..=b'f' => c - b'a' + 10, - b'A'..=b'F' => c - b'A' + 10, - _ => unreachable!() - } + match c { + b'0'..=b'9' => c - b'0', + b'a'..=b'f' => c - b'a' + 10, + b'A'..=b'F' => c - b'A' + 10, + _ => unreachable!(), + } } macro_rules! define_parse_f { - ($name:ident, $f:ident, $i:ident, $parse_hex:ident) => ( - fn $name(s: &str) -> $f { - let (negate, s) = match &s[0..1] { - "-" => (true, &s[1..]), - "+" => (false, &s[1..]), - _ => (false, s), - }; - let z = if s == "infinity" { - $f::INFINITY - } else if s.starts_with("nan") { - $f::from_bits($f::INFINITY.to_bits() | $i::from_str_radix(&s[6..], 16).unwrap()) - } else if s.starts_with("0x") { - if s.contains('.') { - $parse_hex(s, true).unwrap() - } else { - $parse_hex(&s.replace("p", ".p"), true).unwrap() - } - } else { - $f::from_str(s).unwrap() - }; - if negate { -z } else { z } - } - ) + ($name:ident, $f:ident, $i:ident, $parse_hex:ident) => { + fn $name(s: &str) -> $f { + let (negate, s) = match &s[0..1] { + "-" => (true, &s[1..]), + "+" => (false, &s[1..]), + _ => (false, s), + }; + let z = if s == "infinity" { + $f::INFINITY + } else if s.starts_with("nan") { + $f::from_bits($f::INFINITY.to_bits() | $i::from_str_radix(&s[6..], 16).unwrap()) + } else if s.starts_with("0x") { + if s.contains('.') { + $parse_hex(s, true).unwrap() + } else { + $parse_hex(&s.replace("p", ".p"), true).unwrap() + } + } else { + $f::from_str(s).unwrap() + }; + if negate { + -z + } else { + z + } + } + }; } define_parse_f!(parse_f32, f32, u32, parse_hexf32); define_parse_f!(parse_f64, f64, u64, parse_hexf64); -fn expected(found: &str, options: &[&str]) -> String{ - format!("found: {:?}, expected: {:?}", found, options) +fn expected(found: &str, options: &[&str]) -> String { + format!("found: {:?}, expected: {:?}", found, options) } diff --git a/tests/script/run.rs b/tests/script/run.rs index d881827..98f60e5 100644 --- a/tests/script/run.rs +++ b/tests/script/run.rs @@ -1,277 +1,363 @@ -use std::{f32, f64}; -use std::fs::File; -use std::path::Path; -use std::io::{Cursor, Read}; -use std::collections::HashMap; use rust_wasm::*; use script::*; +use std::collections::HashMap; +use std::fs::File; +use std::io::{Cursor, Read}; +use std::path::Path; +use std::{f32, f64}; type Exports = HashMap; struct Registry { - mod_exports: HashMap, Exports>, - last_key: Option, + mod_exports: HashMap, Exports>, + last_key: Option, } pub fn run>(path: P) { - let mut f = File::open(path).unwrap(); - let mut src = String::new(); - f.read_to_string(&mut src).unwrap(); - let parser = parser::Parser::new(&src); + let mut f = File::open(path).unwrap(); + let mut src = String::new(); + f.read_to_string(&mut src).unwrap(); + let parser = parser::Parser::new(&src); - let mut store = init_store(); - let mut registry = Registry { - mod_exports: HashMap::new(), - last_key: None, - }; + let mut store = init_store(); + let mut registry = Registry { + mod_exports: HashMap::new(), + last_key: None, + }; - // Special test host module - init_spectest(&mut store, &mut registry); + // Special test host module + init_spectest(&mut store, &mut registry); - for cmd in parser { - match cmd { - Cmd::ModuleSource(src) => { - let (opt_name, m) = decode_module_src(&src); + for cmd in parser { + match cmd { + Cmd::ModuleSource(src) => { + let (opt_name, m) = decode_module_src(&src); - let imports = resolve_imports(&m, &mut registry).unwrap(); - let export_names: Vec = module_exports(&m).map(|(name, _)| name.to_owned()).collect(); + let imports = resolve_imports(&m, &mut registry).unwrap(); + let export_names: Vec = module_exports(&m) + .map(|(name, _)| name.to_owned()) + .collect(); - let inst = instantiate_module(&mut store, m, &imports[..]).unwrap(); + let inst = instantiate_module(&mut store, m, &imports[..]).unwrap(); - let exports = export_names.into_iter().map(|name| { - let export = get_export(&inst, &name).unwrap(); - (name, export) - }).collect(); + let exports = export_names + .into_iter() + .map(|name| { + let export = get_export(&inst, &name).unwrap(); + (name, export) + }) + .collect(); - registry.last_key = opt_name.clone(); - registry.mod_exports.insert(opt_name, exports); - } - Cmd::Assertion(a) => { - run_assertion(&mut store, ®istry, a); - } - Cmd::Action(a) => { - let _ = run_action(&mut store, ®istry, &a); - } - Cmd::Register { name, mod_ref } => { - let mod_name = if mod_ref.is_some() { &mod_ref } else { ®istry.last_key }; - let inst = registry.mod_exports[mod_name].clone(); - registry.mod_exports.insert(Some(name), inst); - } - } - } + registry.last_key = opt_name.clone(); + registry.mod_exports.insert(opt_name, exports); + } + Cmd::Assertion(a) => { + run_assertion(&mut store, ®istry, a); + } + Cmd::Action(a) => { + let _ = run_action(&mut store, ®istry, &a); + } + Cmd::Register { name, mod_ref } => { + let mod_name = if mod_ref.is_some() { + &mod_ref + } else { + ®istry.last_key + }; + let inst = registry.mod_exports[mod_name].clone(); + registry.mod_exports.insert(Some(name), inst); + } + } + } } fn decode_module_src(module: &ModuleSource) -> (Option, ast::Module) { - match *module { - ModuleSource::Binary(ref name, ref bytes) => (name.clone(), decode_module(Cursor::new(bytes)).unwrap()), - ModuleSource::Quoted(_, _) => unimplemented!("quoted modules are not supported"), - } + match *module { + ModuleSource::Binary(ref name, ref bytes) => { + (name.clone(), decode_module(Cursor::new(bytes)).unwrap()) + } + ModuleSource::Quoted(_, _) => unimplemented!("quoted modules are not supported"), + } } fn run_assertion(store: &mut Store, registry: &Registry, assertion: Assertion) { - use self::Assertion::*; + use self::Assertion::*; - println!("run assertion {:?}", assertion); - match assertion { - Return(action, expected) => { - let result = run_action(store, registry, &action); - match result { - Ok(ref actual) if *actual == expected => {} - _ => { - panic!( - "the result of the action `{:?}` is `{:?}` but should be `{:?}`", - action, result, expected, - ); - } - } - } - ReturnCanonicalNan(action) => { - let result = run_action(store, registry, &action).unwrap(); - assert!(result.len() == 1); - let val = result[0]; - match val { - values::Value::F32(f) if f.to_bits() == f32::NAN.to_bits() || f.to_bits() == (-f32::NAN).to_bits() => {}, - values::Value::F64(f) if f.to_bits() == f64::NAN.to_bits() || f.to_bits() == (-f64::NAN).to_bits() => {}, - _ => { - panic!( - "the result of the action `{:?}` is `{:?}` but should be a canonical NaN", - action, result, - ); - } - }; - } - ReturnArithmeticNan(action) => { - let result = run_action(store, registry, &action).unwrap(); - assert!(result.len() == 1); - let val = result[0]; - match val { - values::Value::F32(f) if f.to_bits() & f32::NAN.to_bits() == f32::NAN.to_bits() => {}, - values::Value::F64(f) if f.to_bits() & f64::NAN.to_bits() == f64::NAN.to_bits() => {}, - _ => { - panic!( - "the result of the action `{:?}` is `{:?}` but should be an arithmetic NaN", - action, result, - ); - } - }; - } - TrapAction(action, _) => { - if let Err(Error::CodeTrapped(_)) = run_action(store, registry, &action) { - // There is no if let != in Rust? - } else { - panic!("the action `{:?}` should cause a trap", action); - } - } - TrapInstantiate(module, _) => { - let (_, m) = decode_module_src(&module); - let imports = resolve_imports(&m, registry).unwrap(); - if let Err(Error::CodeTrapped(_)) = instantiate_module(store, m, &imports[..]) { - } else { - panic!("instantiating module `{:?}` should cause a trap", module); - } - } - Exhaustion(action, reason) => { - match (reason.as_ref(), run_action(store, registry, &action)) { - ("call stack exhausted", Err(Error::StackOverflow)) => (), - (reason, err) => panic!("the action `{:?}` should cause a stack overflow (reason = {}, err = {:?})", action, reason, err), - }; - } - Invalid(module, reason) => { - let (_, m) = decode_module_src(&module); - // Do not resolve the imports for invalid modules - match (reason, instantiate_module(store, m, &[]).err()) { - (_, Some(Error::InvalidModule)) => (), - (reason, err) => panic!("instantiating module `{:?}` should not be valid (reason = {}, err = {:?})", module, reason, err), - } - } - Malformed(mod_src, _) => { - match mod_src { - ModuleSource::Binary(_, bytes) => { - assert_eq!(decode_module(Cursor::new(bytes)).unwrap_err(), Error::DecodeModuleFailed); - }, - ModuleSource::Quoted(_, _) => { - // NB: quoted sources use text format, which we do not support - } - } - } - Unlinkable(module, reason) => { - let (_, m) = decode_module_src(&module); + println!("run assertion {:?}", assertion); + match assertion { + Return(action, expected) => { + let result = run_action(store, registry, &action); + match result { + Ok(ref actual) if *actual == expected => {} + _ => { + panic!( + "the result of the action `{:?}` is `{:?}` but should be `{:?}`", + action, result, expected, + ); + } + } + } + ReturnCanonicalNan(action) => { + let result = run_action(store, registry, &action).unwrap(); + assert!(result.len() == 1); + let val = result[0]; + match val { + values::Value::F32(f) + if f.to_bits() == f32::NAN.to_bits() + || f.to_bits() == (-f32::NAN).to_bits() => {} + values::Value::F64(f) + if f.to_bits() == f64::NAN.to_bits() + || f.to_bits() == (-f64::NAN).to_bits() => {} + _ => { + panic!( + "the result of the action `{:?}` is `{:?}` but should be a canonical NaN", + action, result, + ); + } + }; + } + ReturnArithmeticNan(action) => { + let result = run_action(store, registry, &action).unwrap(); + assert!(result.len() == 1); + let val = result[0]; + match val { + values::Value::F32(f) if f.to_bits() & f32::NAN.to_bits() == f32::NAN.to_bits() => { + } + values::Value::F64(f) if f.to_bits() & f64::NAN.to_bits() == f64::NAN.to_bits() => { + } + _ => { + panic!( + "the result of the action `{:?}` is `{:?}` but should be an arithmetic NaN", + action, result, + ); + } + }; + } + TrapAction(action, _) => { + if let Err(Error::CodeTrapped(_)) = run_action(store, registry, &action) { + // There is no if let != in Rust? + } else { + panic!("the action `{:?}` should cause a trap", action); + } + } + TrapInstantiate(module, _) => { + let (_, m) = decode_module_src(&module); + let imports = resolve_imports(&m, registry).unwrap(); + if let Err(Error::CodeTrapped(_)) = instantiate_module(store, m, &imports[..]) { + } else { + panic!("instantiating module `{:?}` should cause a trap", module); + } + } + Exhaustion(action, reason) => { + match (reason.as_ref(), run_action(store, registry, &action)) { + ("call stack exhausted", Err(Error::StackOverflow)) => (), + (reason, err) => panic!( + "the action `{:?}` should cause a stack overflow (reason = {}, err = {:?})", + action, reason, err + ), + }; + } + Invalid(module, reason) => { + let (_, m) = decode_module_src(&module); + // Do not resolve the imports for invalid modules + match (reason, instantiate_module(store, m, &[]).err()) { + (_, Some(Error::InvalidModule)) => (), + (reason, err) => panic!( + "instantiating module `{:?}` should not be valid (reason = {}, err = {:?})", + module, reason, err + ), + } + } + Malformed(mod_src, _) => { + match mod_src { + ModuleSource::Binary(_, bytes) => { + assert_eq!( + decode_module(Cursor::new(bytes)).unwrap_err(), + Error::DecodeModuleFailed + ); + } + ModuleSource::Quoted(_, _) => { + // NB: quoted sources use text format, which we do not support + } + } + } + Unlinkable(module, reason) => { + let (_, m) = decode_module_src(&module); - let imports = match (reason.as_ref(), resolve_imports(&m, registry)) { - ("unknown import", Err(_)) => return, - (_, Err(err)) => panic!("failed to resolve import: `{:?}`", err), - (_, Ok(imports)) => imports, - }; + let imports = match (reason.as_ref(), resolve_imports(&m, registry)) { + ("unknown import", Err(_)) => return, + (_, Err(err)) => panic!("failed to resolve import: `{:?}`", err), + (_, Ok(imports)) => imports, + }; - match (reason.as_ref(), instantiate_module(store, m, &imports[..]).err()) { - ("incompatible import type", Some(Error::ImportTypeMismatch)) => (), - ("elements segment does not fit", Some(Error::ElemOffsetTooLarge(_))) => (), - ("data segment does not fit", Some(Error::DataOffsetTooLarge(_))) => (), - (reason, err) => panic!("instantiating module `{:?}` should not link (reason = {}, err = {:?})", module, reason, err), - } - } - } + match ( + reason.as_ref(), + instantiate_module(store, m, &imports[..]).err(), + ) { + ("incompatible import type", Some(Error::ImportTypeMismatch)) => (), + ("elements segment does not fit", Some(Error::ElemOffsetTooLarge(_))) => (), + ("data segment does not fit", Some(Error::DataOffsetTooLarge(_))) => (), + (reason, err) => panic!( + "instantiating module `{:?}` should not link (reason = {}, err = {:?})", + module, reason, err + ), + } + } + } } -fn run_action(store: &mut Store, registry: &Registry, action: &Action) -> Result, Error> { - match *action { - Action::Invoke { mod_ref: ref mod_name, ref func, ref args } => { - let mod_name = if mod_name.is_some() { mod_name } else { ®istry.last_key }; - match registry.mod_exports[mod_name][func] { - ExternVal::Func(addr) => { - invoke_func(store, addr, args.clone()) - } - _ => panic!("extern val should be a function"), - } - } - Action::Get { mod_ref: ref mod_name, ref global } => { - let mod_name = if mod_name.is_some() { mod_name } else { ®istry.last_key }; - match registry.mod_exports[mod_name][global] { - ExternVal::Global(addr) => { - Ok(vec![read_global(store, addr)]) - } - _ => panic!("extern val should be a global"), - } - } - } +fn run_action( + store: &mut Store, + registry: &Registry, + action: &Action, +) -> Result, Error> { + match *action { + Action::Invoke { + mod_ref: ref mod_name, + ref func, + ref args, + } => { + let mod_name = if mod_name.is_some() { + mod_name + } else { + ®istry.last_key + }; + match registry.mod_exports[mod_name][func] { + ExternVal::Func(addr) => invoke_func(store, addr, args.clone()), + _ => panic!("extern val should be a function"), + } + } + Action::Get { + mod_ref: ref mod_name, + ref global, + } => { + let mod_name = if mod_name.is_some() { + mod_name + } else { + ®istry.last_key + }; + match registry.mod_exports[mod_name][global] { + ExternVal::Global(addr) => Ok(vec![read_global(store, addr)]), + _ => panic!("extern val should be a global"), + } + } + } } fn init_spectest(store: &mut Store, registry: &mut Registry) { - let mut symbols = HashMap::new(); + let mut symbols = HashMap::new(); - symbols.insert("table".to_owned(), ExternVal::Table( - alloc_table( - store, - &types::Table { limits: types::Limits { min: 10, max: Some(20) }, elem: types::Elem::AnyFunc } - ) - )); + symbols.insert( + "table".to_owned(), + ExternVal::Table(alloc_table( + store, + &types::Table { + limits: types::Limits { + min: 10, + max: Some(20), + }, + elem: types::Elem::AnyFunc, + }, + )), + ); - symbols.insert("memory".to_owned(), ExternVal::Memory( - alloc_mem( - store, - &types::Memory { limits: types::Limits { min: 1, max: Some(2) } } - ) - )); + symbols.insert( + "memory".to_owned(), + ExternVal::Memory(alloc_mem( + store, + &types::Memory { + limits: types::Limits { + min: 1, + max: Some(2), + }, + }, + )), + ); - fn print(store: &mut Store, args_types: Vec) -> ExternVal { - let func = move |args: &[values::Value], _ret: &mut[values::Value]| { - for val in args { - println!("{:?}", val); - } - None - }; + fn print(store: &mut Store, args_types: Vec) -> ExternVal { + let func = move |args: &[values::Value], _ret: &mut [values::Value]| { + for val in args { + println!("{:?}", val); + } + None + }; - ExternVal::Func( - alloc_func( - store, - &types::Func { args: args_types, result: Vec::new() }, - Box::new(func) - ) - ) - } + ExternVal::Func(alloc_func( + store, + &types::Func { + args: args_types, + result: Vec::new(), + }, + Box::new(func), + )) + } - fn global(store: &mut Store, val: values::Value) -> ExternVal { - ExternVal::Global( - alloc_global( - store, - &types::Global { value: val.type_(), mutable: false }, - val - ) - ) - } + fn global(store: &mut Store, val: values::Value) -> ExternVal { + ExternVal::Global(alloc_global( + store, + &types::Global { + value: val.type_(), + mutable: false, + }, + val, + )) + } - symbols.insert("print".to_owned(), print(store, vec![])); - symbols.insert("print_i32".to_owned(), print(store, vec![types::I32])); - symbols.insert("print_i32_f32".to_owned(), print(store, vec![types::I32, types::F32])); - symbols.insert("print_f32".to_owned(), print(store, vec![types::F32])); - symbols.insert("print_f64".to_owned(), print(store, vec![types::F64])); - symbols.insert("print_f64_f64".to_owned(), print(store, vec![types::F64, types::F64])); - symbols.insert("global_i32".to_owned(), global(store, values::Value::I32(666))); - symbols.insert("global_f32".to_owned(), global(store, values::Value::F32(666.0))); - symbols.insert("global_f64".to_owned(), global(store, values::Value::F64(666.0))); + symbols.insert("print".to_owned(), print(store, vec![])); + symbols.insert("print_i32".to_owned(), print(store, vec![types::I32])); + symbols.insert( + "print_i32_f32".to_owned(), + print(store, vec![types::I32, types::F32]), + ); + symbols.insert("print_f32".to_owned(), print(store, vec![types::F32])); + symbols.insert("print_f64".to_owned(), print(store, vec![types::F64])); + symbols.insert( + "print_f64_f64".to_owned(), + print(store, vec![types::F64, types::F64]), + ); + symbols.insert( + "global_i32".to_owned(), + global(store, values::Value::I32(666)), + ); + symbols.insert( + "global_f32".to_owned(), + global(store, values::Value::F32(666.0)), + ); + symbols.insert( + "global_f64".to_owned(), + global(store, values::Value::F64(666.0)), + ); - registry.mod_exports.insert(Some(String::from("spectest")), symbols); + registry + .mod_exports + .insert(Some(String::from("spectest")), symbols); } #[derive(Debug)] enum UnknownImport { - UnknownModule { mod_name: String }, - UnknownSymbol { mod_name: String, symbol_name: String }, + UnknownModule { + mod_name: String, + }, + UnknownSymbol { + mod_name: String, + symbol_name: String, + }, } fn resolve_imports(m: &ast::Module, registry: &Registry) -> Result, UnknownImport> { - module_imports(m).map(|(mod_name, import_name, _)| { - match registry.mod_exports.get(&Some(mod_name.to_owned())) { - Some(ref mod_exports) => { - match mod_exports.get(import_name) { - Some(val) => Ok(val.clone()), - None => Err(UnknownImport::UnknownSymbol { - mod_name: mod_name.to_owned(), - symbol_name: import_name.to_owned() - }), - } - }, - None => Err(UnknownImport::UnknownModule { mod_name: mod_name.to_owned() }), - } - }).collect() + module_imports(m) + .map(|(mod_name, import_name, _)| { + match registry.mod_exports.get(&Some(mod_name.to_owned())) { + Some(mod_exports) => match mod_exports.get(import_name) { + Some(val) => Ok(*val), + None => Err(UnknownImport::UnknownSymbol { + mod_name: mod_name.to_owned(), + symbol_name: import_name.to_owned(), + }), + }, + None => Err(UnknownImport::UnknownModule { + mod_name: mod_name.to_owned(), + }), + } + }) + .collect() }