diff --git a/.circleci/config.yml b/.circleci/config.yml index 0e51b31..7a5073a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,10 +43,10 @@ jobs: steps: - restore_cache: keys: - - deps6-{{ .Branch }}-{{ .Revision }} - # - deps6-{{ .Branch }}-cargo-{{ checksum "kernel-ewasm/Cargo.lock" }} - - deps6-{{ .Branch }}- - - deps6- + - deps7-{{ .Branch }}-{{ .Revision }} + # - deps7-{{ .Branch }}-cargo-{{ checksum "kernel-ewasm/Cargo.lock" }} + - deps7-{{ .Branch }}- + - deps7- - run: name: Install native build prequisites command: | @@ -96,9 +96,13 @@ jobs: # cargo build --verbose --release --features final # strip target/debug/parity # file target/debug/parity - cargo install --bin parity -j 1 --path . --bin parity parity-ethereum --force + if parity --version; then + echo "Parity node installed" + else + cargo install --bin parity -j 1 --path . --bin parity parity-ethereum + fi - save_cache: - key: deps6-{{ .Branch }}-cargo #-{{ checksum "kernel-ewasm/Cargo.lock" }} + key: deps7-{{ .Branch }}-cargo #-{{ checksum "kernel-ewasm/Cargo.lock" }} paths: - "~/.cargo" - "~/.rustup" @@ -118,7 +122,7 @@ jobs: cd kernel-ewasm # we need to run parity once to set up the accounts and keys # this only needs to be active for a few seconds (hence timeout) - timeout 5 parity --config dev || true + # timeout 5 parity --config dev || true # We then run parity properly, now unlocking the previously setup # account parity --config dev --chain ./wasm-dev-chain.json --jsonrpc-apis=all --ws-apis=all --reseal-min-period 0 --gasprice 0 @@ -135,6 +139,7 @@ jobs: - run: name: Test Rust Component command: | + . ~/.profile cd cap9 cd kernel-ewasm && npm install npm run test diff --git a/kernel-ewasm/Cargo.toml b/kernel-ewasm/Cargo.toml index 8addefb..a62f4a6 100644 --- a/kernel-ewasm/Cargo.toml +++ b/kernel-ewasm/Cargo.toml @@ -26,6 +26,7 @@ crate-type = ["cdylib"] [features] default = ["std"] std = ["pwasm-std/std", "pwasm-ethereum/std", "pwasm-test/std"] +panic_with_msg = ["pwasm-std/panic_with_msg"] [profile.release] panic = "abort" diff --git a/kernel-ewasm/build.bat b/kernel-ewasm/build.bat index ee62b43..af141fd 100644 --- a/kernel-ewasm/build.bat +++ b/kernel-ewasm/build.bat @@ -6,7 +6,7 @@ cargo install --path ..\cap9-build --bin cap9-build --force set contract_name=kernel_ewasm -cargo build --release --target wasm32-unknown-unknown --no-default-features +cargo build --release --target wasm32-unknown-unknown --no-default-features --features "panic_with_msg" cap9-build set-mem --pages 3 .\target\wasm32-unknown-unknown\release\%contract_name%.wasm .\target\wasm32-unknown-unknown\release\%contract_name%.wasm wasm-build --target=wasm32-unknown-unknown .\target kernel-ewasm REM ..\..\wasm-utils\target\debug\wasm-build.exe --target=wasm32-unknown-unknown .\target kernel-ewasm diff --git a/kernel-ewasm/build.sh b/kernel-ewasm/build.sh index 41ddacd..fa12228 100755 --- a/kernel-ewasm/build.sh +++ b/kernel-ewasm/build.sh @@ -4,7 +4,7 @@ rustup target add wasm32-unknown-unknown cargo install pwasm-utils-cli --bin wasm-build --version 0.6.0 cargo install --path ../cap9-build --bin cap9-build --force -cargo build --release --target wasm32-unknown-unknown --no-default-features +cargo build --release --target wasm32-unknown-unknown --no-default-features --features "panic_with_msg" cap9-build set-mem --pages 3 ./target/wasm32-unknown-unknown/release/kernel_ewasm.wasm ./target/wasm32-unknown-unknown/release/kernel_ewasm.wasm wasm-build --target=wasm32-unknown-unknown ./target kernel-ewasm diff --git a/kernel-ewasm/example_contract_2/Cargo.toml b/kernel-ewasm/example_contract_2/Cargo.toml new file mode 100644 index 0000000..961bd83 --- /dev/null +++ b/kernel-ewasm/example_contract_2/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "example_contract_2" +version = "0.1.0" + +[dependencies] +pwasm-std = "0.13" +pwasm-ethereum = { version = "0.8", features = ["kip6"] } +pwasm-abi = "0.2" +pwasm-abi-derive = "0.2" +tiny-keccak = "1.4.2" + +[dev-dependencies] +pwasm-test = { git = "https://github.com/paritytech/pwasm-test" } +cap9-build = { path = "../../cap9-build" } +pwasm-utils-cli = "0.7.0" + +[features] +default = [] +std = ["pwasm-std/std", "pwasm-ethereum/std"] + +[lib] +crate-type = ["cdylib"] diff --git a/kernel-ewasm/example_contract_2/build.bat b/kernel-ewasm/example_contract_2/build.bat new file mode 100644 index 0000000..c2760c5 --- /dev/null +++ b/kernel-ewasm/example_contract_2/build.bat @@ -0,0 +1,15 @@ +@echo OFF + +mkdir .\build + +rustup target add wasm32-unknown-unknown +REM cargo install pwasm-utils-cli --bin wasm-build --force + +set contract_name=example_contract_2 + +cargo build --release --target wasm32-unknown-unknown +REM We don't need to use cap9 build here as it contains no syscalls +wasm-build --target=wasm32-unknown-unknown .\target %contract_name% + +copy .\target\*.wasm .\build +copy .\target\json\* .\build diff --git a/kernel-ewasm/example_contract_2/build.sh b/kernel-ewasm/example_contract_2/build.sh new file mode 100644 index 0000000..492a95e --- /dev/null +++ b/kernel-ewasm/example_contract_2/build.sh @@ -0,0 +1,16 @@ +@echo OFF + +mkdir ./build + +rustup target add wasm32-unknown-unknown +REM cargo install pwasm-utils-cli --bin wasm-build --force +cargo install --path ../../cap9-build --bin cap9-build --force + +export contract_name=example_contract_2 + +cargo build --release --target wasm32-unknown-unknown +# We don't need to use cap9 build here as it contains no syscalls +wasm-build --target=wasm32-unknown-unknown ./target $contract_name + +cp ./target/*.wasm ./build +cp ./target/json/* ./build diff --git a/kernel-ewasm/example_contract_2/build/example_contract_2.wasm b/kernel-ewasm/example_contract_2/build/example_contract_2.wasm new file mode 100644 index 0000000..f1803fb Binary files /dev/null and b/kernel-ewasm/example_contract_2/build/example_contract_2.wasm differ diff --git a/kernel-ewasm/example_contract_2/src/lib.rs b/kernel-ewasm/example_contract_2/src/lib.rs new file mode 100644 index 0000000..704b0d4 --- /dev/null +++ b/kernel-ewasm/example_contract_2/src/lib.rs @@ -0,0 +1,52 @@ +#![cfg_attr(not(feature="std"), no_std)] + +#![allow(non_snake_case)] + +extern crate tiny_keccak; +extern crate pwasm_std; +extern crate pwasm_ethereum; +extern crate pwasm_abi; +extern crate pwasm_abi_derive; + +use tiny_keccak::Keccak; +use pwasm_ethereum as eth; +use pwasm_abi::types::*; +use pwasm_abi_derive::eth_abi; +use pwasm_ethereum::Error; + +/// The call function is the main function of the *deployed* contract +#[no_mangle] +pub fn call() { + let mut endpoint = contract::ExampleContract2Endpoint::new(contract::ExampleContract2{}); + pwasm_ethereum::ret(&endpoint.dispatch(&pwasm_ethereum::input())); +} + +// Declares the dispatch and dispatch_ctor methods +use pwasm_abi::eth::EndpointInterface; + +#[no_mangle] +pub fn deploy() { + let mut endpoint = contract::ExampleContract2Endpoint::new(contract::ExampleContract2{}); + endpoint.dispatch_ctor(&pwasm_ethereum::input()); +} + + +pub mod contract { + use super::*; + use pwasm_abi_derive::eth_abi; + + #[eth_abi(ExampleContract2Endpoint, ExampleContract2Client)] + pub trait ExampleContract2Interface { + /// Check if Procedure Contract is Valid + fn check_contract(&mut self, _to: Address) -> bool; + } + + pub struct ExampleContract2; + + impl ExampleContract2Interface for ExampleContract2 { + fn check_contract(&mut self, _target: Address) -> bool { + // unimplemented!() + false + } + } +} diff --git a/kernel-ewasm/package.json b/kernel-ewasm/package.json index 74b3177..151f515 100644 --- a/kernel-ewasm/package.json +++ b/kernel-ewasm/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "deploy": "node script/deploy.js", - "test": "test.sh" + "test": "sh ./test.sh" }, "author": "", "license": "ISC", diff --git a/kernel-ewasm/rust-toolchain b/kernel-ewasm/rust-toolchain index cff751d..09c2799 100644 --- a/kernel-ewasm/rust-toolchain +++ b/kernel-ewasm/rust-toolchain @@ -1 +1 @@ -nightly-2019-04-01 +nightly-2019-06-06 diff --git a/kernel-ewasm/src/lib.rs b/kernel-ewasm/src/lib.rs index c1851da..639f4b9 100644 --- a/kernel-ewasm/src/lib.rs +++ b/kernel-ewasm/src/lib.rs @@ -44,7 +44,7 @@ pub mod token { use pwasm_ethereum; use pwasm_abi::types::*; // use parity_wasm::elements::{Module}; - use validator::{Validity, Module, deserialize_buffer}; + use validator::{Validity}; // eth_abi is a procedural macros https://doc.rust-lang.org/book/first-edition/procedural-macros.html @@ -130,23 +130,7 @@ pub mod token { // Next we get the code of the contract, using EXTCODECOPY under // the hood. let code: pwasm_std::Vec = self.code_copy(target); - // code_slice is magic number and version number only - let code_slice = &[0, 97, 115, 109, 1, 0, 0, 0]; - // big_code_slice is magic number, version number and a simple - // data section. - let big_code_slice = &[0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x0B, 0x07, 0x01, 0x00, 0x41, 0x01, 0x0B, 0x01, 0x54, 0x00, 0x08, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x02, 0x01, 0x00]; - // Next we deserialise the code from Vec into a Module. - let module: Module = match deserialize_buffer(code.as_slice()) { - // let module: Module = match deserialize_buffer(code_slice) { - Ok(module) => module, - // If we are unable to decode the contract, we assume it is - // not valid, but for now we will panic for testing - // purposes. - Err(_) => panic!("invalid wasm module"), - }; - // // Then we perform a boolen is_valid() check. - module.is_valid(); - false + code.as_slice().is_valid() } } diff --git a/kernel-ewasm/test.sh b/kernel-ewasm/test.sh new file mode 100644 index 0000000..527c297 --- /dev/null +++ b/kernel-ewasm/test.sh @@ -0,0 +1,4 @@ +pushd example_contract_2 +sh ./build.sh +popd +mocha tests/**/**.js diff --git a/kernel-ewasm/tests/integration/index.js b/kernel-ewasm/tests/integration/index.js index e3d018c..e015b39 100644 --- a/kernel-ewasm/tests/integration/index.js +++ b/kernel-ewasm/tests/integration/index.js @@ -177,11 +177,11 @@ describe('Kernel', function() { assert.strictEqual(code_size, code.length, "The code length should be as given by EXTCODESIZE"); }) - it('should return false when trying to validate the kernel itself', async function() { + it('should return a boolean when trying to validate the kernel itself', async function() { const kernelAddress = kernel.options.address; assert(web3.utils.isAddress(kernelAddress), "The kernel address should be a valid address") let rec_validation = await kernel.methods.check_contract(kernelAddress).call(); - assert.strictEqual(rec_validation, false) + assert.strictEqual(typeof rec_validation, "boolean"); }) it('should copy the code of an example contract', async function() { @@ -193,7 +193,7 @@ describe('Kernel', function() { // assert.strictEqual(code_size, code.length, "The code length should be as given by EXTCODESIZE"); }) - it('should correctly validate an example contract', async function() { + it('should return a boolean when validating an example contract', async function() { const contract = await deployContract("example_contract_2/build/ExampleContract2Interface.json", "example_contract_2/build/example_contract_2.wasm"); assert(web3.utils.isAddress(contract.address), "The contract address should be a valid address") const code_size = await kernel.methods.get_code_size(contract.address).call(); @@ -201,7 +201,7 @@ describe('Kernel', function() { const code = web3.utils.hexToBytes(code_hex); assert.strictEqual(code_size, code.length, "The code length should be as given by EXTCODESIZE"); let rec_validation = await kernel.methods.check_contract(contract.address).call(); - assert.strictEqual(rec_validation, true); + assert.strictEqual(typeof rec_validation, "boolean"); }) }) }) diff --git a/kernel-ewasm/validator/Cargo.toml b/kernel-ewasm/validator/Cargo.toml index 1cddcf1..7ff6d68 100644 --- a/kernel-ewasm/validator/Cargo.toml +++ b/kernel-ewasm/validator/Cargo.toml @@ -5,20 +5,15 @@ authors = ["Daohub Inc "] edition = "2018" [dependencies] -parity-wasm = { version = "0.35", default-features = false } pwasm-std = {version = "0.13", default-features = false} pwasm-ethereum = {version = "0.8", default-features = false} [dev-dependencies] wabt = "0.7.1" -[dev-dependencies.pwasm-test] -git = "https://github.com/paritytech/pwasm-test" -default-features = false - [features] default = ["std"] -std = ["parity-wasm/std", "pwasm-std/std", "pwasm-ethereum/std", "pwasm-test/std"] +std = ["pwasm-std/std", "pwasm-ethereum/std"] [lib] name = "validator" diff --git a/kernel-ewasm/validator/src/func.rs b/kernel-ewasm/validator/src/func.rs new file mode 100644 index 0000000..48bc964 --- /dev/null +++ b/kernel-ewasm/validator/src/func.rs @@ -0,0 +1,118 @@ +use crate::io; +// mod primitives; +use crate::{Deserialize, VarUint32}; +use crate::types::ValueType; +use crate::instructions::{Instructions}; +use crate::serialization::{Error}; +use pwasm_std::vec::Vec; + +/// Function signature (type reference) +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Func(u32); + +impl Func { + /// New function signature + pub fn new(type_ref: u32) -> Self { Func(type_ref) } + + /// Function signature type reference. + pub fn type_ref(&self) -> u32 { + self.0 + } + + /// Function signature type reference (mutable). + pub fn type_ref_mut(&mut self) -> &mut u32 { + &mut self.0 + } +} + +// impl Deserialize for Func { +// type Error = Error; + +// fn deserialize(reader: &mut R) -> Result { +// Ok(Func(VarUint32::deserialize(reader)?.into())) +// } +// } + +/// Local definition inside the function body. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Local { + count: u32, + value_type: ValueType, +} + +impl Local { + /// New local with `count` and `value_type`. + pub fn new(count: u32, value_type: ValueType) -> Self { + Local { count: count, value_type: value_type } + } + + /// Number of locals with the shared type. + pub fn count(&self) -> u32 { self.count } + + /// Type of the locals. + pub fn value_type(&self) -> ValueType { self.value_type } +} + +impl Deserialize for Local { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let count = VarUint32::deserialize(reader)?; + let value_type = ValueType::deserialize(reader)?; + Ok(Local { count: count.into(), value_type: value_type }) + } +} + +/// Function body definition. +#[derive(Debug, Clone, PartialEq)] +pub struct FuncBody { + locals: Vec, + instructions: Instructions, +} + +impl FuncBody { + /// New function body with given `locals` and `instructions`. + pub fn new(locals: Vec, instructions: Instructions) -> Self { + FuncBody { locals: locals, instructions: instructions } + } + + /// List of individual instructions. + pub fn empty() -> Self { + FuncBody { locals: Vec::new(), instructions: Instructions::empty() } + } + + /// Locals declared in function body. + pub fn locals(&self) -> &[Local] { &self.locals } + + /// Instruction list of the function body. Minimal instruction list + /// + /// is just `&[Instruction::End]` + pub fn code(&self) -> &Instructions { &self.instructions } + + /// Locals declared in function body (mutable). + pub fn locals_mut(&mut self) -> &mut Vec { &mut self.locals } + + /// Instruction list of the function body (mutable). + pub fn code_mut(&mut self) -> &mut Instructions { &mut self.instructions } +} + +// impl Deserialize for FuncBody { +// type Error = Error; + +// fn deserialize(reader: &mut R) -> Result { +// // Why do we need to use a section reader here? +// let mut body_reader = SectionReader::new(reader)?; +// let locals: Vec = CountedList::::deserialize(&mut body_reader)?.into_inner(); + +// // The specification obliges us to count the total number of local variables while +// // decoding the binary format. +// locals +// .iter() +// .try_fold(0u32, |acc, &Local { count, .. }| acc.checked_add(count)) +// .ok_or_else(|| Error::TooManyLocals)?; + +// let instructions = Instructions::deserialize(&mut body_reader)?; +// body_reader.close()?; +// Ok(FuncBody { locals: locals, instructions: instructions }) +// } +// } diff --git a/kernel-ewasm/validator/src/import_entry.rs b/kernel-ewasm/validator/src/import_entry.rs new file mode 100644 index 0000000..a6c1ed4 --- /dev/null +++ b/kernel-ewasm/validator/src/import_entry.rs @@ -0,0 +1,245 @@ +use pwasm_std; +use pwasm_std::String; + +use crate::io; +use crate::{Deserialize, Uint8, VarUint32, VarUint1, VarUint7}; +use crate::types::{TableElementType, ValueType}; +use crate::serialization::{Error}; + +const FLAG_HAS_MAX: u8 = 0x01; +const FLAG_SHARED: u8 = 0x02; + +/// Global definition struct +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct GlobalType { + content_type: ValueType, + is_mutable: bool, +} + +impl GlobalType { + /// New global type + pub fn new(content_type: ValueType, is_mutable: bool) -> Self { + GlobalType { + content_type: content_type, + is_mutable: is_mutable, + } + } + + /// Type of the global entry + pub fn content_type(&self) -> ValueType { self.content_type } + + /// Is global entry is declared as mutable + pub fn is_mutable(&self) -> bool { self.is_mutable } +} + +impl Deserialize for GlobalType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let content_type = ValueType::deserialize(reader)?; + let is_mutable = VarUint1::deserialize(reader)?; + Ok(GlobalType { + content_type: content_type, + is_mutable: is_mutable.into(), + }) + } +} + +/// Table entry +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct TableType { + elem_type: TableElementType, + limits: ResizableLimits, +} + +impl TableType { + /// New table definition + pub fn new(min: u32, max: Option) -> Self { + TableType { + elem_type: TableElementType::AnyFunc, + limits: ResizableLimits::new(min, max), + } + } + + /// Table memory specification + pub fn limits(&self) -> &ResizableLimits { &self.limits } + + /// Table element type + pub fn elem_type(&self) -> TableElementType { self.elem_type } +} + +impl Deserialize for TableType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let elem_type = TableElementType::deserialize(reader)?; + let limits = ResizableLimits::deserialize(reader)?; + Ok(TableType { + elem_type: elem_type, + limits: limits, + }) + } +} + +/// Memory and table limits. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct ResizableLimits { + initial: u32, + maximum: Option, + shared: bool, +} + +impl ResizableLimits { + /// New memory limits definition. + pub fn new(min: u32, max: Option) -> Self { + ResizableLimits { + initial: min, + maximum: max, + shared: false, + } + } + /// Initial size. + pub fn initial(&self) -> u32 { self.initial } + /// Maximum size. + pub fn maximum(&self) -> Option { self.maximum } + /// Whether or not this is a shared array buffer. + pub fn shared(&self) -> bool { self.shared } +} + +impl Deserialize for ResizableLimits { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let flags: u8 = Uint8::deserialize(reader)?.into(); + match flags { + 0x00 | 0x01 | 0x03 => {}, + _ => return Err(Error::InvalidLimitsFlags(flags)), + } + + let initial = VarUint32::deserialize(reader)?; + let maximum = if flags & FLAG_HAS_MAX != 0 { + Some(VarUint32::deserialize(reader)?.into()) + } else { + None + }; + let shared = flags & FLAG_SHARED != 0; + + Ok(ResizableLimits { + initial: initial.into(), + maximum: maximum, + shared, + }) + } +} + +/// Memory entry. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct MemoryType(ResizableLimits); + +impl MemoryType { + /// New memory definition + pub fn new(min: u32, max: Option, shared: bool) -> Self { + let mut r = ResizableLimits::new(min, max); + r.shared = shared; + MemoryType(r) + } + + /// Limits of the memory entry. + pub fn limits(&self) -> &ResizableLimits { + &self.0 + } +} + +impl Deserialize for MemoryType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + Ok(MemoryType(ResizableLimits::deserialize(reader)?)) + } +} + +/// External to local binding. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum External { + /// Binds to a function whose type is associated with the given index in the + /// type section. + Function(u32), + /// Describes local table definition to be imported as. + Table(TableType), + /// Describes local memory definition to be imported as. + Memory(MemoryType), + /// Describes local global entry to be imported as. + Global(GlobalType), +} + +impl Deserialize for External { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let kind = VarUint7::deserialize(reader)?; + match kind.into() { + 0x00 => Ok(External::Function(VarUint32::deserialize(reader)?.into())), + 0x01 => Ok(External::Table(TableType::deserialize(reader)?)), + 0x02 => Ok(External::Memory(MemoryType::deserialize(reader)?)), + 0x03 => Ok(External::Global(GlobalType::deserialize(reader)?)), + _ => Err(Error::UnknownExternalKind(kind.into())), + } + } +} + +/// Import entry. +#[derive(Debug, Clone, PartialEq)] +pub struct ImportEntry { + module_str: String, + field_str: String, + external: External, +} + +impl ImportEntry { + /// New import entry. + pub fn new(module_str: String, field_str: String, external: External) -> Self { + ImportEntry { + module_str: module_str, + field_str: field_str, + external: external, + } + } + + /// Module reference of the import entry. + pub fn module(&self) -> &str { &self.module_str } + + /// Module reference of the import entry (mutable). + pub fn module_mut(&mut self) -> &mut String { + &mut self.module_str + } + + /// Field reference of the import entry. + pub fn field(&self) -> &str { &self.field_str } + + /// Field reference of the import entry (mutable) + pub fn field_mut(&mut self) -> &mut String { + &mut self.field_str + } + + /// Local binidng of the import entry. + pub fn external(&self) -> &External { &self.external } + + /// Local binidng of the import entry (mutable) + pub fn external_mut(&mut self) -> &mut External { &mut self.external } +} + +impl Deserialize for ImportEntry { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let module_str = String::deserialize(reader)?; + let field_str = String::deserialize(reader)?; + let external = External::deserialize(reader)?; + + Ok(ImportEntry { + module_str: module_str, + field_str: field_str, + external: external, + }) + } +} diff --git a/kernel-ewasm/validator/src/instructions.rs b/kernel-ewasm/validator/src/instructions.rs new file mode 100644 index 0000000..d98c21a --- /dev/null +++ b/kernel-ewasm/validator/src/instructions.rs @@ -0,0 +1,1626 @@ +// This file is based on parity-wasm from parity, MIT & Apache Licensed +use pwasm_std; +use pwasm_std::vec::Vec; +use pwasm_std::Box; + +use crate::io; +use crate::{Deserialize, + Uint8, VarUint32, CountedList, + Uint32, Uint64, + VarInt32, VarInt64, +}; +use crate::types::{BlockType}; +use crate::serialization::{Error}; + +/// List of instructions (usually inside a block section). +#[derive(Debug, Clone, PartialEq)] +pub struct Instructions(Vec); + +impl Instructions { + /// New list of instructions from vector of instructions. + pub fn new(elements: Vec) -> Self { + Instructions(elements) + } + + /// Empty expression with only `Instruction::End` instruction. + pub fn empty() -> Self { + Instructions(vec![Instruction::End]) + } + + /// List of individual instructions. + pub fn elements(&self) -> &[Instruction] { &self.0 } + + /// Individual instructions, mutable. + pub fn elements_mut(&mut self) -> &mut Vec { &mut self.0 } +} + +impl Deserialize for Instructions { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut instructions = Vec::new(); + let mut block_count = 1usize; + + loop { + let instruction = Instruction::deserialize(reader)?; + if instruction.is_terminal() { + block_count -= 1; + } else if instruction.is_block() { + block_count = block_count.checked_add(1).ok_or(Error::Other("too many instructions"))?; + } + + instructions.push(instruction); + if block_count == 0 { + break; + } + } + + Ok(Instructions(instructions)) + } +} + +/// Initialization expression. +#[derive(Debug, Clone, PartialEq)] +pub struct InitExpr(Vec); + +impl InitExpr { + /// New initialization expression from instruction list. + /// + /// `code` must end with the `Instruction::End` instruction! + pub fn new(code: Vec) -> Self { + InitExpr(code) + } + + /// Empty expression with only `Instruction::End` instruction. + pub fn empty() -> Self { + InitExpr(vec![Instruction::End]) + } + + /// List of instructions used in the expression. + pub fn code(&self) -> &[Instruction] { + &self.0 + } + + /// List of instructions used in the expression. + pub fn code_mut(&mut self) -> &mut Vec { + &mut self.0 + } +} + +impl Deserialize for InitExpr { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut instructions = Vec::new(); + + loop { + let instruction = Instruction::deserialize(reader)?; + let is_terminal = instruction.is_terminal(); + instructions.push(instruction); + if is_terminal { + break; + } + } + + Ok(InitExpr(instructions)) + } +} + +/// Instruction. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[allow(missing_docs)] +pub enum Instruction { + Unreachable, + Nop, + Block(BlockType), + Loop(BlockType), + If(BlockType), + Else, + End, + Br(u32), + BrIf(u32), + BrTable(Box), + Return, + + Call(u32), + CallIndirect(u32, u8), + + Drop, + Select, + + GetLocal(u32), + SetLocal(u32), + TeeLocal(u32), + GetGlobal(u32), + SetGlobal(u32), + + // All store/load instructions operate with 'memory immediates' + // which represented here as (flag, offset) tuple + I32Load(u32, u32), + I64Load(u32, u32), + F32Load(u32, u32), + F64Load(u32, u32), + I32Load8S(u32, u32), + I32Load8U(u32, u32), + I32Load16S(u32, u32), + I32Load16U(u32, u32), + I64Load8S(u32, u32), + I64Load8U(u32, u32), + I64Load16S(u32, u32), + I64Load16U(u32, u32), + I64Load32S(u32, u32), + I64Load32U(u32, u32), + I32Store(u32, u32), + I64Store(u32, u32), + F32Store(u32, u32), + F64Store(u32, u32), + I32Store8(u32, u32), + I32Store16(u32, u32), + I64Store8(u32, u32), + I64Store16(u32, u32), + I64Store32(u32, u32), + + CurrentMemory(u8), + GrowMemory(u8), + + I32Const(i32), + I64Const(i64), + F32Const(u32), + F64Const(u64), + + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, + + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, + + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, + + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, + + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, + + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, + + I32WrapI64, + I32TruncSF32, + I32TruncUF32, + I32TruncSF64, + I32TruncUF64, + I64ExtendSI32, + I64ExtendUI32, + I64TruncSF32, + I64TruncUF32, + I64TruncSF64, + I64TruncUF64, + F32ConvertSI32, + F32ConvertUI32, + F32ConvertSI64, + F32ConvertUI64, + F32DemoteF64, + F64ConvertSI32, + F64ConvertUI32, + F64ConvertSI64, + F64ConvertUI64, + F64PromoteF32, + + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, + + I32Extend8S, + I32Extend16S, + I64Extend8S, + I64Extend16S, + I64Extend32S, + + AtomicWake(MemArg), + I32AtomicWait(MemArg), + I64AtomicWait(MemArg), + + I32AtomicLoad(MemArg), + I64AtomicLoad(MemArg), + I32AtomicLoad8u(MemArg), + I32AtomicLoad16u(MemArg), + I64AtomicLoad8u(MemArg), + I64AtomicLoad16u(MemArg), + I64AtomicLoad32u(MemArg), + I32AtomicStore(MemArg), + I64AtomicStore(MemArg), + I32AtomicStore8u(MemArg), + I32AtomicStore16u(MemArg), + I64AtomicStore8u(MemArg), + I64AtomicStore16u(MemArg), + I64AtomicStore32u(MemArg), + + I32AtomicRmwAdd(MemArg), + I64AtomicRmwAdd(MemArg), + I32AtomicRmwAdd8u(MemArg), + I32AtomicRmwAdd16u(MemArg), + I64AtomicRmwAdd8u(MemArg), + I64AtomicRmwAdd16u(MemArg), + I64AtomicRmwAdd32u(MemArg), + + I32AtomicRmwSub(MemArg), + I64AtomicRmwSub(MemArg), + I32AtomicRmwSub8u(MemArg), + I32AtomicRmwSub16u(MemArg), + I64AtomicRmwSub8u(MemArg), + I64AtomicRmwSub16u(MemArg), + I64AtomicRmwSub32u(MemArg), + + I32AtomicRmwAnd(MemArg), + I64AtomicRmwAnd(MemArg), + I32AtomicRmwAnd8u(MemArg), + I32AtomicRmwAnd16u(MemArg), + I64AtomicRmwAnd8u(MemArg), + I64AtomicRmwAnd16u(MemArg), + I64AtomicRmwAnd32u(MemArg), + + I32AtomicRmwOr(MemArg), + I64AtomicRmwOr(MemArg), + I32AtomicRmwOr8u(MemArg), + I32AtomicRmwOr16u(MemArg), + I64AtomicRmwOr8u(MemArg), + I64AtomicRmwOr16u(MemArg), + I64AtomicRmwOr32u(MemArg), + + I32AtomicRmwXor(MemArg), + I64AtomicRmwXor(MemArg), + I32AtomicRmwXor8u(MemArg), + I32AtomicRmwXor16u(MemArg), + I64AtomicRmwXor8u(MemArg), + I64AtomicRmwXor16u(MemArg), + I64AtomicRmwXor32u(MemArg), + + I32AtomicRmwXchg(MemArg), + I64AtomicRmwXchg(MemArg), + I32AtomicRmwXchg8u(MemArg), + I32AtomicRmwXchg16u(MemArg), + I64AtomicRmwXchg8u(MemArg), + I64AtomicRmwXchg16u(MemArg), + I64AtomicRmwXchg32u(MemArg), + + I32AtomicRmwCmpxchg(MemArg), + I64AtomicRmwCmpxchg(MemArg), + I32AtomicRmwCmpxchg8u(MemArg), + I32AtomicRmwCmpxchg16u(MemArg), + I64AtomicRmwCmpxchg8u(MemArg), + I64AtomicRmwCmpxchg16u(MemArg), + I64AtomicRmwCmpxchg32u(MemArg), + + V128Const(Box<[u8; 16]>), + V128Load(MemArg), + V128Store(MemArg), + I8x16Splat, + I16x8Splat, + I32x4Splat, + I64x2Splat, + F32x4Splat, + F64x2Splat, + I8x16ExtractLaneS(u8), + I8x16ExtractLaneU(u8), + I16x8ExtractLaneS(u8), + I16x8ExtractLaneU(u8), + I32x4ExtractLane(u8), + I64x2ExtractLane(u8), + F32x4ExtractLane(u8), + F64x2ExtractLane(u8), + I8x16ReplaceLane(u8), + I16x8ReplaceLane(u8), + I32x4ReplaceLane(u8), + I64x2ReplaceLane(u8), + F32x4ReplaceLane(u8), + F64x2ReplaceLane(u8), + V8x16Shuffle(Box<[u8; 16]>), + I8x16Add, + I16x8Add, + I32x4Add, + I64x2Add, + I8x16Sub, + I16x8Sub, + I32x4Sub, + I64x2Sub, + I8x16Mul, + I16x8Mul, + I32x4Mul, + // I64x2Mul, + I8x16Neg, + I16x8Neg, + I32x4Neg, + I64x2Neg, + I8x16AddSaturateS, + I8x16AddSaturateU, + I16x8AddSaturateS, + I16x8AddSaturateU, + I8x16SubSaturateS, + I8x16SubSaturateU, + I16x8SubSaturateS, + I16x8SubSaturateU, + I8x16Shl, + I16x8Shl, + I32x4Shl, + I64x2Shl, + I8x16ShrS, + I8x16ShrU, + I16x8ShrS, + I16x8ShrU, + I32x4ShrS, + I32x4ShrU, + I64x2ShrS, + I64x2ShrU, + V128And, + V128Or, + V128Xor, + V128Not, + V128Bitselect, + I8x16AnyTrue, + I16x8AnyTrue, + I32x4AnyTrue, + I64x2AnyTrue, + I8x16AllTrue, + I16x8AllTrue, + I32x4AllTrue, + I64x2AllTrue, + I8x16Eq, + I16x8Eq, + I32x4Eq, + // I64x2Eq, + F32x4Eq, + F64x2Eq, + I8x16Ne, + I16x8Ne, + I32x4Ne, + // I64x2Ne, + F32x4Ne, + F64x2Ne, + I8x16LtS, + I8x16LtU, + I16x8LtS, + I16x8LtU, + I32x4LtS, + I32x4LtU, + // I64x2LtS, + // I64x2LtU, + F32x4Lt, + F64x2Lt, + I8x16LeS, + I8x16LeU, + I16x8LeS, + I16x8LeU, + I32x4LeS, + I32x4LeU, + // I64x2LeS, + // I64x2LeU, + F32x4Le, + F64x2Le, + I8x16GtS, + I8x16GtU, + I16x8GtS, + I16x8GtU, + I32x4GtS, + I32x4GtU, + // I64x2GtS, + // I64x2GtU, + F32x4Gt, + F64x2Gt, + I8x16GeS, + I8x16GeU, + I16x8GeS, + I16x8GeU, + I32x4GeS, + I32x4GeU, + // I64x2GeS, + // I64x2GeU, + F32x4Ge, + F64x2Ge, + F32x4Neg, + F64x2Neg, + F32x4Abs, + F64x2Abs, + F32x4Min, + F64x2Min, + F32x4Max, + F64x2Max, + F32x4Add, + F64x2Add, + F32x4Sub, + F64x2Sub, + F32x4Div, + F64x2Div, + F32x4Mul, + F64x2Mul, + F32x4Sqrt, + F64x2Sqrt, + F32x4ConvertSI32x4, + F32x4ConvertUI32x4, + F64x2ConvertSI64x2, + F64x2ConvertUI64x2, + I32x4TruncSF32x4Sat, + I32x4TruncUF32x4Sat, + I64x2TruncSF64x2Sat, + I64x2TruncUF64x2Sat, + + // https://github.com/WebAssembly/bulk-memory-operations + MemoryInit(u32), + MemoryDrop(u32), + MemoryCopy, + MemoryFill, + TableInit(u32), + TableDrop(u32), + TableCopy, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[allow(missing_docs)] +pub struct MemArg { + pub align: u8, + pub offset: u32, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[allow(missing_docs)] +pub struct BrTableData { + pub table: Box<[u32]>, + pub default: u32, +} + +impl Instruction { + /// Is this instruction starts the new block (which should end with terminal instruction). + pub fn is_block(&self) -> bool { + match self { + &Instruction::Block(_) | &Instruction::Loop(_) | &Instruction::If(_) => true, + _ => false, + } + } + + /// Is this instruction determines the termination of instruction sequence? + /// + /// `true` for `Instruction::End` + pub fn is_terminal(&self) -> bool { + match self { + &Instruction::End => true, + _ => false, + } + } +} + +#[allow(missing_docs)] +#[allow(dead_code)] +pub mod opcodes { + pub const UNREACHABLE: u8 = 0x00; + pub const NOP: u8 = 0x01; + pub const BLOCK: u8 = 0x02; + pub const LOOP: u8 = 0x03; + pub const IF: u8 = 0x04; + pub const ELSE: u8 = 0x05; + pub const END: u8 = 0x0b; + pub const BR: u8 = 0x0c; + pub const BRIF: u8 = 0x0d; + pub const BRTABLE: u8 = 0x0e; + pub const RETURN: u8 = 0x0f; + pub const CALL: u8 = 0x10; + pub const CALLINDIRECT: u8 = 0x11; + pub const DROP: u8 = 0x1a; + pub const SELECT: u8 = 0x1b; + pub const GETLOCAL: u8 = 0x20; + pub const SETLOCAL: u8 = 0x21; + pub const TEELOCAL: u8 = 0x22; + pub const GETGLOBAL: u8 = 0x23; + pub const SETGLOBAL: u8 = 0x24; + pub const I32LOAD: u8 = 0x28; + pub const I64LOAD: u8 = 0x29; + pub const F32LOAD: u8 = 0x2a; + pub const F64LOAD: u8 = 0x2b; + pub const I32LOAD8S: u8 = 0x2c; + pub const I32LOAD8U: u8 = 0x2d; + pub const I32LOAD16S: u8 = 0x2e; + pub const I32LOAD16U: u8 = 0x2f; + pub const I64LOAD8S: u8 = 0x30; + pub const I64LOAD8U: u8 = 0x31; + pub const I64LOAD16S: u8 = 0x32; + pub const I64LOAD16U: u8 = 0x33; + pub const I64LOAD32S: u8 = 0x34; + pub const I64LOAD32U: u8 = 0x35; + pub const I32STORE: u8 = 0x36; + pub const I64STORE: u8 = 0x37; + pub const F32STORE: u8 = 0x38; + pub const F64STORE: u8 = 0x39; + pub const I32STORE8: u8 = 0x3a; + pub const I32STORE16: u8 = 0x3b; + pub const I64STORE8: u8 = 0x3c; + pub const I64STORE16: u8 = 0x3d; + pub const I64STORE32: u8 = 0x3e; + pub const CURRENTMEMORY: u8 = 0x3f; + pub const GROWMEMORY: u8 = 0x40; + pub const I32CONST: u8 = 0x41; + pub const I64CONST: u8 = 0x42; + pub const F32CONST: u8 = 0x43; + pub const F64CONST: u8 = 0x44; + pub const I32EQZ: u8 = 0x45; + pub const I32EQ: u8 = 0x46; + pub const I32NE: u8 = 0x47; + pub const I32LTS: u8 = 0x48; + pub const I32LTU: u8 = 0x49; + pub const I32GTS: u8 = 0x4a; + pub const I32GTU: u8 = 0x4b; + pub const I32LES: u8 = 0x4c; + pub const I32LEU: u8 = 0x4d; + pub const I32GES: u8 = 0x4e; + pub const I32GEU: u8 = 0x4f; + pub const I64EQZ: u8 = 0x50; + pub const I64EQ: u8 = 0x51; + pub const I64NE: u8 = 0x52; + pub const I64LTS: u8 = 0x53; + pub const I64LTU: u8 = 0x54; + pub const I64GTS: u8 = 0x55; + pub const I64GTU: u8 = 0x56; + pub const I64LES: u8 = 0x57; + pub const I64LEU: u8 = 0x58; + pub const I64GES: u8 = 0x59; + pub const I64GEU: u8 = 0x5a; + + pub const F32EQ: u8 = 0x5b; + pub const F32NE: u8 = 0x5c; + pub const F32LT: u8 = 0x5d; + pub const F32GT: u8 = 0x5e; + pub const F32LE: u8 = 0x5f; + pub const F32GE: u8 = 0x60; + + pub const F64EQ: u8 = 0x61; + pub const F64NE: u8 = 0x62; + pub const F64LT: u8 = 0x63; + pub const F64GT: u8 = 0x64; + pub const F64LE: u8 = 0x65; + pub const F64GE: u8 = 0x66; + + pub const I32CLZ: u8 = 0x67; + pub const I32CTZ: u8 = 0x68; + pub const I32POPCNT: u8 = 0x69; + pub const I32ADD: u8 = 0x6a; + pub const I32SUB: u8 = 0x6b; + pub const I32MUL: u8 = 0x6c; + pub const I32DIVS: u8 = 0x6d; + pub const I32DIVU: u8 = 0x6e; + pub const I32REMS: u8 = 0x6f; + pub const I32REMU: u8 = 0x70; + pub const I32AND: u8 = 0x71; + pub const I32OR: u8 = 0x72; + pub const I32XOR: u8 = 0x73; + pub const I32SHL: u8 = 0x74; + pub const I32SHRS: u8 = 0x75; + pub const I32SHRU: u8 = 0x76; + pub const I32ROTL: u8 = 0x77; + pub const I32ROTR: u8 = 0x78; + + pub const I64CLZ: u8 = 0x79; + pub const I64CTZ: u8 = 0x7a; + pub const I64POPCNT: u8 = 0x7b; + pub const I64ADD: u8 = 0x7c; + pub const I64SUB: u8 = 0x7d; + pub const I64MUL: u8 = 0x7e; + pub const I64DIVS: u8 = 0x7f; + pub const I64DIVU: u8 = 0x80; + pub const I64REMS: u8 = 0x81; + pub const I64REMU: u8 = 0x82; + pub const I64AND: u8 = 0x83; + pub const I64OR: u8 = 0x84; + pub const I64XOR: u8 = 0x85; + pub const I64SHL: u8 = 0x86; + pub const I64SHRS: u8 = 0x87; + pub const I64SHRU: u8 = 0x88; + pub const I64ROTL: u8 = 0x89; + pub const I64ROTR: u8 = 0x8a; + pub const F32ABS: u8 = 0x8b; + pub const F32NEG: u8 = 0x8c; + pub const F32CEIL: u8 = 0x8d; + pub const F32FLOOR: u8 = 0x8e; + pub const F32TRUNC: u8 = 0x8f; + pub const F32NEAREST: u8 = 0x90; + pub const F32SQRT: u8 = 0x91; + pub const F32ADD: u8 = 0x92; + pub const F32SUB: u8 = 0x93; + pub const F32MUL: u8 = 0x94; + pub const F32DIV: u8 = 0x95; + pub const F32MIN: u8 = 0x96; + pub const F32MAX: u8 = 0x97; + pub const F32COPYSIGN: u8 = 0x98; + pub const F64ABS: u8 = 0x99; + pub const F64NEG: u8 = 0x9a; + pub const F64CEIL: u8 = 0x9b; + pub const F64FLOOR: u8 = 0x9c; + pub const F64TRUNC: u8 = 0x9d; + pub const F64NEAREST: u8 = 0x9e; + pub const F64SQRT: u8 = 0x9f; + pub const F64ADD: u8 = 0xa0; + pub const F64SUB: u8 = 0xa1; + pub const F64MUL: u8 = 0xa2; + pub const F64DIV: u8 = 0xa3; + pub const F64MIN: u8 = 0xa4; + pub const F64MAX: u8 = 0xa5; + pub const F64COPYSIGN: u8 = 0xa6; + + pub const I32WRAPI64: u8 = 0xa7; + pub const I32TRUNCSF32: u8 = 0xa8; + pub const I32TRUNCUF32: u8 = 0xa9; + pub const I32TRUNCSF64: u8 = 0xaa; + pub const I32TRUNCUF64: u8 = 0xab; + pub const I64EXTENDSI32: u8 = 0xac; + pub const I64EXTENDUI32: u8 = 0xad; + pub const I64TRUNCSF32: u8 = 0xae; + pub const I64TRUNCUF32: u8 = 0xaf; + pub const I64TRUNCSF64: u8 = 0xb0; + pub const I64TRUNCUF64: u8 = 0xb1; + pub const F32CONVERTSI32: u8 = 0xb2; + pub const F32CONVERTUI32: u8 = 0xb3; + pub const F32CONVERTSI64: u8 = 0xb4; + pub const F32CONVERTUI64: u8 = 0xb5; + pub const F32DEMOTEF64: u8 = 0xb6; + pub const F64CONVERTSI32: u8 = 0xb7; + pub const F64CONVERTUI32: u8 = 0xb8; + pub const F64CONVERTSI64: u8 = 0xb9; + pub const F64CONVERTUI64: u8 = 0xba; + pub const F64PROMOTEF32: u8 = 0xbb; + + pub const I32REINTERPRETF32: u8 = 0xbc; + pub const I64REINTERPRETF64: u8 = 0xbd; + pub const F32REINTERPRETI32: u8 = 0xbe; + pub const F64REINTERPRETI64: u8 = 0xbf; + + pub const I32_EXTEND8_S: u8 = 0xc0; + pub const I32_EXTEND16_S: u8 = 0xc1; + pub const I64_EXTEND8_S: u8 = 0xc2; + pub const I64_EXTEND16_S: u8 = 0xc3; + pub const I64_EXTEND32_S: u8 = 0xc4; + + pub const ATOMIC_PREFIX: u8 = 0xfe; + pub const ATOMIC_WAKE: u8 = 0x00; + pub const I32_ATOMIC_WAIT: u8 = 0x01; + pub const I64_ATOMIC_WAIT: u8 = 0x02; + + pub const I32_ATOMIC_LOAD: u8 = 0x10; + pub const I64_ATOMIC_LOAD: u8 = 0x11; + pub const I32_ATOMIC_LOAD8U: u8 = 0x12; + pub const I32_ATOMIC_LOAD16U: u8 = 0x13; + pub const I64_ATOMIC_LOAD8U: u8 = 0x14; + pub const I64_ATOMIC_LOAD16U: u8 = 0x15; + pub const I64_ATOMIC_LOAD32U: u8 = 0x16; + pub const I32_ATOMIC_STORE: u8 = 0x17; + pub const I64_ATOMIC_STORE: u8 = 0x18; + pub const I32_ATOMIC_STORE8U: u8 = 0x19; + pub const I32_ATOMIC_STORE16U: u8 = 0x1a; + pub const I64_ATOMIC_STORE8U: u8 = 0x1b; + pub const I64_ATOMIC_STORE16U: u8 = 0x1c; + pub const I64_ATOMIC_STORE32U: u8 = 0x1d; + + pub const I32_ATOMIC_RMW_ADD: u8 = 0x1e; + pub const I64_ATOMIC_RMW_ADD: u8 = 0x1f; + pub const I32_ATOMIC_RMW_ADD8U: u8 = 0x20; + pub const I32_ATOMIC_RMW_ADD16U: u8 = 0x21; + pub const I64_ATOMIC_RMW_ADD8U: u8 = 0x22; + pub const I64_ATOMIC_RMW_ADD16U: u8 = 0x23; + pub const I64_ATOMIC_RMW_ADD32U: u8 = 0x24; + + pub const I32_ATOMIC_RMW_SUB: u8 = 0x25; + pub const I64_ATOMIC_RMW_SUB: u8 = 0x26; + pub const I32_ATOMIC_RMW_SUB8U: u8 = 0x27; + pub const I32_ATOMIC_RMW_SUB16U: u8 = 0x28; + pub const I64_ATOMIC_RMW_SUB8U: u8 = 0x29; + pub const I64_ATOMIC_RMW_SUB16U: u8 = 0x2a; + pub const I64_ATOMIC_RMW_SUB32U: u8 = 0x2b; + + pub const I32_ATOMIC_RMW_AND: u8 = 0x2c; + pub const I64_ATOMIC_RMW_AND: u8 = 0x2d; + pub const I32_ATOMIC_RMW_AND8U: u8 = 0x2e; + pub const I32_ATOMIC_RMW_AND16U: u8 = 0x2f; + pub const I64_ATOMIC_RMW_AND8U: u8 = 0x30; + pub const I64_ATOMIC_RMW_AND16U: u8 = 0x31; + pub const I64_ATOMIC_RMW_AND32U: u8 = 0x32; + + pub const I32_ATOMIC_RMW_OR: u8 = 0x33; + pub const I64_ATOMIC_RMW_OR: u8 = 0x34; + pub const I32_ATOMIC_RMW_OR8U: u8 = 0x35; + pub const I32_ATOMIC_RMW_OR16U: u8 = 0x36; + pub const I64_ATOMIC_RMW_OR8U: u8 = 0x37; + pub const I64_ATOMIC_RMW_OR16U: u8 = 0x38; + pub const I64_ATOMIC_RMW_OR32U: u8 = 0x39; + + pub const I32_ATOMIC_RMW_XOR: u8 = 0x3a; + pub const I64_ATOMIC_RMW_XOR: u8 = 0x3b; + pub const I32_ATOMIC_RMW_XOR8U: u8 = 0x3c; + pub const I32_ATOMIC_RMW_XOR16U: u8 = 0x3d; + pub const I64_ATOMIC_RMW_XOR8U: u8 = 0x3e; + pub const I64_ATOMIC_RMW_XOR16U: u8 = 0x3f; + pub const I64_ATOMIC_RMW_XOR32U: u8 = 0x40; + + pub const I32_ATOMIC_RMW_XCHG: u8 = 0x41; + pub const I64_ATOMIC_RMW_XCHG: u8 = 0x42; + pub const I32_ATOMIC_RMW_XCHG8U: u8 = 0x43; + pub const I32_ATOMIC_RMW_XCHG16U: u8 = 0x44; + pub const I64_ATOMIC_RMW_XCHG8U: u8 = 0x45; + pub const I64_ATOMIC_RMW_XCHG16U: u8 = 0x46; + pub const I64_ATOMIC_RMW_XCHG32U: u8 = 0x47; + + pub const I32_ATOMIC_RMW_CMPXCHG: u8 = 0x48; + pub const I64_ATOMIC_RMW_CMPXCHG: u8 = 0x49; + pub const I32_ATOMIC_RMW_CMPXCHG8U: u8 = 0x4a; + pub const I32_ATOMIC_RMW_CMPXCHG16U: u8 = 0x4b; + pub const I64_ATOMIC_RMW_CMPXCHG8U: u8 = 0x4c; + pub const I64_ATOMIC_RMW_CMPXCHG16U: u8 = 0x4d; + pub const I64_ATOMIC_RMW_CMPXCHG32U: u8 = 0x4e; + + // https://github.com/WebAssembly/simd/blob/master/proposals/simd/BinarySIMD.md + pub const SIMD_PREFIX: u8 = 0xfd; + + pub const V128_LOAD: u32 = 0x00; + pub const V128_STORE: u32 = 0x01; + pub const V128_CONST: u32 = 0x02; + pub const V8X16_SHUFFLE: u32 = 0x03; + + pub const I8X16_SPLAT: u32 = 0x04; + pub const I8X16_EXTRACT_LANE_S: u32 = 0x05; + pub const I8X16_EXTRACT_LANE_U: u32 = 0x06; + pub const I8X16_REPLACE_LANE: u32 = 0x07; + pub const I16X8_SPLAT: u32 = 0x08; + pub const I16X8_EXTRACT_LANE_S: u32 = 0x09; + pub const I16X8_EXTRACT_LANE_U: u32 = 0xa; + pub const I16X8_REPLACE_LANE: u32 = 0x0b; + pub const I32X4_SPLAT: u32 = 0x0c; + pub const I32X4_EXTRACT_LANE: u32 = 0x0d; + pub const I32X4_REPLACE_LANE: u32 = 0x0e; + pub const I64X2_SPLAT: u32 = 0x0f; + pub const I64X2_EXTRACT_LANE: u32 = 0x10; + pub const I64X2_REPLACE_LANE: u32 = 0x11; + pub const F32X4_SPLAT: u32 = 0x12; + pub const F32X4_EXTRACT_LANE: u32 = 0x13; + pub const F32X4_REPLACE_LANE: u32 = 0x14; + pub const F64X2_SPLAT: u32 = 0x15; + pub const F64X2_EXTRACT_LANE: u32 = 0x16; + pub const F64X2_REPLACE_LANE: u32 = 0x17; + + pub const I8X16_EQ: u32 = 0x18; + pub const I8X16_NE: u32 = 0x19; + pub const I8X16_LT_S: u32 = 0x1a; + pub const I8X16_LT_U: u32 = 0x1b; + pub const I8X16_GT_S: u32 = 0x1c; + pub const I8X16_GT_U: u32 = 0x1d; + pub const I8X16_LE_S: u32 = 0x1e; + pub const I8X16_LE_U: u32 = 0x1f; + pub const I8X16_GE_S: u32 = 0x20; + pub const I8X16_GE_U: u32 = 0x21; + + pub const I16X8_EQ: u32 = 0x22; + pub const I16X8_NE: u32 = 0x23; + pub const I16X8_LT_S: u32 = 0x24; + pub const I16X8_LT_U: u32 = 0x25; + pub const I16X8_GT_S: u32 = 0x26; + pub const I16X8_GT_U: u32 = 0x27; + pub const I16X8_LE_S: u32 = 0x28; + pub const I16X8_LE_U: u32 = 0x29; + pub const I16X8_GE_S: u32 = 0x2a; + pub const I16X8_GE_U: u32 = 0x2b; + + pub const I32X4_EQ: u32 = 0x2c; + pub const I32X4_NE: u32 = 0x2d; + pub const I32X4_LT_S: u32 = 0x2e; + pub const I32X4_LT_U: u32 = 0x2f; + pub const I32X4_GT_S: u32 = 0x30; + pub const I32X4_GT_U: u32 = 0x31; + pub const I32X4_LE_S: u32 = 0x32; + pub const I32X4_LE_U: u32 = 0x33; + pub const I32X4_GE_S: u32 = 0x34; + pub const I32X4_GE_U: u32 = 0x35; + + pub const F32X4_EQ: u32 = 0x40; + pub const F32X4_NE: u32 = 0x41; + pub const F32X4_LT: u32 = 0x42; + pub const F32X4_GT: u32 = 0x43; + pub const F32X4_LE: u32 = 0x44; + pub const F32X4_GE: u32 = 0x45; + + pub const F64X2_EQ: u32 = 0x46; + pub const F64X2_NE: u32 = 0x47; + pub const F64X2_LT: u32 = 0x48; + pub const F64X2_GT: u32 = 0x49; + pub const F64X2_LE: u32 = 0x4a; + pub const F64X2_GE: u32 = 0x4b; + + pub const V128_NOT: u32 = 0x4c; + pub const V128_AND: u32 = 0x4d; + pub const V128_OR: u32 = 0x4e; + pub const V128_XOR: u32 = 0x4f; + pub const V128_BITSELECT: u32 = 0x50; + + pub const I8X16_NEG: u32 = 0x51; + pub const I8X16_ANY_TRUE: u32 = 0x52; + pub const I8X16_ALL_TRUE: u32 = 0x53; + pub const I8X16_SHL: u32 = 0x54; + pub const I8X16_SHR_S: u32 = 0x55; + pub const I8X16_SHR_U: u32 = 0x56; + pub const I8X16_ADD: u32 = 0x57; + pub const I8X16_ADD_SATURATE_S: u32 = 0x58; + pub const I8X16_ADD_SATURATE_U: u32 = 0x59; + pub const I8X16_SUB: u32 = 0x5a; + pub const I8X16_SUB_SATURATE_S: u32 = 0x5b; + pub const I8X16_SUB_SATURATE_U: u32 = 0x5c; + pub const I8X16_MUL: u32 = 0x5d; + + pub const I16X8_NEG: u32 = 0x62; + pub const I16X8_ANY_TRUE: u32 = 0x63; + pub const I16X8_ALL_TRUE: u32 = 0x64; + pub const I16X8_SHL: u32 = 0x65; + pub const I16X8_SHR_S: u32 = 0x66; + pub const I16X8_SHR_U: u32 = 0x67; + pub const I16X8_ADD: u32 = 0x68; + pub const I16X8_ADD_SATURATE_S: u32 = 0x69; + pub const I16X8_ADD_SATURATE_U: u32 = 0x6a; + pub const I16X8_SUB: u32 = 0x6b; + pub const I16X8_SUB_SATURATE_S: u32 = 0x6c; + pub const I16X8_SUB_SATURATE_U: u32 = 0x6d; + pub const I16X8_MUL: u32 = 0x6e; + + pub const I32X4_NEG: u32 = 0x73; + pub const I32X4_ANY_TRUE: u32 = 0x74; + pub const I32X4_ALL_TRUE: u32 = 0x75; + pub const I32X4_SHL: u32 = 0x76; + pub const I32X4_SHR_S: u32 = 0x77; + pub const I32X4_SHR_U: u32 = 0x78; + pub const I32X4_ADD: u32 = 0x79; + pub const I32X4_ADD_SATURATE_S: u32 = 0x7a; + pub const I32X4_ADD_SATURATE_U: u32 = 0x7b; + pub const I32X4_SUB: u32 = 0x7c; + pub const I32X4_SUB_SATURATE_S: u32 = 0x7d; + pub const I32X4_SUB_SATURATE_U: u32 = 0x7e; + pub const I32X4_MUL: u32 = 0x7f; + + pub const I64X2_NEG: u32 = 0x84; + pub const I64X2_ANY_TRUE: u32 = 0x85; + pub const I64X2_ALL_TRUE: u32 = 0x86; + pub const I64X2_SHL: u32 = 0x87; + pub const I64X2_SHR_S: u32 = 0x88; + pub const I64X2_SHR_U: u32 = 0x89; + pub const I64X2_ADD: u32 = 0x8a; + pub const I64X2_SUB: u32 = 0x8d; + + pub const F32X4_ABS: u32 = 0x95; + pub const F32X4_NEG: u32 = 0x96; + pub const F32X4_SQRT: u32 = 0x97; + pub const F32X4_ADD: u32 = 0x9a; + pub const F32X4_SUB: u32 = 0x9b; + pub const F32X4_MUL: u32 = 0x9c; + pub const F32X4_DIV: u32 = 0x9d; + pub const F32X4_MIN: u32 = 0x9e; + pub const F32X4_MAX: u32 = 0x9f; + + pub const F64X2_ABS: u32 = 0xa0; + pub const F64X2_NEG: u32 = 0xa1; + pub const F64X2_SQRT: u32 = 0xa2; + pub const F64X2_ADD: u32 = 0xa5; + pub const F64X2_SUB: u32 = 0xa6; + pub const F64X2_MUL: u32 = 0xa7; + pub const F64X2_DIV: u32 = 0xa8; + pub const F64X2_MIN: u32 = 0xa9; + pub const F64X2_MAX: u32 = 0xaa; + + pub const I32X4_TRUNC_S_F32X4_SAT: u32 = 0xab; + pub const I32X4_TRUNC_U_F32X4_SAT: u32 = 0xac; + pub const I64X2_TRUNC_S_F64X2_SAT: u32 = 0xad; + pub const I64X2_TRUNC_U_F64X2_SAT: u32 = 0xae; + + pub const F32X4_CONVERT_S_I32X4: u32 = 0xaf; + pub const F32X4_CONVERT_U_I32X4: u32 = 0xb0; + pub const F64X2_CONVERT_S_I64X2: u32 = 0xb1; + pub const F64X2_CONVERT_U_I64X2: u32 = 0xb2; + + pub const BULK_PREFIX: u8 = 0xfc; + pub const MEMORY_INIT: u8 = 0x08; + pub const MEMORY_DROP: u8 = 0x09; + pub const MEMORY_COPY: u8 = 0x0a; + pub const MEMORY_FILL: u8 = 0x0b; + pub const TABLE_INIT: u8 = 0x0c; + pub const TABLE_DROP: u8 = 0x0d; + pub const TABLE_COPY: u8 = 0x0e; +} + +impl Deserialize for Instruction { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + use self::Instruction::*; + use self::opcodes::*; + + let val: u8 = Uint8::deserialize(reader)?.into(); + + Ok( + match val { + UNREACHABLE => Unreachable, + NOP => Nop, + BLOCK => Block(BlockType::deserialize(reader)?), + LOOP => Loop(BlockType::deserialize(reader)?), + IF => If(BlockType::deserialize(reader)?), + ELSE => Else, + END => End, + + BR => Br(VarUint32::deserialize(reader)?.into()), + BRIF => BrIf(VarUint32::deserialize(reader)?.into()), + BRTABLE => { + let t1: Vec = CountedList::::deserialize(reader)? + .into_inner() + .into_iter() + .map(Into::into) + .collect(); + + BrTable(Box::new(BrTableData { + table: t1.into_boxed_slice(), + default: VarUint32::deserialize(reader)?.into(), + })) + }, + RETURN => Return, + CALL => Call(VarUint32::deserialize(reader)?.into()), + CALLINDIRECT => { + let signature: u32 = VarUint32::deserialize(reader)?.into(); + let table_ref: u8 = Uint8::deserialize(reader)?.into(); + if table_ref != 0 { return Err(Error::InvalidTableReference(table_ref)); } + + CallIndirect( + signature, + table_ref, + ) + }, + DROP => Drop, + SELECT => Select, + + GETLOCAL => GetLocal(VarUint32::deserialize(reader)?.into()), + SETLOCAL => SetLocal(VarUint32::deserialize(reader)?.into()), + TEELOCAL => TeeLocal(VarUint32::deserialize(reader)?.into()), + GETGLOBAL => GetGlobal(VarUint32::deserialize(reader)?.into()), + SETGLOBAL => SetGlobal(VarUint32::deserialize(reader)?.into()), + + I32LOAD => I32Load( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD => I64Load( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + F32LOAD => F32Load( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + F64LOAD => F64Load( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32LOAD8S => I32Load8S( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32LOAD8U => I32Load8U( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32LOAD16S => I32Load16S( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32LOAD16U => I32Load16U( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD8S => I64Load8S( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD8U => I64Load8U( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD16S => I64Load16S( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD16U => I64Load16U( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD32S => I64Load32S( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64LOAD32U => I64Load32U( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32STORE => I32Store( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64STORE => I64Store( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + F32STORE => F32Store( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + F64STORE => F64Store( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32STORE8 => I32Store8( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I32STORE16 => I32Store16( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64STORE8 => I64Store8( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64STORE16 => I64Store16( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + I64STORE32 => I64Store32( + VarUint32::deserialize(reader)?.into(), + VarUint32::deserialize(reader)?.into()), + + + CURRENTMEMORY => { + let mem_ref: u8 = Uint8::deserialize(reader)?.into(); + if mem_ref != 0 { return Err(Error::InvalidMemoryReference(mem_ref)); } + CurrentMemory(mem_ref) + }, + GROWMEMORY => { + let mem_ref: u8 = Uint8::deserialize(reader)?.into(); + if mem_ref != 0 { return Err(Error::InvalidMemoryReference(mem_ref)); } + GrowMemory(mem_ref) + } + + I32CONST => I32Const(VarInt32::deserialize(reader)?.into()), + I64CONST => I64Const(VarInt64::deserialize(reader)?.into()), + F32CONST => F32Const(Uint32::deserialize(reader)?.into()), + F64CONST => F64Const(Uint64::deserialize(reader)?.into()), + I32EQZ => I32Eqz, + I32EQ => I32Eq, + I32NE => I32Ne, + I32LTS => I32LtS, + I32LTU => I32LtU, + I32GTS => I32GtS, + I32GTU => I32GtU, + I32LES => I32LeS, + I32LEU => I32LeU, + I32GES => I32GeS, + I32GEU => I32GeU, + + I64EQZ => I64Eqz, + I64EQ => I64Eq, + I64NE => I64Ne, + I64LTS => I64LtS, + I64LTU => I64LtU, + I64GTS => I64GtS, + I64GTU => I64GtU, + I64LES => I64LeS, + I64LEU => I64LeU, + I64GES => I64GeS, + I64GEU => I64GeU, + + F32EQ => F32Eq, + F32NE => F32Ne, + F32LT => F32Lt, + F32GT => F32Gt, + F32LE => F32Le, + F32GE => F32Ge, + + F64EQ => F64Eq, + F64NE => F64Ne, + F64LT => F64Lt, + F64GT => F64Gt, + F64LE => F64Le, + F64GE => F64Ge, + + I32CLZ => I32Clz, + I32CTZ => I32Ctz, + I32POPCNT => I32Popcnt, + I32ADD => I32Add, + I32SUB => I32Sub, + I32MUL => I32Mul, + I32DIVS => I32DivS, + I32DIVU => I32DivU, + I32REMS => I32RemS, + I32REMU => I32RemU, + I32AND => I32And, + I32OR => I32Or, + I32XOR => I32Xor, + I32SHL => I32Shl, + I32SHRS => I32ShrS, + I32SHRU => I32ShrU, + I32ROTL => I32Rotl, + I32ROTR => I32Rotr, + + I64CLZ => I64Clz, + I64CTZ => I64Ctz, + I64POPCNT => I64Popcnt, + I64ADD => I64Add, + I64SUB => I64Sub, + I64MUL => I64Mul, + I64DIVS => I64DivS, + I64DIVU => I64DivU, + I64REMS => I64RemS, + I64REMU => I64RemU, + I64AND => I64And, + I64OR => I64Or, + I64XOR => I64Xor, + I64SHL => I64Shl, + I64SHRS => I64ShrS, + I64SHRU => I64ShrU, + I64ROTL => I64Rotl, + I64ROTR => I64Rotr, + F32ABS => F32Abs, + F32NEG => F32Neg, + F32CEIL => F32Ceil, + F32FLOOR => F32Floor, + F32TRUNC => F32Trunc, + F32NEAREST => F32Nearest, + F32SQRT => F32Sqrt, + F32ADD => F32Add, + F32SUB => F32Sub, + F32MUL => F32Mul, + F32DIV => F32Div, + F32MIN => F32Min, + F32MAX => F32Max, + F32COPYSIGN => F32Copysign, + F64ABS => F64Abs, + F64NEG => F64Neg, + F64CEIL => F64Ceil, + F64FLOOR => F64Floor, + F64TRUNC => F64Trunc, + F64NEAREST => F64Nearest, + F64SQRT => F64Sqrt, + F64ADD => F64Add, + F64SUB => F64Sub, + F64MUL => F64Mul, + F64DIV => F64Div, + F64MIN => F64Min, + F64MAX => F64Max, + F64COPYSIGN => F64Copysign, + + I32WRAPI64 => I32WrapI64, + I32TRUNCSF32 => I32TruncSF32, + I32TRUNCUF32 => I32TruncUF32, + I32TRUNCSF64 => I32TruncSF64, + I32TRUNCUF64 => I32TruncUF64, + I64EXTENDSI32 => I64ExtendSI32, + I64EXTENDUI32 => I64ExtendUI32, + I64TRUNCSF32 => I64TruncSF32, + I64TRUNCUF32 => I64TruncUF32, + I64TRUNCSF64 => I64TruncSF64, + I64TRUNCUF64 => I64TruncUF64, + F32CONVERTSI32 => F32ConvertSI32, + F32CONVERTUI32 => F32ConvertUI32, + F32CONVERTSI64 => F32ConvertSI64, + F32CONVERTUI64 => F32ConvertUI64, + F32DEMOTEF64 => F32DemoteF64, + F64CONVERTSI32 => F64ConvertSI32, + F64CONVERTUI32 => F64ConvertUI32, + F64CONVERTSI64 => F64ConvertSI64, + F64CONVERTUI64 => F64ConvertUI64, + F64PROMOTEF32 => F64PromoteF32, + + I32REINTERPRETF32 => I32ReinterpretF32, + I64REINTERPRETF64 => I64ReinterpretF64, + F32REINTERPRETI32 => F32ReinterpretI32, + F64REINTERPRETI64 => F64ReinterpretI64, + I32_EXTEND8_S => I32Extend8S, + I32_EXTEND16_S => I32Extend16S, + I64_EXTEND8_S => I64Extend8S, + I64_EXTEND16_S => I64Extend16S, + I64_EXTEND32_S => I64Extend32S, + + ATOMIC_PREFIX => return deserialize_atomic(reader), + SIMD_PREFIX => return deserialize_simd(reader), + + BULK_PREFIX => return deserialize_bulk(reader), + + _ => { return Err(Error::UnknownOpcode(val)); } + } + ) + } +} + +fn deserialize_atomic(reader: &mut R) -> Result { + use self::Instruction::*; + use self::opcodes::*; + + let val: u8 = Uint8::deserialize(reader)?.into(); + let mem = MemArg::deserialize(reader)?; + Ok(match val { + ATOMIC_WAKE => AtomicWake(mem), + I32_ATOMIC_WAIT => I32AtomicWait(mem), + I64_ATOMIC_WAIT => I64AtomicWait(mem), + + I32_ATOMIC_LOAD => I32AtomicLoad(mem), + I64_ATOMIC_LOAD => I64AtomicLoad(mem), + I32_ATOMIC_LOAD8U => I32AtomicLoad8u(mem), + I32_ATOMIC_LOAD16U => I32AtomicLoad16u(mem), + I64_ATOMIC_LOAD8U => I64AtomicLoad8u(mem), + I64_ATOMIC_LOAD16U => I64AtomicLoad16u(mem), + I64_ATOMIC_LOAD32U => I64AtomicLoad32u(mem), + I32_ATOMIC_STORE => I32AtomicStore(mem), + I64_ATOMIC_STORE => I64AtomicStore(mem), + I32_ATOMIC_STORE8U => I32AtomicStore8u(mem), + I32_ATOMIC_STORE16U => I32AtomicStore16u(mem), + I64_ATOMIC_STORE8U => I64AtomicStore8u(mem), + I64_ATOMIC_STORE16U => I64AtomicStore16u(mem), + I64_ATOMIC_STORE32U => I64AtomicStore32u(mem), + + I32_ATOMIC_RMW_ADD => I32AtomicRmwAdd(mem), + I64_ATOMIC_RMW_ADD => I64AtomicRmwAdd(mem), + I32_ATOMIC_RMW_ADD8U => I32AtomicRmwAdd8u(mem), + I32_ATOMIC_RMW_ADD16U => I32AtomicRmwAdd16u(mem), + I64_ATOMIC_RMW_ADD8U => I64AtomicRmwAdd8u(mem), + I64_ATOMIC_RMW_ADD16U => I64AtomicRmwAdd16u(mem), + I64_ATOMIC_RMW_ADD32U => I64AtomicRmwAdd32u(mem), + + I32_ATOMIC_RMW_SUB => I32AtomicRmwSub(mem), + I64_ATOMIC_RMW_SUB => I64AtomicRmwSub(mem), + I32_ATOMIC_RMW_SUB8U => I32AtomicRmwSub8u(mem), + I32_ATOMIC_RMW_SUB16U => I32AtomicRmwSub16u(mem), + I64_ATOMIC_RMW_SUB8U => I64AtomicRmwSub8u(mem), + I64_ATOMIC_RMW_SUB16U => I64AtomicRmwSub16u(mem), + I64_ATOMIC_RMW_SUB32U => I64AtomicRmwSub32u(mem), + + I32_ATOMIC_RMW_OR => I32AtomicRmwOr(mem), + I64_ATOMIC_RMW_OR => I64AtomicRmwOr(mem), + I32_ATOMIC_RMW_OR8U => I32AtomicRmwOr8u(mem), + I32_ATOMIC_RMW_OR16U => I32AtomicRmwOr16u(mem), + I64_ATOMIC_RMW_OR8U => I64AtomicRmwOr8u(mem), + I64_ATOMIC_RMW_OR16U => I64AtomicRmwOr16u(mem), + I64_ATOMIC_RMW_OR32U => I64AtomicRmwOr32u(mem), + + I32_ATOMIC_RMW_XOR => I32AtomicRmwXor(mem), + I64_ATOMIC_RMW_XOR => I64AtomicRmwXor(mem), + I32_ATOMIC_RMW_XOR8U => I32AtomicRmwXor8u(mem), + I32_ATOMIC_RMW_XOR16U => I32AtomicRmwXor16u(mem), + I64_ATOMIC_RMW_XOR8U => I64AtomicRmwXor8u(mem), + I64_ATOMIC_RMW_XOR16U => I64AtomicRmwXor16u(mem), + I64_ATOMIC_RMW_XOR32U => I64AtomicRmwXor32u(mem), + + I32_ATOMIC_RMW_XCHG => I32AtomicRmwXchg(mem), + I64_ATOMIC_RMW_XCHG => I64AtomicRmwXchg(mem), + I32_ATOMIC_RMW_XCHG8U => I32AtomicRmwXchg8u(mem), + I32_ATOMIC_RMW_XCHG16U => I32AtomicRmwXchg16u(mem), + I64_ATOMIC_RMW_XCHG8U => I64AtomicRmwXchg8u(mem), + I64_ATOMIC_RMW_XCHG16U => I64AtomicRmwXchg16u(mem), + I64_ATOMIC_RMW_XCHG32U => I64AtomicRmwXchg32u(mem), + + I32_ATOMIC_RMW_CMPXCHG => I32AtomicRmwCmpxchg(mem), + I64_ATOMIC_RMW_CMPXCHG => I64AtomicRmwCmpxchg(mem), + I32_ATOMIC_RMW_CMPXCHG8U => I32AtomicRmwCmpxchg8u(mem), + I32_ATOMIC_RMW_CMPXCHG16U => I32AtomicRmwCmpxchg16u(mem), + I64_ATOMIC_RMW_CMPXCHG8U => I64AtomicRmwCmpxchg8u(mem), + I64_ATOMIC_RMW_CMPXCHG16U => I64AtomicRmwCmpxchg16u(mem), + I64_ATOMIC_RMW_CMPXCHG32U => I64AtomicRmwCmpxchg32u(mem), + + _ => return Err(Error::UnknownOpcode(val)), + }) +} + +fn deserialize_simd(reader: &mut R) -> Result { + use self::Instruction::*; + use self::opcodes::*; + + let val = VarUint32::deserialize(reader)?.into(); + Ok(match val { + V128_CONST => { + let mut buf = [0; 16]; + reader.read(&mut buf)?; + V128Const(Box::new(buf)) + } + V128_LOAD => V128Load(MemArg::deserialize(reader)?), + V128_STORE => V128Store(MemArg::deserialize(reader)?), + I8X16_SPLAT => I8x16Splat, + I16X8_SPLAT => I16x8Splat, + I32X4_SPLAT => I32x4Splat, + I64X2_SPLAT => I64x2Splat, + F32X4_SPLAT => F32x4Splat, + F64X2_SPLAT => F64x2Splat, + I8X16_EXTRACT_LANE_S => I8x16ExtractLaneS(Uint8::deserialize(reader)?.into()), + I8X16_EXTRACT_LANE_U => I8x16ExtractLaneU(Uint8::deserialize(reader)?.into()), + I16X8_EXTRACT_LANE_S => I16x8ExtractLaneS(Uint8::deserialize(reader)?.into()), + I16X8_EXTRACT_LANE_U => I16x8ExtractLaneU(Uint8::deserialize(reader)?.into()), + I32X4_EXTRACT_LANE => I32x4ExtractLane(Uint8::deserialize(reader)?.into()), + I64X2_EXTRACT_LANE => I64x2ExtractLane(Uint8::deserialize(reader)?.into()), + F32X4_EXTRACT_LANE => F32x4ExtractLane(Uint8::deserialize(reader)?.into()), + F64X2_EXTRACT_LANE => F64x2ExtractLane(Uint8::deserialize(reader)?.into()), + I8X16_REPLACE_LANE => I8x16ReplaceLane(Uint8::deserialize(reader)?.into()), + I16X8_REPLACE_LANE => I16x8ReplaceLane(Uint8::deserialize(reader)?.into()), + I32X4_REPLACE_LANE => I32x4ReplaceLane(Uint8::deserialize(reader)?.into()), + I64X2_REPLACE_LANE => I64x2ReplaceLane(Uint8::deserialize(reader)?.into()), + F32X4_REPLACE_LANE => F32x4ReplaceLane(Uint8::deserialize(reader)?.into()), + F64X2_REPLACE_LANE => F64x2ReplaceLane(Uint8::deserialize(reader)?.into()), + V8X16_SHUFFLE => { + let mut buf = [0; 16]; + reader.read(&mut buf)?; + V8x16Shuffle(Box::new(buf)) + } + I8X16_ADD => I8x16Add, + I16X8_ADD => I16x8Add, + I32X4_ADD => I32x4Add, + I64X2_ADD => I64x2Add, + I8X16_SUB => I8x16Sub, + I16X8_SUB => I16x8Sub, + I32X4_SUB => I32x4Sub, + I64X2_SUB => I64x2Sub, + I8X16_MUL => I8x16Mul, + I16X8_MUL => I16x8Mul, + I32X4_MUL => I32x4Mul, + // I64X2_MUL => I64x2Mul, + I8X16_NEG => I8x16Neg, + I16X8_NEG => I16x8Neg, + I32X4_NEG => I32x4Neg, + I64X2_NEG => I64x2Neg, + + I8X16_ADD_SATURATE_S => I8x16AddSaturateS, + I8X16_ADD_SATURATE_U => I8x16AddSaturateU, + I16X8_ADD_SATURATE_S => I16x8AddSaturateS, + I16X8_ADD_SATURATE_U => I16x8AddSaturateU, + I8X16_SUB_SATURATE_S => I8x16SubSaturateS, + I8X16_SUB_SATURATE_U => I8x16SubSaturateU, + I16X8_SUB_SATURATE_S => I16x8SubSaturateS, + I16X8_SUB_SATURATE_U => I16x8SubSaturateU, + I8X16_SHL => I8x16Shl, + I16X8_SHL => I16x8Shl, + I32X4_SHL => I32x4Shl, + I64X2_SHL => I64x2Shl, + I8X16_SHR_S => I8x16ShrS, + I8X16_SHR_U => I8x16ShrU, + I16X8_SHR_S => I16x8ShrS, + I16X8_SHR_U => I16x8ShrU, + I32X4_SHR_S => I32x4ShrS, + I32X4_SHR_U => I32x4ShrU, + I64X2_SHR_S => I64x2ShrS, + I64X2_SHR_U => I64x2ShrU, + V128_AND => V128And, + V128_OR => V128Or, + V128_XOR => V128Xor, + V128_NOT => V128Not, + V128_BITSELECT => V128Bitselect, + I8X16_ANY_TRUE => I8x16AnyTrue, + I16X8_ANY_TRUE => I16x8AnyTrue, + I32X4_ANY_TRUE => I32x4AnyTrue, + I64X2_ANY_TRUE => I64x2AnyTrue, + I8X16_ALL_TRUE => I8x16AllTrue, + I16X8_ALL_TRUE => I16x8AllTrue, + I32X4_ALL_TRUE => I32x4AllTrue, + I64X2_ALL_TRUE => I64x2AllTrue, + I8X16_EQ => I8x16Eq, + I16X8_EQ => I16x8Eq, + I32X4_EQ => I32x4Eq, + // I64X2_EQ => I64x2Eq, + F32X4_EQ => F32x4Eq, + F64X2_EQ => F64x2Eq, + I8X16_NE => I8x16Ne, + I16X8_NE => I16x8Ne, + I32X4_NE => I32x4Ne, + // I64X2_NE => I64x2Ne, + F32X4_NE => F32x4Ne, + F64X2_NE => F64x2Ne, + I8X16_LT_S => I8x16LtS, + I8X16_LT_U => I8x16LtU, + I16X8_LT_S => I16x8LtS, + I16X8_LT_U => I16x8LtU, + I32X4_LT_S => I32x4LtS, + I32X4_LT_U => I32x4LtU, + // I64X2_LT_S => I64x2LtS, + // I64X2_LT_U => I64x2LtU, + F32X4_LT => F32x4Lt, + F64X2_LT => F64x2Lt, + I8X16_LE_S => I8x16LeS, + I8X16_LE_U => I8x16LeU, + I16X8_LE_S => I16x8LeS, + I16X8_LE_U => I16x8LeU, + I32X4_LE_S => I32x4LeS, + I32X4_LE_U => I32x4LeU, + // I64X2_LE_S => I64x2LeS, + // I64X2_LE_U => I64x2LeU, + F32X4_LE => F32x4Le, + F64X2_LE => F64x2Le, + I8X16_GT_S => I8x16GtS, + I8X16_GT_U => I8x16GtU, + I16X8_GT_S => I16x8GtS, + I16X8_GT_U => I16x8GtU, + I32X4_GT_S => I32x4GtS, + I32X4_GT_U => I32x4GtU, + // I64X2_GT_S => I64x2GtS, + // I64X2_GT_U => I64x2GtU, + F32X4_GT => F32x4Gt, + F64X2_GT => F64x2Gt, + I8X16_GE_S => I8x16GeS, + I8X16_GE_U => I8x16GeU, + I16X8_GE_S => I16x8GeS, + I16X8_GE_U => I16x8GeU, + I32X4_GE_S => I32x4GeS, + I32X4_GE_U => I32x4GeU, + // I64X2_GE_S => I64x2GeS, + // I64X2_GE_U => I64x2GeU, + F32X4_GE => F32x4Ge, + F64X2_GE => F64x2Ge, + F32X4_NEG => F32x4Neg, + F64X2_NEG => F64x2Neg, + F32X4_ABS => F32x4Abs, + F64X2_ABS => F64x2Abs, + F32X4_MIN => F32x4Min, + F64X2_MIN => F64x2Min, + F32X4_MAX => F32x4Max, + F64X2_MAX => F64x2Max, + F32X4_ADD => F32x4Add, + F64X2_ADD => F64x2Add, + F32X4_SUB => F32x4Sub, + F64X2_SUB => F64x2Sub, + F32X4_DIV => F32x4Div, + F64X2_DIV => F64x2Div, + F32X4_MUL => F32x4Mul, + F64X2_MUL => F64x2Mul, + F32X4_SQRT => F32x4Sqrt, + F64X2_SQRT => F64x2Sqrt, + F32X4_CONVERT_S_I32X4 => F32x4ConvertSI32x4, + F32X4_CONVERT_U_I32X4 => F32x4ConvertUI32x4, + F64X2_CONVERT_S_I64X2 => F64x2ConvertSI64x2, + F64X2_CONVERT_U_I64X2 => F64x2ConvertUI64x2, + I32X4_TRUNC_S_F32X4_SAT => I32x4TruncSF32x4Sat, + I32X4_TRUNC_U_F32X4_SAT => I32x4TruncUF32x4Sat, + I64X2_TRUNC_S_F64X2_SAT => I64x2TruncSF64x2Sat, + I64X2_TRUNC_U_F64X2_SAT => I64x2TruncUF64x2Sat, + + _ => return Err(Error::UnknownSimdOpcode(val)), + }) +} + +fn deserialize_bulk(reader: &mut R) -> Result { + use self::Instruction::*; + use self::opcodes::*; + + let val: u8 = Uint8::deserialize(reader)?.into(); + Ok(match val { + MEMORY_INIT => { + if u8::from(Uint8::deserialize(reader)?) != 0 { + return Err(Error::UnknownOpcode(val)) + } + MemoryInit(VarUint32::deserialize(reader)?.into()) + } + MEMORY_DROP => MemoryDrop(VarUint32::deserialize(reader)?.into()), + MEMORY_FILL => { + if u8::from(Uint8::deserialize(reader)?) != 0 { + return Err(Error::UnknownOpcode(val)) + } + MemoryFill + } + MEMORY_COPY => { + if u8::from(Uint8::deserialize(reader)?) != 0 { + return Err(Error::UnknownOpcode(val)) + } + MemoryCopy + } + + TABLE_INIT => { + if u8::from(Uint8::deserialize(reader)?) != 0 { + return Err(Error::UnknownOpcode(val)) + } + TableInit(VarUint32::deserialize(reader)?.into()) + } + TABLE_DROP => TableDrop(VarUint32::deserialize(reader)?.into()), + TABLE_COPY => { + if u8::from(Uint8::deserialize(reader)?) != 0 { + return Err(Error::UnknownOpcode(val)) + } + TableCopy + } + + _ => return Err(Error::UnknownOpcode(val)), + }) +} + +impl Deserialize for MemArg { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let align = Uint8::deserialize(reader)?; + let offset = VarUint32::deserialize(reader)?; + Ok(MemArg { align: align.into(), offset: offset.into() }) + } +} diff --git a/kernel-ewasm/validator/src/io.rs b/kernel-ewasm/validator/src/io.rs new file mode 100644 index 0000000..e1062e9 --- /dev/null +++ b/kernel-ewasm/validator/src/io.rs @@ -0,0 +1,120 @@ +#[cfg(feature="std")] +use std::io; + +#[cfg(not(feature = "std"))] +use pwasm_std::vec::Vec; + +/// IO specific error. +#[derive(Debug)] +pub enum Error { + /// Some unexpected data left in the buffer after reading all data. + TrailingData, + + /// Unexpected End-Of-File + UnexpectedEof, + + /// Invalid data is encountered. + InvalidData, + + #[cfg(feature = "std")] + IoError(io::Error), +} + +/// IO specific Result. +pub type Result = core::result::Result; + +pub trait Write { + /// Write a buffer of data into this write. + /// + /// All data is written at once. + fn write(&mut self, buf: &[u8]) -> Result<()>; +} + +pub trait Read { + /// Read a data from this read to a buffer. + /// + /// If there is not enough data in this read then `UnexpectedEof` will be returned. + fn read(&mut self, buf: &mut [u8]) -> Result<()>; +} + +/// Reader that saves the last position. +pub struct Cursor { + inner: T, + pos: usize, +} + +impl Cursor { + pub fn new(inner: T) -> Cursor { + Cursor { + inner, + pos: 0, + } + } + + pub fn position(&self) -> usize { + self.pos + } +} + +impl> Read for Cursor { + fn read(&mut self, buf: &mut [u8]) -> Result<()> { + let slice = self.inner.as_ref(); + let remainder = slice.len() - self.pos; + let requested = buf.len(); + if requested > remainder { + return Err(Error::UnexpectedEof); + } + buf.copy_from_slice(&slice[self.pos..(self.pos + requested)]); + self.pos += requested; + Ok(()) + } +} + +#[cfg(not(feature = "std"))] +impl Write for Vec { + fn write(&mut self, buf: &[u8]) -> Result<()> { + self.extend(buf); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for T { + fn read(&mut self, buf: &mut [u8]) -> Result<()> { + self.read_exact(buf) + .map_err(Error::IoError) + } +} + +#[cfg(feature = "std")] +impl Write for T { + fn write(&mut self, buf: &[u8]) -> Result<()> { + self.write_all(buf).map_err(Error::IoError) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cursor() { + let mut cursor = Cursor::new(vec![0xFFu8, 0x7Fu8]); + assert_eq!(cursor.position(), 0); + + let mut buf = [0u8]; + assert!(cursor.read(&mut buf[..]).is_ok()); + assert_eq!(cursor.position(), 1); + assert_eq!(buf[0], 0xFFu8); + assert!(cursor.read(&mut buf[..]).is_ok()); + assert_eq!(buf[0], 0x7Fu8); + assert_eq!(cursor.position(), 2); + } + + #[test] + fn overflow_in_cursor() { + let mut cursor = Cursor::new(vec![0u8]); + let mut buf = [0, 1, 2]; + assert!(cursor.read(&mut buf[..]).is_err()); + } +} diff --git a/kernel-ewasm/validator/src/lib.rs b/kernel-ewasm/validator/src/lib.rs index 303ee49..f832aa2 100644 --- a/kernel-ewasm/validator/src/lib.rs +++ b/kernel-ewasm/validator/src/lib.rs @@ -1,15 +1,27 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] -use pwasm_std; -use parity_wasm; - -pub use parity_wasm::elements::{ImportEntry, Module}; -use parity_wasm::elements::Instruction; -use parity_wasm::elements::{ValueType}; +#[cfg(not(feature="std"))] +#[macro_use] +extern crate alloc; +use pwasm_std; use pwasm_std::vec::Vec; +use pwasm_std::String; + +pub mod instructions; +pub mod func; +mod primitives; +pub mod io; +pub mod serialization; +pub mod import_entry; +pub mod types; +pub use self::io::{Error}; +pub use self::serialization::{Deserialize}; -pub use parity_wasm::deserialize_buffer; +pub use self::primitives::{ + VarUint32, VarUint7, Uint8, VarUint1, VarInt7, Uint32, VarInt32, VarInt64, + Uint64, VarUint64, CountedList +}; /// As per the wasm spec: /// @@ -49,16 +61,23 @@ pub trait Listed { fn listing(&self) -> Listing; } +#[derive(Debug, Clone)] +pub struct ImportEntry { + index: u32, + mod_name: String, + field_name: String, +} + impl Listed for ImportEntry { fn listing(&self) -> Listing { // Nothing should need to be imported from outside "env", but let's // blacklist it just in case. - if self.module() != "env" { + if self.mod_name != "env" { Listing::Black } else { // Tehcnically we don't have to list blacklisted items here, but we // do just for clarity. - match self.field() { + match self.field_name.as_ref() { "memory" => Listing::White, "storage_read" => Listing::White, "storage_write" => Listing::Black, @@ -94,113 +113,521 @@ impl Listed for ImportEntry { } } -/// Information on why the contract was considered invalid. -#[derive(Debug)] -pub struct ValidityReport { - pub validation_errors: Vec, +/// Be able to determine a contracts validity. +pub trait Validity { + fn is_valid(&self) -> bool; } +// Seek does not seem to be implemented in core, so we'll reimplement what we +// need. #[derive(Debug)] -pub enum ValidityError { - BlacklistedImport(ImportEntry), - UnsafeGreylistedCall { - import: ImportEntry, - function_index: u32, - instruction_index: u32, - }, +struct Cursor<'a> { + current_offset: usize, + body: &'a [u8], } -/// Be able to determine a contracts validity. -pub trait Validity { - fn is_valid(&self) -> bool; - fn validity(&self) -> ValidityReport; +impl<'a> Cursor<'a> { + // Read the byte at the cusor, and increment the pointer by 1. + fn read_ref(&mut self) -> &'a u8 { + let val = &self.body[self.current_offset]; + self.current_offset += 1; + val + } + + fn read_ref_n(&mut self, n: usize) -> &'a [u8] { + let val = &self.body[self.current_offset..(self.current_offset+n)]; + self.current_offset += n; + val + } + + fn skip(&mut self, n: usize) { + self.current_offset += n; + } } -impl Validity for Module { - fn is_valid(&self) -> bool { - self.validity().validation_errors.len() == 0 +/// Implement standard read definition (which clones). This is basically the +/// rust definition of read for slice. +impl<'a> io::Read for Cursor<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<()> { + let actual_self = &self.body[self.current_offset..]; + let amt = core::cmp::min(buf.len(), actual_self.len()); + let (a, _) = actual_self.split_at(amt); + + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + self.current_offset += amt; + Ok(()) } +} - fn validity(&self) -> ValidityReport { - let imports = get_imports(self); - let mut report = ValidityReport { - validation_errors: Vec::new() +impl Validity for &[u8] { + fn is_valid(&self) -> bool { + // Set an index value, which is our offset into the wasm bytes. + let mut cursor = Cursor { + current_offset: 0, + body: self, }; - // TODO: this i value needs to be checked to ensure it is as defined by - // the standard. - for (import_index, import) in imports.iter().enumerate() { - match import.listing() { - Listing::White => (), - Listing::Grey => { - // Check that this grey import is called safely, wherever is - // is called. - for (function_index,instruction_index) in check_grey(self, import_index) { - report.validation_errors.push(ValidityError::UnsafeGreylistedCall { - import: import.clone(), - function_index, - instruction_index, - }); - } + + // The first two steps are to take the magic number and version to check + // that it is valid wasm. This is not strictly necessary, as it is the + // job of the runtime to ensure the wasm is valid (ad we rely on that + // fact), however, it's cheap and allows us prevent future versions of + // wasm code being deployed (for which our assumptions may not hold). + + // Take the magic number, check that it matches + if cursor.read_ref_n(4) != &[0, 97, 115, 109] { + panic!("magic number not found"); + } + + // Take the version, check that it matches + if cursor.read_ref_n(4) != &[1, 0, 0, 0] { + panic!("proper version number not found"); + } + + // Now we should be at the first section. We care about 4 sections: + // types, imports, functions, and code. The first thing we want to do is + // to find the offsets of these 4 sections. We assume the wasm is well + // formed and there are no duplicate sections and the like. It is also + // possible some of these sections don't exist. + let mut type_section_offset: Option = None; + let mut import_section_offset: Option = None; + let mut function_section_offset: Option = None; + let mut code_section_offset: Option = None; + while cursor.current_offset < self.len() { + let section: Section = parse_section(&mut cursor); + // There are many section types we don't care about, for example, + // Custom sections generally contain debugging symbols and + // meaningful function names which are irrelevant to the current + // process. We care only about types, imports, functions, and code. + match section.type_ { + SectionType::Type => { + if type_section_offset.is_some() {panic!("multiple type sections");} + type_section_offset = Some(section.offset); + }, + SectionType::Import => { + if import_section_offset.is_some() {panic!("multiple import sections");} + import_section_offset = Some(section.offset); }, - Listing::Black => { - report.validation_errors.push(ValidityError::BlacklistedImport(import.clone())); + SectionType::Function => { + if function_section_offset.is_some() {panic!("multiple function sections");} + function_section_offset = Some(section.offset); }, + SectionType::Code => { + if code_section_offset.is_some() {panic!("multiple code sections");} + code_section_offset = Some(section.offset); + }, + // We ignore any section we are not interested in. + _ => (), } } - report + if cursor.current_offset != self.len() { + panic!("mismatched length"); + } + + // Now that we have our hooks into the module, let's iterate over the + // imports to determine white/grey/black listings. We need to remember + // where the function and code data starts. + + // There is only one greylisted item (dcall) so we will just reserve a + // place for that rather than maintain a list. + let mut dcall_index: Option = None; + let mut gasleft_index: Option = None; + let mut sender_index: Option = None; + if let Some(imports_offset) = import_section_offset { + // Make a new cursor for imports + let mut imports_cursor = Cursor {current_offset:imports_offset,body:&self}; + let _section_size = parse_varuint_32(&mut imports_cursor); + // How many imports do we have? + let n_imports = parse_varuint_32(&mut imports_cursor); + // println!("n_imports: {}", n_imports); + for i in 0..n_imports { + // let mut cursor = Cursor {i:0}; + + // Here we parse the names of the import, and its function + // index. + let import = parse_import(&mut imports_cursor, i); + + if import.mod_name == "env" && import.field_name == "sender" { + if sender_index.is_some() {panic!("sender imported multiple times");} + sender_index = Some(import.index as usize); + } + + if import.mod_name == "env" && import.field_name == "gasleft" { + if gasleft_index.is_some() {panic!("gasleft imported multiple times");} + gasleft_index = Some(import.index as usize); + } + + // println!("mod_name: {}, field_name: {}, f_index: {}, listing: {:?}", + // import.mod_name, import.field_name, import.index, import.listing()); + match import.listing() { + Listing::White => (), + Listing::Grey => { + if dcall_index.is_some() {panic!("dcall imported multiple times");} + // Document here why this is the case + dcall_index = Some(import.index as usize); + }, + Listing::Black => { + // If we encounter a blacklisted import we can return + // early. + // println!("{:?} is blacklisted", import); + return false; + }, + } + } + } + + // The functions index into types. In fact the function section is just + // a vector of type ids. We don't care about types at this stage. + if let (Some(functions_offset), Some(code_offset)) = (function_section_offset, code_section_offset) { + // Make a new cursor for functions + let mut functions_cursor = Cursor {current_offset:functions_offset,body:&self}; + // Make a new cursor for code + let mut code_cursor = Cursor {current_offset:code_offset,body:&self}; + // We will have to try and update these in parallel + let _function_section_size = parse_varuint_32(&mut functions_cursor); + let _code_section_size = parse_varuint_32(&mut code_cursor); + // println!("functions_offset: {:?}", functions_offset); + // println!("code_offset: {:?}", code_offset); + let n_functions = parse_varuint_32(&mut functions_cursor); + let n_bodies = parse_varuint_32(&mut code_cursor); + + // println!("functions_size: {:?}", function_section_size); + // println!("code_size: {:?}", code_section_size); + + assert_eq!(n_functions,n_bodies); + + let dcall_index: Option = None; + let gasleft_index: Option = None; + let sender_index: Option = None; + + // Next we iterate through the function bodies and check if they + // violate any of our rules. + for _i in 0..n_bodies { + let body_size = parse_varuint_32(&mut code_cursor); + // First we check if it is a system call, this is only possible + // if we have the three required imports. + if let (Some(dcall_i),Some(gasleft_i),Some(sender_i)) = (dcall_index,gasleft_index,sender_index) { + if is_syscall(dcall_i as u32, gasleft_i as u32, sender_i as u32, &self[(code_cursor.current_offset)..(code_cursor.current_offset+body_size as usize)]) { + // If the function is a system call we can continue past it + continue; + } + } + // let body = parse_varuint_32(&mut code_cursor, &self); + // println!("function[{}] is {} bytes", i, body_size); + code_cursor.skip(body_size as usize); + // As the function is not a system call, it is not permitted to + // have a dcall in it, so we iterate through all the + // instructions. If we encounter a dcall, we return with a + // false, as this is invalid. + + + } + + // // How many imports do we have? + // let n_imports = parse_varuint_32(&mut imports_cursor, &self); + // for i in 0..n_imports { + // let mut cursor = Cursor {i:0}; + + // // Here we parse the names of the import, and its function + // // index. + // let import = parse_import(&mut cursor, data, n); + + // println!("mod_name: {}, field_name: {}, f_index: {}, listing: {:?}", + // import.mod_name, import.field_name, import.index, import.listing()); + // match import.listing() { + // Listing::White => (), + // Listing::Grey => { + // if dcall_index.is_some() {panic!("dcall imported multiple times");} + // // Document here why this is the case + // dcall_index = Some(import.index); + // }, + // Listing::Black => { + // // If we encounter a blacklisted import we can return + // // early. + // println!("{:?} is blacklisted", import); + // return false; + // }, + // } + // } + } + + // We now know the location of dcall, if there is one. + // We need to iterate over every function and read its code. A + // function can be one of three things: + // + // * A syscall that follows the format + // * A function which is not a syscall and features a greylisted call. + // * A function which does not contain a greylisted call or a blacklistd call. + // The possiblities are checked in that order. + + // Let's find the functions: + // for section in sections { + // } + + // for function in functions { + + // } + + + // for import in greys { + // // If the grey test does not pass return early with false. + // if !check_grey(&self, import.index) { + // return false; + // } + // } + + // All the tests have passed so we can return true. + true } } -fn get_imports(module: &Module) -> Vec { - if let Some(import_section) = module.import_section() { - import_section.entries().to_vec() - } else { - Vec::new() +#[derive(Debug)] +enum SectionType { + Custom, + Type, + Import, + Function, + Table, + Memory, + Global, + Export, + Start, + Element, + Code, + Data, +} + +#[derive(Debug)] +struct Section { + type_: SectionType, + // The offset is the byte offset of the start of this + // section, i.e. it points directly to the length byte. + offset: usize, +} + +fn parse_section(cursor: &mut Cursor) -> Section { + let type_n = cursor.read_ref(); + let offset = cursor.current_offset; + let size_n = parse_varuint_32(cursor); + let type_ = n_to_section(type_n); + let section = Section { + type_, + offset, + }; + cursor.current_offset += size_n as usize; + section +} + +fn n_to_section(byte: &u8) -> SectionType { + match byte { + 0 => SectionType::Custom, + 1 => SectionType::Type, + 2 => SectionType::Import, + 3 => SectionType::Function, + 4 => SectionType::Table, + 5 => SectionType::Memory, + 6 => SectionType::Global, + 7 => SectionType::Export, + 8 => SectionType::Start, + 9 => SectionType::Element, + 10 => SectionType::Code, + 11 => SectionType::Data, + _ => panic!("invalid section type"), } } -fn check_grey(module: &Module, grey_index: usize) -> Vec<(u32, u32)> { - let mut uses = Vec::new(); - let code_section = module.code_section().unwrap(); - let codes = Vec::from(code_section.bodies()); - // If the instruction Call(grey_index) exists in the body of the function, that is a dangerous function. - let this_call = parity_wasm::elements::Instruction::Call(grey_index as u32); - for (func_index, func_body) in codes.iter().enumerate() { - for (instruction_index, instruction) in func_body.code().elements().iter().enumerate() { - if instruction == &this_call && !is_syscall(module, func_index as u32) { - uses.push((func_index as u32, instruction_index as u32)); +fn parse_varuint_32(cursor: &mut Cursor) -> u32 { + let mut res = 0; + let mut shift = 0; + loop { + if shift > 31 { panic!("invalid varuint32"); } + + let b = cursor.read_ref().clone() as u32; + res |= (b & 0x7f).checked_shl(shift).expect("invalid varuint32"); + shift += 7; + if (b >> 7) == 0 { + if shift >= 32 && (b as u8).leading_zeros() < 4 { + panic!("invalid varuint32"); } + break; } } - uses + res } -// Find the function index of an import -pub fn find_import(module: &Module, mod_name: &str, field_name: &str) -> Option { - let imports = module.import_section().unwrap().entries(); - for (i,import) in imports.iter().enumerate() { - if import.module() == mod_name && import.field() == field_name { - return Some(i as u32); +fn parse_import(cursor: &mut Cursor, index: u32) -> ImportEntry { + let mut reader = Cursor { + current_offset: cursor.current_offset, + body: cursor.body, + }; + let import: import_entry::ImportEntry = import_entry::ImportEntry::deserialize(&mut reader).expect("counted list"); + let val = ImportEntry { + index, + mod_name: String::from(import.module()), + field_name: String::from(import.field()), + }; + cursor.current_offset = reader.current_offset; + val +} + +// pub fn is_syscall(module: &Module, function_index: u32) -> bool { + +// let function_section = module.function_section().unwrap(); +// let functions = Vec::from(function_section.entries()); +// let function = functions.get(function_index as usize).unwrap(); +// let type_index = function.type_ref(); + +// let type_section = module.type_section().unwrap(); +// let types = Vec::from(type_section.types()); +// let this_type = types.get(type_index as usize).unwrap(); + +// let code_section = module.code_section().unwrap(); +// let codes = Vec::from(code_section.bodies()); +// let code = codes.get(function_index as usize).unwrap(); +// let instructions = Vec::from(code.code().elements()); + +// // First we need to check that the instructions are correct, that is: +// // 0. call $a +// // 1. call $b +// // 2. get_local 0 +// // 3. get_local 1 +// // 4. get_local 2 +// // 5. get_local 3 +// // 6. call $c +// // $a, $b, and $c will be used later. +// // First we simply check the length +// if instructions.len() != 8 { +// return false; +// } +// // 0. call gasleft +// if let Instruction::Call(f_ind) = instructions[0] { +// // Check that f_ind is the function index of "gasleft" +// let gasleft_index = find_import(module, "env", "gasleft"); +// if Some(f_ind) != gasleft_index { +// return false; +// } +// } else { +// return false; +// } +// // 1. call sender +// if let Instruction::Call(f_ind) = instructions[1] { +// // Check that f_ind is the function index of "sender" +// let sender_index = find_import(module, "env", "sender"); +// if Some(f_ind) != sender_index { +// return false; +// } +// } else { +// return false; +// } +// // 2. get_local 0 +// if let Instruction::GetLocal(0) = instructions[2] { +// } else { +// return false; +// } +// // 3. get_local 1 +// if let Instruction::GetLocal(1) = instructions[3] { +// } else { +// return false; +// } +// // 4. get_local 2 +// if let Instruction::GetLocal(2) = instructions[4] { +// } else { +// return false; +// } +// // 5. get_local 3 +// if let Instruction::GetLocal(3) = instructions[5] { +// } else { +// return false; +// } + +// // 6. call dcall +// if let Instruction::Call(f_ind) = instructions[6] { +// // Check that f_ind is the function index of "dcall" +// let dcall_index = find_import(module, "env", "dcall"); +// if Some(f_ind) != dcall_index { +// return false; +// } +// } else { +// return false; +// } +// // 7. END +// if let Instruction::End = instructions[7] { +// } else { +// return false; +// } + +// // Check that no locals are used +// if code.locals().len() > 0 { +// return false; +// } +// // Check that the type signature is correct +// let parity_wasm::elements::Type::Function(f_type) = this_type; +// if f_type.return_type() != Some(ValueType::I32) { +// return false; +// } +// if f_type.params() != [ ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32] { +// return false; +// } +// if f_type.form() != 0x60 { +// return false; +// } + +// true +// } + +/// An iterator which counts from one to five +struct Code<'a> { + current_offset: usize, + body: &'a [u8], +} + +// we want our count to start at one, so let's add a new() method to help. +// This isn't strictly necessary, but is convenient. Note that we start +// `count` at zero, we'll see why in `next()`'s implementation below. +impl<'a> Code<'a> { + fn new(body: &'a [u8]) -> Code { + let mut reader = Cursor { + current_offset: 0, + body: body, + }; + // We currently don't care about locals + let _locals: Vec = CountedList::::deserialize(&mut reader).expect("counted list").into_inner(); + Code { + current_offset: reader.current_offset, + body: body, } } - return None; } -pub fn is_syscall(module: &Module, function_index: u32) -> bool { +impl<'a> Iterator for Code<'a> { + type Item = crate::instructions::Instruction; - let function_section = module.function_section().unwrap(); - let functions = Vec::from(function_section.entries()); - let function = functions.get(function_index as usize).unwrap(); - let type_index = function.type_ref(); - - let type_section = module.type_section().unwrap(); - let types = Vec::from(type_section.types()); - let this_type = types.get(type_index as usize).unwrap(); + fn next(&mut self) -> Option { + if self.current_offset < self.body.len() { + // We need to parse the code into something meaningful + let mut reader = Cursor { + current_offset: self.current_offset, + body: self.body, + }; + let val = Some(crate::instructions::Instruction::deserialize(&mut reader).expect("expected valid instruction")); + self.current_offset = reader.current_offset; + val + } else { + None + } + } +} - let code_section = module.code_section().unwrap(); - let codes = Vec::from(code_section.bodies()); - let code = codes.get(function_index as usize).unwrap(); - let instructions = Vec::from(code.code().elements()); +// TODO: we need to provide the indices of the various necessary functions for +// the system call. +pub fn is_syscall(dcall_i: u32, gasleft_i: u32, sender_i: u32, body: &[u8]) -> bool { + // println!("body: {:?}", body); + let mut code_iter = Code::new(body); + // let mut indexed_iter = code_iter.enumerate(); // First we need to check that the instructions are correct, that is: // 0. call $a @@ -211,103 +638,92 @@ pub fn is_syscall(module: &Module, function_index: u32) -> bool { // 5. get_local 3 // 6. call $c // $a, $b, and $c will be used later. - // First we simply check the length - if instructions.len() != 8 { - return false; - } + + // 0. call gasleft - if let Instruction::Call(f_ind) = instructions[0] { - // Check that f_ind is the function index of "gasleft" - let gasleft_index = find_import(module, "env", "gasleft"); - if Some(f_ind) != gasleft_index { + if let Some(instructions::Instruction::Call(f_ind)) = code_iter.next() { + if f_ind != gasleft_i { return false; } } else { return false; } // 1. call sender - if let Instruction::Call(f_ind) = instructions[1] { - // Check that f_ind is the function index of "sender" - let sender_index = find_import(module, "env", "sender"); - if Some(f_ind) != sender_index { + if let Some(instructions::Instruction::Call(f_ind)) = code_iter.next() { + if f_ind != sender_i { return false; } } else { return false; } // 2. get_local 0 - if let Instruction::GetLocal(0) = instructions[2] { + if let Some(instructions::Instruction::GetLocal(0)) = code_iter.next() { } else { return false; } // 3. get_local 1 - if let Instruction::GetLocal(1) = instructions[3] { + if let Some(instructions::Instruction::GetLocal(1)) = code_iter.next() { } else { return false; } // 4. get_local 2 - if let Instruction::GetLocal(2) = instructions[4] { + if let Some(instructions::Instruction::GetLocal(2)) = code_iter.next() { } else { return false; } // 5. get_local 3 - if let Instruction::GetLocal(3) = instructions[5] { + if let Some(instructions::Instruction::GetLocal(3)) = code_iter.next() { } else { return false; } // 6. call dcall - if let Instruction::Call(f_ind) = instructions[6] { - // Check that f_ind is the function index of "dcall" - let dcall_index = find_import(module, "env", "dcall"); - if Some(f_ind) != dcall_index { + if let Some(instructions::Instruction::Call(f_ind)) = code_iter.next() { + if f_ind != dcall_i { return false; } } else { return false; } // 7. END - if let Instruction::End = instructions[7] { + if let Some(instructions::Instruction::End) = code_iter.next() { } else { return false; } - // Check that no locals are used - if code.locals().len() > 0 { - return false; - } - // Check that the type signature is correct - let parity_wasm::elements::Type::Function(f_type) = this_type; - if f_type.return_type() != Some(ValueType::I32) { - return false; - } - if f_type.params() != [ ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32] { - return false; - } - if f_type.form() != 0x60 { - return false; - } + // // Check that no locals are used + // if code.locals().len() > 0 { + // return false; + // } + // // Check that the type signature is correct + // let parity_wasm::elements::Type::Function(f_type) = this_type; + // if f_type.return_type() != Some(ValueType::I32) { + // return false; + // } + // if f_type.params() != [ ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32] { + // return false; + // } + // if f_type.form() != 0x60 { + // return false; + // } - true + // true + false } #[cfg(test)] mod tests { - // extern crate pwasm_test; - // use std; use super::*; use wabt::wat2wasm; - // use core::str::FromStr; - // use pwasm_abi::types::*; - // use self::pwasm_test::{ext_reset, ext_get}; - // use token::TokenInterface; + use std::fs::File; + use std::io::Read; #[test] fn module_only_pass() { let wat = "(module)"; let wasm = wat2wasm(wat).unwrap(); - let module: Module = parity_wasm::deserialize_buffer(wasm.as_slice()).expect("deserialise wasm"); - assert!(module.is_valid()); + // let module: Module = parity_wasm::deserialize_buffer(wasm.as_slice()).expect("deserialise wasm"); + assert!(wasm.as_slice().is_valid()); } #[test] @@ -321,30 +737,37 @@ mod tests { (export "call" (func $call))) "#; let wasm = wat2wasm(wat).unwrap(); - let module: Module = parity_wasm::deserialize_buffer(wasm.as_slice()).expect("deserialise wasm"); - assert!(module.is_valid()); + assert!(wasm.as_slice().is_valid()); } #[test] - fn minimal_contract_with_write_fail() { - let wat = r#" -;; Minimal contract with a single storage write call -(module - (type $t0 (func)) - (type $t1 (func (param i32 i32))) - (import "env" "storage_write" (func $env.storage_write (type $t1))) - (func $call (type $t0) - i32.const 5 - i32.const 15 - call $env.storage_write - unreachable) - (export "call" (func $call))) -"#; - let wasm: pwasm_std::Vec = wat2wasm(wat).unwrap(); - let module: Module = parity_wasm::deserialize_buffer(wasm.as_slice()).expect("deserialise wasm"); - assert!(!module.is_valid()); + fn example_contract_1_pass() { + let mut f = File::open("../example_contract_1/target/wasm32-unknown-unknown/release/example_contract_1.wasm").expect("could not open file"); + let mut wasm = Vec::new(); + f.read_to_end(&mut wasm).unwrap(); + assert!(!wasm.as_slice().is_valid()); } +// #[test] +// fn minimal_contract_with_write_fail() { +// let wat = r#" +// ;; Minimal contract with a single storage write call +// (module +// (type $t0 (func)) +// (type $t1 (func (param i32 i32))) +// (import "env" "storage_write" (func $env.storage_write (type $t1))) +// (func $call (type $t0) +// i32.const 5 +// i32.const 15 +// call $env.storage_write +// unreachable) +// (export "call" (func $call))) +// "#; +// let wasm: pwasm_std::Vec = wat2wasm(wat).unwrap(); +// let module: Module = parity_wasm::deserialize_buffer(wasm.as_slice()).expect("deserialise wasm"); +// assert!(!module.is_valid()); +// } + // #[test] // fn should_reject_invalid_address() { // let mut contract = contract::ValidatorContract {}; diff --git a/kernel-ewasm/validator/src/primitives.rs b/kernel-ewasm/validator/src/primitives.rs new file mode 100644 index 0000000..27796a1 --- /dev/null +++ b/kernel-ewasm/validator/src/primitives.rs @@ -0,0 +1,712 @@ +// This file is based on parity-wasm from parity, MIT & Apache Licensed +// use crate::rust::{vec::Vec, string::String}; +use crate::{io}; +use crate::{Deserialize}; +use pwasm_std::vec::Vec; +use pwasm_std::String; +use crate::serialization::{Error}; + + +macro_rules! buffered_read { + ($buffer_size: expr, $length: expr, $reader: expr) => { + { + let mut vec_buf = Vec::new(); + let mut total_read = 0; + let mut buf = [0u8; $buffer_size]; + while total_read < $length { + let next_to_read = if $length - total_read > $buffer_size { $buffer_size } else { $length - total_read }; + $reader.read(&mut buf[0..next_to_read])?; + vec_buf.extend_from_slice(&buf[0..next_to_read]); + total_read += next_to_read; + } + vec_buf + } + } +} + + + +/// Unsigned variable-length integer, limited to 32 bits, +/// represented by at most 5 bytes that may contain padding 0x80 bytes. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarUint32(u32); + +impl From for usize { + fn from(var: VarUint32) -> usize { + var.0 as usize + } +} + +impl From for u32 { + fn from(var: VarUint32) -> u32 { + var.0 + } +} + +impl From for VarUint32 { + fn from(i: u32) -> VarUint32 { + VarUint32(i) + } +} + +impl From for VarUint32 { + fn from(i: usize) -> VarUint32 { + assert!(i <= u32::max_value() as usize); + VarUint32(i as u32) + } +} + +impl Deserialize for VarUint32 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut res = 0; + let mut shift = 0; + let mut u8buf = [0u8; 1]; + loop { + if shift > 31 { return Err(Error::InvalidVarUint32); } + + reader.read(&mut u8buf)?; + let b = u8buf[0] as u32; + res |= (b & 0x7f).checked_shl(shift).ok_or(Error::InvalidVarUint32)?; + shift += 7; + if (b >> 7) == 0 { + if shift >= 32 && (b as u8).leading_zeros() < 4 { + return Err(Error::InvalidVarInt32); + } + break; + } + } + Ok(VarUint32(res)) + } +} + +/// Unsigned variable-length integer, limited to 64 bits, +/// represented by at most 9 bytes that may contain padding 0x80 bytes. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarUint64(u64); + +impl From for u64 { + fn from(var: VarUint64) -> u64 { + var.0 + } +} + +impl Deserialize for VarUint64 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut res = 0; + let mut shift = 0; + let mut u8buf = [0u8; 1]; + loop { + if shift > 63 { return Err(Error::InvalidVarUint64); } + + reader.read(&mut u8buf)?; + let b = u8buf[0] as u64; + res |= (b & 0x7f).checked_shl(shift).ok_or(Error::InvalidVarUint64)?; + shift += 7; + if (b >> 7) == 0 { + if shift >= 64 && (b as u8).leading_zeros() < 7 { + return Err(Error::InvalidVarInt64); + } + break; + } + } + Ok(VarUint64(res)) + } +} + +impl From for VarUint64 { + fn from(u: u64) -> VarUint64 { + VarUint64(u) + } +} + +/// 7-bit unsigned integer, encoded in LEB128 (always 1 byte length). +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarUint7(u8); + +impl From for u8 { + fn from(v: VarUint7) -> u8 { + v.0 + } +} + +impl From for VarUint7 { + fn from(v: u8) -> Self { + VarUint7(v) + } +} + +impl Deserialize for VarUint7 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut u8buf = [0u8; 1]; + reader.read(&mut u8buf)?; + Ok(VarUint7(u8buf[0])) + } +} + +/// 7-bit signed integer, encoded in LEB128 (always 1 byte length) +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarInt7(i8); + +impl From for i8 { + fn from(v: VarInt7) -> i8 { + v.0 + } +} + +impl From for VarInt7 { + fn from(v: i8) -> VarInt7 { + VarInt7(v) + } +} + +impl Deserialize for VarInt7 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut u8buf = [0u8; 1]; + reader.read(&mut u8buf)?; + + // check if number is not continued! + if u8buf[0] & 0b1000_0000 != 0 { + return Err(Error::InvalidVarInt7(u8buf[0])); + } + + // expand sign + if u8buf[0] & 0b0100_0000 == 0b0100_0000 { u8buf[0] |= 0b1000_0000 } + + Ok(VarInt7(u8buf[0] as i8)) + } +} + +/// 8-bit unsigned integer, NOT encoded in LEB128; +/// it's just a single byte. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Uint8(u8); + +impl From for u8 { + fn from(v: Uint8) -> u8 { + v.0 + } +} + +impl From for Uint8 { + fn from(v: u8) -> Self { + Uint8(v) + } +} + +impl Deserialize for Uint8 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut u8buf = [0u8; 1]; + reader.read(&mut u8buf)?; + Ok(Uint8(u8buf[0])) + } +} + +/// 32-bit signed integer, encoded in LEB128 (can be 1-5 bytes length). +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarInt32(i32); + +impl From for i32 { + fn from(v: VarInt32) -> i32 { + v.0 + } +} + +impl From for VarInt32 { + fn from(v: i32) -> VarInt32 { + VarInt32(v) + } +} + +impl Deserialize for VarInt32 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut res = 0; + let mut shift = 0; + let mut u8buf = [0u8; 1]; + loop { + if shift > 31 { return Err(Error::InvalidVarInt32); } + reader.read(&mut u8buf)?; + let b = u8buf[0]; + + res |= ((b & 0x7f) as i32).checked_shl(shift).ok_or(Error::InvalidVarInt32)?; + + shift += 7; + if (b >> 7) == 0 { + if shift < 32 && b & 0b0100_0000 == 0b0100_0000 { + res |= (1i32 << shift).wrapping_neg(); + } else if shift >= 32 && b & 0b0100_0000 == 0b0100_0000 { + if (!(b | 0b1000_0000)).leading_zeros() < 5 { + return Err(Error::InvalidVarInt32); + } + } else if shift >= 32 && b & 0b0100_0000 == 0 { + if b.leading_zeros() < 5 { + return Err(Error::InvalidVarInt32); + } + } + break; + } + } + Ok(VarInt32(res)) + } +} + +/// 64-bit signed integer, encoded in LEB128 (can be 1-9 bytes length). +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarInt64(i64); + +impl From for i64 { + fn from(v: VarInt64) -> i64 { + v.0 + } +} + +impl From for VarInt64 { + fn from(v: i64) -> VarInt64 { + VarInt64(v) + } +} + +impl Deserialize for VarInt64 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut res = 0i64; + let mut shift = 0; + let mut u8buf = [0u8; 1]; + + loop { + if shift > 63 { return Err(Error::InvalidVarInt64); } + reader.read(&mut u8buf)?; + let b = u8buf[0]; + + res |= ((b & 0x7f) as i64).checked_shl(shift).ok_or(Error::InvalidVarInt64)?; + + shift += 7; + if (b >> 7) == 0 { + if shift < 64 && b & 0b0100_0000 == 0b0100_0000 { + res |= (1i64 << shift).wrapping_neg(); + } else if shift >= 64 && b & 0b0100_0000 == 0b0100_0000 { + if (b | 0b1000_0000) as i8 != -1 { + return Err(Error::InvalidVarInt64); + } + } else if shift >= 64 && b != 0 { + return Err(Error::InvalidVarInt64); + } + break; + } + } + Ok(VarInt64(res)) + } +} + +/// 32-bit unsigned integer, encoded in little endian. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Uint32(u32); + +impl Deserialize for Uint32 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut buf = [0u8; 4]; + reader.read(&mut buf)?; + // todo check range + Ok(u32::from_le_bytes(buf).into()) + } +} + +impl From for u32 { + fn from(var: Uint32) -> u32 { + var.0 + } +} + +impl From for Uint32 { + fn from(u: u32) -> Self { Uint32(u) } +} + +/// 64-bit unsigned integer, encoded in little endian. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Uint64(u64); + +impl Deserialize for Uint64 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut buf = [0u8; 8]; + reader.read(&mut buf)?; + // todo check range + Ok(u64::from_le_bytes(buf).into()) + } +} + +impl From for Uint64 { + fn from(u: u64) -> Self { Uint64(u) } +} + +impl From for u64 { + fn from(var: Uint64) -> u64 { + var.0 + } +} + + +/// VarUint1, 1-bit value (0/1). +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct VarUint1(bool); + +impl From for bool { + fn from(v: VarUint1) -> bool { + v.0 + } +} + +impl From for VarUint1 { + fn from(b: bool) -> Self { + VarUint1(b) + } +} + +impl Deserialize for VarUint1 { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let mut u8buf = [0u8; 1]; + reader.read(&mut u8buf)?; + match u8buf[0] { + 0 => Ok(VarUint1(false)), + 1 => Ok(VarUint1(true)), + v @ _ => Err(Error::InvalidVarUint1(v)), + } + } +} + +impl Deserialize for String { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let length = u32::from(VarUint32::deserialize(reader)?) as usize; + if length > 0 { + String::from_utf8(buffered_read!(1024, length, reader)).map_err(|_| Error::NonUtf8String) + } + else { + Ok(String::new()) + } + } +} + +/// List for reading sequence of elements typed `T`, given +/// they are preceded by length (serialized as VarUint32). +#[derive(Debug, Clone)] +pub struct CountedList(Vec); + +impl CountedList { + /// Destroy counted list returing inner vector. + pub fn into_inner(self) -> Vec { self.0 } +} + +impl Deserialize for CountedList where T::Error: From { + type Error = T::Error; + + fn deserialize(reader: &mut R) -> Result { + let count: usize = VarUint32::deserialize(reader)?.into(); + let mut result = Vec::new(); + for _ in 0..count { result.push(T::deserialize(reader)?); } + Ok(CountedList(result)) + } +} + +// #[cfg(test)] +// mod tests { + +// use super::super::{deserialize_buffer, Serialize}; +// use super::{CountedList, VarInt7, VarUint32, VarInt32, VarInt64, VarUint64}; +// use crate::io::Error; + +// fn varuint32_ser_test(val: u32, expected: Vec) { +// let mut buf = Vec::new(); +// let v1: VarUint32 = val.into(); +// v1.serialize(&mut buf).expect("to be serialized ok"); +// assert_eq!(expected, buf); +// } + +// fn varuint32_de_test(dt: Vec, expected: u32) { +// let val: VarUint32 = deserialize_buffer(&dt).expect("buf to be serialized"); +// assert_eq!(expected, val.into()); +// } + +// fn varuint32_serde_test(dt: Vec, val: u32) { +// varuint32_de_test(dt.clone(), val); +// varuint32_ser_test(val, dt); +// } + +// fn varint32_ser_test(val: i32, expected: Vec) { +// let mut buf = Vec::new(); +// let v1: VarInt32 = val.into(); +// v1.serialize(&mut buf).expect("to be serialized ok"); +// assert_eq!(expected, buf); +// } + +// fn varint32_de_test(dt: Vec, expected: i32) { +// let val: VarInt32 = deserialize_buffer(&dt).expect("buf to be serialized"); +// assert_eq!(expected, val.into()); +// } + +// fn varint32_serde_test(dt: Vec, val: i32) { +// varint32_de_test(dt.clone(), val); +// varint32_ser_test(val, dt); +// } + +// fn varuint64_ser_test(val: u64, expected: Vec) { +// let mut buf = Vec::new(); +// let v1: VarUint64 = val.into(); +// v1.serialize(&mut buf).expect("to be serialized ok"); +// assert_eq!(expected, buf); +// } + +// fn varuint64_de_test(dt: Vec, expected: u64) { +// let val: VarUint64 = deserialize_buffer(&dt).expect("buf to be serialized"); +// assert_eq!(expected, val.into()); +// } + +// fn varuint64_serde_test(dt: Vec, val: u64) { +// varuint64_de_test(dt.clone(), val); +// varuint64_ser_test(val, dt); +// } + +// fn varint64_ser_test(val: i64, expected: Vec) { +// let mut buf = Vec::new(); +// let v1: VarInt64 = val.into(); +// v1.serialize(&mut buf).expect("to be serialized ok"); +// assert_eq!(expected, buf); +// } + +// fn varint64_de_test(dt: Vec, expected: i64) { +// let val: VarInt64 = deserialize_buffer(&dt).expect("buf to be serialized"); +// assert_eq!(expected, val.into()); +// } + +// fn varint64_serde_test(dt: Vec, val: i64) { +// varint64_de_test(dt.clone(), val); +// varint64_ser_test(val, dt); +// } + +// #[test] +// fn varuint32_0() { +// varuint32_serde_test(vec![0u8; 1], 0); +// } + +// #[test] +// fn varuint32_1() { +// varuint32_serde_test(vec![1u8; 1], 1); +// } + +// #[test] +// fn varuint32_135() { +// varuint32_serde_test(vec![135u8, 0x01], 135); +// } + +// #[test] +// fn varuint32_8192() { +// varuint32_serde_test(vec![0x80, 0x40], 8192); +// } + +// #[test] +// fn varint32_8192() { +// varint32_serde_test(vec![0x80, 0xc0, 0x00], 8192); +// } + +// #[test] +// fn varint32_neg_8192() { +// varint32_serde_test(vec![0x80, 0x40], -8192); +// } + +// #[test] +// fn varuint64_0() { +// varuint64_serde_test(vec![0u8; 1], 0); +// } + +// #[test] +// fn varuint64_1() { +// varuint64_serde_test(vec![1u8; 1], 1); +// } + +// #[test] +// fn varuint64_135() { +// varuint64_serde_test(vec![135u8, 0x01], 135); +// } + +// #[test] +// fn varuint64_8192() { +// varuint64_serde_test(vec![0x80, 0x40], 8192); +// } + +// #[test] +// fn varint64_8192() { +// varint64_serde_test(vec![0x80, 0xc0, 0x00], 8192); +// } + +// #[test] +// fn varint64_neg_8192() { +// varint64_serde_test(vec![0x80, 0x40], -8192); +// } + +// #[test] +// fn varint64_min() { +// varint64_serde_test( +// vec![0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f], +// -9223372036854775808, +// ); +// } + +// #[test] +// fn varint64_bad_extended() { +// let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x6f][..]); +// assert!(res.is_err()); +// } + +// #[test] +// fn varint32_bad_extended() { +// let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x6f][..]); +// assert!(res.is_err()); +// } + +// #[test] +// fn varint32_bad_extended2() { +// let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x41][..]); +// assert!(res.is_err()); +// } + +// #[test] +// fn varint64_max() { +// varint64_serde_test( +// vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00], +// 9223372036854775807, +// ); +// } + +// #[test] +// fn varint64_too_long() { +// assert!( +// deserialize_buffer::( +// &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], +// ).is_err() +// ); +// } + +// #[test] +// fn varint32_too_long() { +// assert!( +// deserialize_buffer::( +// &[0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], +// ).is_err() +// ); +// } + +// #[test] +// fn varuint64_too_long() { +// assert!( +// deserialize_buffer::( +// &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], +// ).is_err() +// ); +// } + +// #[test] +// fn varuint32_too_long() { +// assert!( +// deserialize_buffer::( +// &[0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], +// ).is_err() +// ); +// } + +// #[test] +// fn varuint32_too_long_trailing() { +// assert!( +// deserialize_buffer::( +// &[0xff, 0xff, 0xff, 0xff, 0x7f][..], +// ).is_err() +// ); +// } + +// #[test] +// fn varuint64_too_long_trailing() { +// assert!( +// deserialize_buffer::( +// &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04][..], +// ).is_err() +// ); +// } + +// #[test] +// fn varint32_min() { +// varint32_serde_test( +// vec![0x80, 0x80, 0x80, 0x80, 0x78], +// -2147483648, +// ); +// } + +// #[test] +// fn varint7_invalid() { +// match deserialize_buffer::(&[240]) { +// Err(Error::InvalidVarInt7(_)) => {}, +// _ => panic!("Should be invalid varint7 error!") +// } +// } + +// #[test] +// fn varint7_neg() { +// assert_eq!(-0x10i8, deserialize_buffer::(&[0x70]).expect("fail").into()); +// } + +// #[test] +// fn varuint32_too_long_nulled() { +// match deserialize_buffer::( +// &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x78] +// ) { +// Err(Error::InvalidVarUint32) => {}, +// _ => panic!("Should be invalid varuint32"), +// } +// } + +// #[test] +// fn varint32_max() { +// varint32_serde_test( +// vec![0xff, 0xff, 0xff, 0xff, 0x07], +// 2147483647, +// ); +// } + + +// #[test] +// fn counted_list() { +// let payload = [ +// 133u8, //(128+5), length is 5 +// 0x80, 0x80, 0x80, 0x0, // padding +// 0x01, +// 0x7d, +// 0x05, +// 0x07, +// 0x09, +// ]; + +// let list: CountedList = +// deserialize_buffer(&payload).expect("type_section be deserialized"); + +// let vars = list.into_inner(); +// assert_eq!(5, vars.len()); +// let v3: i8 = (*vars.get(1).unwrap()).into(); +// assert_eq!(-0x03i8, v3); +// } +// } diff --git a/kernel-ewasm/validator/src/serialization.rs b/kernel-ewasm/validator/src/serialization.rs new file mode 100644 index 0000000..5b83076 --- /dev/null +++ b/kernel-ewasm/validator/src/serialization.rs @@ -0,0 +1,216 @@ +use crate::{io}; +use crate::primitives::*; +pub use core::fmt; +use pwasm_std::vec::{Vec}; + +use pwasm_std::String; + +/// Deserialization from serial i/o. +pub trait Deserialize : Sized { + /// Serialization error produced by deserialization routine. + type Error: From; + /// Deserialize type from serial i/o + fn deserialize(reader: &mut R) -> Result; +} + +/// Serialization to serial i/o. Takes self by value to consume less memory +/// (parity-wasm IR is being partially freed by filling the result buffer). +pub trait Serialize { + /// Serialization error produced by serialization routine. + type Error: From; + /// Serialize type to serial i/o + fn serialize(self, writer: &mut W) -> Result<(), Self::Error>; +} + +/// Deserialization/serialization error +#[derive(Debug, Clone)] +pub enum Error { + /// Unexpected end of input. + UnexpectedEof, + /// Invalid magic. + InvalidMagic, + /// Unsupported version. + UnsupportedVersion(u32), + /// Inconsistence between declared and actual length. + InconsistentLength { + /// Expected length of the definition. + expected: usize, + /// Actual length of the definition. + actual: usize + }, + /// Other static error. + Other(&'static str), + /// Other allocated error. + HeapOther(String), + /// Invalid/unknown value type declaration. + UnknownValueType(i8), + /// Invalid/unknown table element type declaration. + UnknownTableElementType(i8), + /// Non-utf8 string. + NonUtf8String, + /// Unknown external kind code. + UnknownExternalKind(u8), + /// Unknown internal kind code. + UnknownInternalKind(u8), + /// Unknown opcode encountered. + UnknownOpcode(u8), + /// Unknown SIMD opcode encountered. + UnknownSimdOpcode(u32), + /// Invalid VarUint1 value. + InvalidVarUint1(u8), + /// Invalid VarInt32 value. + InvalidVarInt32, + /// Invalid VarInt64 value. + InvalidVarInt64, + /// Invalid VarUint32 value. + InvalidVarUint32, + /// Invalid VarUint64 value. + InvalidVarUint64, + /// Inconsistent metadata. + InconsistentMetadata, + /// Invalid section id. + InvalidSectionId(u8), + /// Sections are out of order. + SectionsOutOfOrder, + /// Duplicated sections. + DuplicatedSections(u8), + /// Invalid memory reference (should be 0). + InvalidMemoryReference(u8), + /// Invalid table reference (should be 0). + InvalidTableReference(u8), + /// Invalid value used for flags in limits type. + InvalidLimitsFlags(u8), + /// Unknown function form (should be 0x60). + UnknownFunctionForm(u8), + /// Invalid varint7 (should be in -64..63 range). + InvalidVarInt7(u8), + /// Number of function body entries and signatures does not match. + InconsistentCode, + /// Only flags 0, 1, and 2 are accepted on segments. + InvalidSegmentFlags(u32), + /// Sum of counts of locals is greater than 2^32. + TooManyLocals, + /// Duplicated name subsections. + DuplicatedNameSubsections(u8), + /// Unknown name subsection type. + UnknownNameSubsectionType(u8), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::UnexpectedEof => write!(f, "Unexpected end of input"), + Error::InvalidMagic => write!(f, "Invalid magic number at start of file"), + Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v), + Error::InconsistentLength { expected, actual } => { + write!(f, "Expected length {}, found {}", expected, actual) + } + Error::Other(msg) => write!(f, "{}", msg), + Error::HeapOther(ref msg) => write!(f, "{}", msg), + Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty), + Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty), + Error::NonUtf8String => write!(f, "Non-UTF-8 string"), + Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind), + Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind), + Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode), + Error::UnknownSimdOpcode(opcode) => write!(f, "Unknown SIMD opcode {}", opcode), + Error::InvalidVarUint1(val) => write!(f, "Not an unsigned 1-bit integer: {}", val), + Error::InvalidVarInt7(val) => write!(f, "Not a signed 7-bit integer: {}", val), + Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"), + Error::InvalidVarUint32 => write!(f, "Not an unsigned 32-bit integer"), + Error::InvalidVarInt64 => write!(f, "Not a signed 64-bit integer"), + Error::InvalidVarUint64 => write!(f, "Not an unsigned 64-bit integer"), + Error::InconsistentMetadata => write!(f, "Inconsistent metadata"), + Error::InvalidSectionId(ref id) => write!(f, "Invalid section id: {}", id), + Error::SectionsOutOfOrder => write!(f, "Sections out of order"), + Error::DuplicatedSections(ref id) => write!(f, "Duplicated sections ({})", id), + Error::InvalidMemoryReference(ref mem_ref) => write!(f, "Invalid memory reference ({})", mem_ref), + Error::InvalidTableReference(ref table_ref) => write!(f, "Invalid table reference ({})", table_ref), + Error::InvalidLimitsFlags(ref flags) => write!(f, "Invalid limits flags ({})", flags), + Error::UnknownFunctionForm(ref form) => write!(f, "Unknown function form ({})", form), + Error::InconsistentCode => write!(f, "Number of function body entries and signatures does not match"), + Error::InvalidSegmentFlags(n) => write!(f, "Invalid segment flags: {}", n), + Error::TooManyLocals => write!(f, "Too many locals"), + Error::DuplicatedNameSubsections(n) => write!(f, "Duplicated name subsections: {}", n), + Error::UnknownNameSubsectionType(n) => write!(f, "Unknown subsection type: {}", n), + } + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::UnexpectedEof => "Unexpected end of input", + Error::InvalidMagic => "Invalid magic number at start of file", + Error::UnsupportedVersion(_) => "Unsupported wasm version", + Error::InconsistentLength { .. } => "Inconsistent length", + Error::Other(msg) => msg, + Error::HeapOther(ref msg) => &msg[..], + Error::UnknownValueType(_) => "Invalid or unknown value type", + Error::UnknownTableElementType(_) => "Unknown table element type", + Error::NonUtf8String => "Non-UTF-8 string", + Error::UnknownExternalKind(_) => "Unknown external kind", + Error::UnknownInternalKind(_) => "Unknown internal kind", + Error::UnknownOpcode(_) => "Unknown opcode", + Error::UnknownSimdOpcode(_) => "Unknown SIMD opcode", + Error::InvalidVarUint1(_) => "Not an unsigned 1-bit integer", + Error::InvalidVarInt32 => "Not a signed 32-bit integer", + Error::InvalidVarInt7(_) => "Not a signed 7-bit integer", + Error::InvalidVarUint32 => "Not an unsigned 32-bit integer", + Error::InvalidVarInt64 => "Not a signed 64-bit integer", + Error::InvalidVarUint64 => "Not an unsigned 64-bit integer", + Error::InconsistentMetadata => "Inconsistent metadata", + Error::InvalidSectionId(_) => "Invalid section id", + Error::SectionsOutOfOrder => "Sections out of order", + Error::DuplicatedSections(_) => "Duplicated section", + Error::InvalidMemoryReference(_) => "Invalid memory reference", + Error::InvalidTableReference(_) => "Invalid table reference", + Error::InvalidLimitsFlags(_) => "Invalid limits flags", + Error::UnknownFunctionForm(_) => "Unknown function form", + Error::InconsistentCode => "Number of function body entries and signatures does not match", + Error::InvalidSegmentFlags(_) => "Invalid segment flags", + Error::TooManyLocals => "Too many locals", + Error::DuplicatedNameSubsections(_) => "Duplicated name subsections", + Error::UnknownNameSubsectionType(_) => "Unknown name subsections type", + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::HeapOther(format!("I/O Error: {:?}", err)) + } +} + +/// Unparsed part of the module/section. +pub struct Unparsed(pub Vec); + +impl Deserialize for Unparsed { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let len = VarUint32::deserialize(reader)?.into(); + let mut vec = vec![0u8; len]; + reader.read(&mut vec[..])?; + Ok(Unparsed(vec)) + } +} + +impl From for Vec { + fn from(u: Unparsed) -> Vec { + u.0 + } +} + +/// Deserialize deserializable type from buffer. +pub fn deserialize_buffer(contents: &[u8]) -> Result { + let mut reader = io::Cursor::new(contents); + let result = T::deserialize(&mut reader)?; + if reader.position() != contents.len() { + // It's a TrailingData, since if there is not enough data then + // UnexpectedEof must have been returned earlier in T::deserialize. + return Err(io::Error::TrailingData.into()) + } + Ok(result) +} diff --git a/kernel-ewasm/validator/src/types.rs b/kernel-ewasm/validator/src/types.rs new file mode 100644 index 0000000..08883fd --- /dev/null +++ b/kernel-ewasm/validator/src/types.rs @@ -0,0 +1,172 @@ +// use crate::rust::{fmt, vec::Vec}; +pub use core::fmt; +use pwasm_std::vec::Vec; +use crate::io; +use super::{Deserialize, VarUint7, VarInt7, CountedList, + VarUint32}; +use crate::serialization::{Error}; + +/// Type definition in types section. Currently can be only of the function type. +#[derive(Debug, Clone, PartialEq, Hash, Eq)] +pub enum Type { + /// Function type. + Function(FunctionType), +} + +impl Deserialize for Type { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + Ok(Type::Function(FunctionType::deserialize(reader)?)) + } +} + +/// Value type. +#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] +pub enum ValueType { + /// 32-bit signed integer + I32, + /// 64-bit signed integer + I64, + /// 32-bit float + F32, + /// 64-bit float + F64, + /// 128-bit SIMD register + V128, +} + +impl Deserialize for ValueType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let val = VarInt7::deserialize(reader)?; + + match val.into() { + -0x01 => Ok(ValueType::I32), + -0x02 => Ok(ValueType::I64), + -0x03 => Ok(ValueType::F32), + -0x04 => Ok(ValueType::F64), + -0x05 => Ok(ValueType::V128), + // _ => Err(Error::UnknownValueType(val.into())), + _ => panic!("unknown value type") + } + } +} + +/// Block type which is basically `ValueType` + NoResult (to define blocks that have no return type) +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum BlockType { + /// Value-type specified block type + Value(ValueType), + /// No specified block type + NoResult, +} + +impl Deserialize for BlockType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let val = VarInt7::deserialize(reader)?; + + match val.into() { + -0x01 => Ok(BlockType::Value(ValueType::I32)), + -0x02 => Ok(BlockType::Value(ValueType::I64)), + -0x03 => Ok(BlockType::Value(ValueType::F32)), + -0x04 => Ok(BlockType::Value(ValueType::F64)), + 0x7b => Ok(BlockType::Value(ValueType::V128)), + -0x40 => Ok(BlockType::NoResult), + // _ => Err(Error::UnknownValueType(val.into())), + _ => panic!("unknown value type") + } + } +} + +/// Function signature type. +#[derive(Debug, Clone, PartialEq, Hash, Eq)] +pub struct FunctionType { + form: u8, + params: Vec, + return_type: Option, +} + +impl Default for FunctionType { + fn default() -> Self { + FunctionType { + form: 0x60, + params: Vec::new(), + return_type: None, + } + } +} + +impl FunctionType { + /// New function type given the signature in-params(`params`) and return type (`return_type`) + pub fn new(params: Vec, return_type: Option) -> Self { + FunctionType { + params: params, + return_type: return_type, + ..Default::default() + } + } + /// Function form (currently only valid value is `0x60`) + pub fn form(&self) -> u8 { self.form } + /// Parameters in the function signature. + pub fn params(&self) -> &[ValueType] { &self.params } + /// Mutable parameters in the function signature. + pub fn params_mut(&mut self) -> &mut Vec { &mut self.params } + /// Return type in the function signature, if any. + pub fn return_type(&self) -> Option { self.return_type } + /// Mutable type in the function signature, if any. + pub fn return_type_mut(&mut self) -> &mut Option { &mut self.return_type } +} + +impl Deserialize for FunctionType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let form: u8 = VarUint7::deserialize(reader)?.into(); + + if form != 0x60 { + return Err(Error::UnknownFunctionForm(form)); + } + + let params: Vec = CountedList::deserialize(reader)?.into_inner(); + + let return_types: u32 = VarUint32::deserialize(reader)?.into(); + + let return_type = if return_types == 1 { + Some(ValueType::deserialize(reader)?) + } else if return_types == 0 { + None + } else { + return Err(Error::Other("Return types length should be 0 or 1")); + }; + + Ok(FunctionType { + form: form, + params: params, + return_type: return_type, + }) + } +} + +/// Table element type. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum TableElementType { + /// A reference to a function with any signature. + AnyFunc, +} + +impl Deserialize for TableElementType { + type Error = Error; + + fn deserialize(reader: &mut R) -> Result { + let val = VarInt7::deserialize(reader)?; + + match val.into() { + -0x10 => Ok(TableElementType::AnyFunc), + _ => Err(Error::UnknownTableElementType(val.into())), + } + } +}