Merge branch 'master' into no_std
This commit is contained in:
commit
03d427b652
|
@ -11,15 +11,13 @@ addons:
|
|||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-6
|
||||
- g++-6
|
||||
- gcc-8
|
||||
- g++-8
|
||||
- cmake
|
||||
env:
|
||||
- CC=/usr/bin/gcc-6 CXX=/usr/bin/g++-6
|
||||
- CC=/usr/bin/gcc-8 CXX=/usr/bin/g++-8
|
||||
|
||||
install:
|
||||
# Install `cargo-deadlinks` unless it is currently installed.
|
||||
- command -v cargo-deadlinks &> /dev/null || cargo install cargo-deadlinks
|
||||
- if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then rustup target add wasm32-unknown-unknown; fi
|
||||
script:
|
||||
# Make sure nightly targets are not broken.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "wasmi"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Svyatoslav Nikolsky <svyatonik@yandex.ru>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
|
@ -28,4 +28,4 @@ libm = { version = "0.1.2", optional = true }
|
|||
[dev-dependencies]
|
||||
assert_matches = "1.1"
|
||||
rand = "0.4.2"
|
||||
wabt = "0.4"
|
||||
wabt = "0.6"
|
||||
|
|
|
@ -6,7 +6,7 @@ authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
|||
[dependencies]
|
||||
wasmi = { path = ".." }
|
||||
assert_matches = "1.2"
|
||||
wabt = "0.3"
|
||||
wabt = "0.6"
|
||||
|
||||
[profile.bench]
|
||||
debug = true
|
||||
|
|
|
@ -10,7 +10,7 @@ cargo-fuzz = true
|
|||
|
||||
[dependencies]
|
||||
wasmi = { path = ".." }
|
||||
wabt = "0.2.0"
|
||||
wabt = "0.6.0"
|
||||
wasmparser = "0.14.1"
|
||||
tempdir = "0.3.6"
|
||||
|
||||
|
|
|
@ -7,4 +7,4 @@ authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
|||
honggfuzz = "=0.5.9" # Strict equal since hfuzz requires dep and cmd versions to match.
|
||||
wasmi = { path = ".." }
|
||||
tempdir = "0.3.6"
|
||||
wabt = "0.2.0"
|
||||
wabt = "0.6.0"
|
||||
|
|
80
src/isa.rs
80
src/isa.rs
|
@ -96,6 +96,22 @@ pub struct Target {
|
|||
pub drop_keep: DropKeep,
|
||||
}
|
||||
|
||||
/// A relocation entry that specifies.
|
||||
#[derive(Debug)]
|
||||
pub enum Reloc {
|
||||
/// Patch the destination of the branch instruction (br, br_eqz, br_nez)
|
||||
/// at the specified pc.
|
||||
Br {
|
||||
pc: u32,
|
||||
},
|
||||
/// Patch the specified destination index inside of br_table instruction at
|
||||
/// the specified pc.
|
||||
BrTable {
|
||||
pc: u32,
|
||||
idx: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Instruction {
|
||||
/// Push a local variable or an argument from the specified depth.
|
||||
|
@ -302,5 +318,67 @@ pub enum Instruction {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Instructions {
|
||||
pub code: Vec<Instruction>,
|
||||
vec: Vec<Instruction>,
|
||||
}
|
||||
|
||||
impl Instructions {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Instructions {
|
||||
vec: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_pc(&self) -> u32 {
|
||||
self.vec.len() as u32
|
||||
}
|
||||
|
||||
pub fn push(&mut self, instruction: Instruction) {
|
||||
self.vec.push(instruction);
|
||||
}
|
||||
|
||||
pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) {
|
||||
match reloc {
|
||||
Reloc::Br { pc } => match self.vec[pc as usize] {
|
||||
Instruction::Br(ref mut target)
|
||||
| Instruction::BrIfEqz(ref mut target)
|
||||
| Instruction::BrIfNez(ref mut target) => target.dst_pc = dst_pc,
|
||||
_ => panic!("branch relocation points to a non-branch instruction"),
|
||||
},
|
||||
Reloc::BrTable { pc, idx } => match self.vec[pc as usize] {
|
||||
Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc,
|
||||
_ => panic!("brtable relocation points to not brtable instruction"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate_from(&self, position: u32) -> InstructionIter {
|
||||
InstructionIter{
|
||||
instructions: &self.vec,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstructionIter<'a> {
|
||||
instructions: &'a [Instruction],
|
||||
position: u32,
|
||||
}
|
||||
|
||||
impl<'a> InstructionIter<'a> {
|
||||
#[inline]
|
||||
pub fn position(&self) -> u32 {
|
||||
self.position
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for InstructionIter<'a> {
|
||||
type Item = &'a Instruction;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
||||
self.instructions.get(self.position as usize).map(|instruction| {
|
||||
self.position += 1;
|
||||
instruction
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
42
src/lib.rs
42
src/lib.rs
|
@ -1,48 +1,48 @@
|
|||
//! # wasmi
|
||||
//!
|
||||
//! This library allows to load WebAssembly modules in binary format and invoke functions on them.
|
||||
//! This library allows to load WebAssembly modules in binary format and invoke their functions.
|
||||
//!
|
||||
//! # Introduction
|
||||
//!
|
||||
//! WebAssembly (wasm) is a safe, portable, compact format that designed for efficient execution.
|
||||
//! WebAssembly (wasm) is a safe, portable and compact format that designed for efficient execution.
|
||||
//!
|
||||
//! Wasm code is distributed in a form of modules, that contains definitions of:
|
||||
//! Wasm code is distributed in the form of modules that contains definitions of:
|
||||
//!
|
||||
//! - functions,
|
||||
//! - global variables,
|
||||
//! - linear memories,
|
||||
//! - linear memory instances and
|
||||
//! - tables.
|
||||
//!
|
||||
//! and this definitions can be imported. Also, each definition can be exported.
|
||||
//! Each of these definitions can be imported and exported.
|
||||
//!
|
||||
//! In addition to definitions, modules can define initialization data for their memories or tables that takes the
|
||||
//! form of segments copied to given offsets. They can also define a `start` function that is automatically executed.
|
||||
//! In addition to these definitions, modules can define initialization data for their memory or tables. This initialization data can take the
|
||||
//! form of segments, copied to given offsets. They can also define a `start` function that is automatically executed when the module is loaded.
|
||||
//!
|
||||
//! ## Loading and Validation
|
||||
//!
|
||||
//! Before execution a module should be validated. This process checks that module is well-formed
|
||||
//! Before execution, a module must be validated. This process checks that module is well-formed
|
||||
//! and makes only allowed operations.
|
||||
//!
|
||||
//! Valid modules can't access memory out of its sandbox, can't cause stack underflow
|
||||
//! and can call functions only with correct signatures.
|
||||
//! A valid module can't access memory out of its sandbox, can't cause stack underflows
|
||||
//! and can only call functions with correct signatures.
|
||||
//!
|
||||
//! ## Instantiation
|
||||
//!
|
||||
//! In order to execute code in wasm module it should be instatiated.
|
||||
//! In order to execute code from a wasm module, it must be instatiated.
|
||||
//! Instantiation includes the following steps:
|
||||
//!
|
||||
//! 1. Create an empty module instance,
|
||||
//! 2. Resolve definition instances for each declared import in the module,
|
||||
//! 3. Instantiate definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc),
|
||||
//! 4. Initialize memory and table contents by copiying segments into them,
|
||||
//! 5. Execute `start` function, if any.
|
||||
//! 1. Creating an empty module instance.
|
||||
//! 2. Resolving the definition instances for each declared import in the module.
|
||||
//! 3. Instantiating definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc.).
|
||||
//! 4. Initializing memory and table contents by copying segments into them.
|
||||
//! 5. Executing the `start` function, if any.
|
||||
//!
|
||||
//! After these steps, module instance are ready to execute functions.
|
||||
//! After these steps, the module instance is ready to execute functions.
|
||||
//!
|
||||
//! ## Execution
|
||||
//!
|
||||
//! It is allowed to only execute functions which are exported by a module.
|
||||
//! Functions can either return a result or trap (e.g. there can't be linking-error at the middle of execution).
|
||||
//! It only is allowed to call functions which are exported by the module.
|
||||
//! Functions can either return a result or trap (e.g. there can't be linking error in the middle of the function execution).
|
||||
//! This property is ensured by the validation process.
|
||||
//!
|
||||
//! # Examples
|
||||
|
@ -72,7 +72,7 @@
|
|||
//! .expect("failed to load wasm");
|
||||
//!
|
||||
//! // Instantiate a module with empty imports and
|
||||
//! // asserting that there is no `start` function.
|
||||
//! // assert that there is no `start` function.
|
||||
//! let instance =
|
||||
//! ModuleInstance::new(
|
||||
//! &module,
|
||||
|
@ -81,7 +81,7 @@
|
|||
//! .expect("failed to instantiate wasm module")
|
||||
//! .assert_no_start();
|
||||
//!
|
||||
//! // Finally, invoke exported function "test" with no parameters
|
||||
//! // Finally, invoke the exported function "test" with no parameters
|
||||
//! // and empty external function executor.
|
||||
//! assert_eq!(
|
||||
//! instance.invoke_export(
|
||||
|
|
|
@ -318,7 +318,7 @@ impl MemoryInstance {
|
|||
|
||||
unsafe { ::core::ptr::copy(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_ptr() as *mut _,
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)}
|
||||
|
||||
|
@ -347,13 +347,36 @@ impl MemoryInstance {
|
|||
|
||||
unsafe { ::core::ptr::copy_nonoverlapping(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_ptr() as *mut _,
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copy memory between two (possibly distinct) memory instances.
|
||||
///
|
||||
/// If the same memory instance passed as `src` and `dst` then usual `copy` will be used.
|
||||
pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
if Rc::ptr_eq(&src.0, &dst.0) {
|
||||
// `transfer` is invoked with with same source and destination. Let's assume that regions may
|
||||
// overlap and use `copy`.
|
||||
return src.copy(src_offset, dst_offset, len);
|
||||
}
|
||||
|
||||
// Because memory references point to different memory instances, it is safe to `borrow_mut`
|
||||
// both buffers at once (modulo `with_direct_access_mut`).
|
||||
let mut src_buffer = src.buffer.borrow_mut();
|
||||
let mut dst_buffer = dst.buffer.borrow_mut();
|
||||
|
||||
let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range();
|
||||
let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range();
|
||||
|
||||
dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fill the memory region with the specified value.
|
||||
///
|
||||
/// Semantically equivalent to `memset`.
|
||||
|
@ -432,7 +455,8 @@ pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), Str
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use std::rc::Rc;
|
||||
use Error;
|
||||
use memory_units::Pages;
|
||||
|
||||
|
@ -535,6 +559,47 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_works() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||
|
||||
MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap();
|
||||
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_still_works_with_same_memory() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
|
||||
MemoryInstance::transfer(&src, 4, &src, 0, 3).unwrap();
|
||||
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[4, 5, 6, 3, 4, 5, 6, 7, 8, 9]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_oob_with_same_memory_errors() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
assert!(MemoryInstance::transfer(&src, 65535, &src, 0, 3).is_err());
|
||||
|
||||
// Check that memories content left untouched
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_oob_errors() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||
|
||||
assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err());
|
||||
|
||||
// Check that memories content left untouched
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear() {
|
||||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
|
|
|
@ -177,7 +177,7 @@ impl Interpreter {
|
|||
let function_return =
|
||||
self.do_run_function(
|
||||
&mut function_context,
|
||||
&function_body.code.code,
|
||||
&function_body.code,
|
||||
).map_err(Trap::new)?;
|
||||
|
||||
match function_return {
|
||||
|
@ -231,18 +231,21 @@ impl Interpreter {
|
|||
}
|
||||
}
|
||||
|
||||
fn do_run_function(&mut self, function_context: &mut FunctionContext, instructions: &[isa::Instruction]) -> Result<RunResult, TrapKind> {
|
||||
fn do_run_function(&mut self, function_context: &mut FunctionContext, instructions: &isa::Instructions)
|
||||
-> Result<RunResult, TrapKind>
|
||||
{
|
||||
let mut iter = instructions.iterate_from(function_context.position);
|
||||
loop {
|
||||
let instruction = &instructions[function_context.position];
|
||||
let instruction = iter.next().expect("instruction");
|
||||
|
||||
match self.run_instruction(function_context, instruction)? {
|
||||
InstructionOutcome::RunNextInstruction => function_context.position += 1,
|
||||
InstructionOutcome::RunNextInstruction => {},
|
||||
InstructionOutcome::Branch(target) => {
|
||||
function_context.position = target.dst_pc as usize;
|
||||
iter = instructions.iterate_from(target.dst_pc);
|
||||
self.value_stack.drop_keep(target.drop_keep);
|
||||
},
|
||||
InstructionOutcome::ExecuteCall(func_ref) => {
|
||||
function_context.position += 1;
|
||||
function_context.position = iter.position();
|
||||
return Ok(RunResult::NestedCall(func_ref));
|
||||
},
|
||||
InstructionOutcome::Return(drop_keep) => {
|
||||
|
@ -1079,7 +1082,7 @@ struct FunctionContext {
|
|||
pub module: ModuleRef,
|
||||
pub memory: Option<MemoryRef>,
|
||||
/// Current instruction position.
|
||||
pub position: usize,
|
||||
pub position: u32,
|
||||
}
|
||||
|
||||
impl FunctionContext {
|
||||
|
|
|
@ -1408,9 +1408,7 @@ impl<'a> FunctionValidationContext<'a> {
|
|||
}
|
||||
|
||||
fn into_code(self) -> isa::Instructions {
|
||||
isa::Instructions {
|
||||
code: self.sink.into_inner(),
|
||||
}
|
||||
self.sink.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1630,22 +1628,6 @@ struct Target {
|
|||
drop_keep: isa::DropKeep,
|
||||
}
|
||||
|
||||
/// A relocation entry that specifies.
|
||||
#[derive(Debug)]
|
||||
enum Reloc {
|
||||
/// Patch the destination of the branch instruction (br, br_eqz, br_nez)
|
||||
/// at the specified pc.
|
||||
Br {
|
||||
pc: u32,
|
||||
},
|
||||
/// Patch the specified destination index inside of br_table instruction at
|
||||
/// the specified pc.
|
||||
BrTable {
|
||||
pc: u32,
|
||||
idx: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Identifier of a label.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
struct LabelId(usize);
|
||||
|
@ -1657,23 +1639,23 @@ enum Label {
|
|||
}
|
||||
|
||||
struct Sink {
|
||||
ins: Vec<isa::Instruction>,
|
||||
labels: Vec<(Label, Vec<Reloc>)>,
|
||||
ins: isa::Instructions,
|
||||
labels: Vec<(Label, Vec<isa::Reloc>)>,
|
||||
}
|
||||
|
||||
impl Sink {
|
||||
fn with_instruction_capacity(capacity: usize) -> Sink {
|
||||
Sink {
|
||||
ins: Vec::with_capacity(capacity),
|
||||
ins: isa::Instructions::with_capacity(capacity),
|
||||
labels: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cur_pc(&self) -> u32 {
|
||||
self.ins.len() as u32
|
||||
self.ins.current_pc()
|
||||
}
|
||||
|
||||
fn pc_or_placeholder<F: FnOnce() -> Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 {
|
||||
fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 {
|
||||
match self.labels[label.0] {
|
||||
(Label::Resolved(dst_pc), _) => dst_pc,
|
||||
(Label::NotResolved, ref mut unresolved) => {
|
||||
|
@ -1694,7 +1676,7 @@ impl Sink {
|
|||
drop_keep,
|
||||
} = target;
|
||||
let pc = self.cur_pc();
|
||||
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
|
||||
self.ins.push(isa::Instruction::Br(isa::Target {
|
||||
dst_pc,
|
||||
drop_keep: drop_keep.into(),
|
||||
|
@ -1707,7 +1689,7 @@ impl Sink {
|
|||
drop_keep,
|
||||
} = target;
|
||||
let pc = self.cur_pc();
|
||||
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
|
||||
self.ins.push(isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc,
|
||||
drop_keep: drop_keep.into(),
|
||||
|
@ -1720,7 +1702,7 @@ impl Sink {
|
|||
drop_keep,
|
||||
} = target;
|
||||
let pc = self.cur_pc();
|
||||
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
|
||||
self.ins.push(isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc,
|
||||
drop_keep: drop_keep.into(),
|
||||
|
@ -1733,7 +1715,7 @@ impl Sink {
|
|||
let pc = self.cur_pc();
|
||||
let mut isa_targets = Vec::new();
|
||||
for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() {
|
||||
let dst_pc = self.pc_or_placeholder(label, || Reloc::BrTable { pc, idx });
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx });
|
||||
isa_targets.push(
|
||||
isa::Target {
|
||||
dst_pc,
|
||||
|
@ -1770,26 +1752,15 @@ impl Sink {
|
|||
// particular label.
|
||||
let unresolved_rels = mem::replace(&mut self.labels[label.0].1, Vec::new());
|
||||
for reloc in unresolved_rels {
|
||||
match reloc {
|
||||
Reloc::Br { pc } => match self.ins[pc as usize] {
|
||||
isa::Instruction::Br(ref mut target)
|
||||
| isa::Instruction::BrIfEqz(ref mut target)
|
||||
| isa::Instruction::BrIfNez(ref mut target) => target.dst_pc = dst_pc,
|
||||
_ => panic!("branch relocation points to a non-branch instruction"),
|
||||
},
|
||||
Reloc::BrTable { pc, idx } => match self.ins[pc as usize] {
|
||||
isa::Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc,
|
||||
_ => panic!("brtable relocation points to not brtable instruction"),
|
||||
}
|
||||
}
|
||||
self.ins.patch_relocation(reloc, dst_pc);
|
||||
}
|
||||
|
||||
// Mark this label as resolved.
|
||||
self.labels[label.0] = (Label::Resolved(dst_pc), Vec::new());
|
||||
}
|
||||
|
||||
/// Consume this Sink and returns isa::Instruction.
|
||||
fn into_inner(self) -> Vec<isa::Instruction> {
|
||||
/// Consume this Sink and returns isa::Instructions.
|
||||
fn into_inner(self) -> isa::Instructions {
|
||||
// At this moment all labels should be resolved.
|
||||
assert!({
|
||||
self.labels.iter().all(|(state, unresolved)|
|
||||
|
|
|
@ -310,15 +310,29 @@ fn validate(wat: &str) -> ValidatedModule {
|
|||
validated_module
|
||||
}
|
||||
|
||||
fn compile(wat: &str) -> Vec<isa::Instruction> {
|
||||
fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||
let validated_module = validate(wat);
|
||||
let code = &validated_module.code_map[0];
|
||||
code.code.clone()
|
||||
|
||||
let mut instructions = Vec::new();
|
||||
let mut pcs = Vec::new();
|
||||
let mut iter = code.iterate_from(0);
|
||||
loop {
|
||||
let pc = iter.position();
|
||||
if let Some(instruction) = iter.next() {
|
||||
instructions.push(instruction.clone());
|
||||
pcs.push(pc);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
(instructions, pcs)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_no_value() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
)
|
||||
|
@ -337,7 +351,7 @@ fn implicit_return_no_value() {
|
|||
|
||||
#[test]
|
||||
fn implicit_return_with_value() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (result i32)
|
||||
i32.const 0
|
||||
|
@ -358,7 +372,7 @@ fn implicit_return_with_value() {
|
|||
|
||||
#[test]
|
||||
fn implicit_return_param() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
)
|
||||
|
@ -377,7 +391,7 @@ fn implicit_return_param() {
|
|||
|
||||
#[test]
|
||||
fn get_local() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
|
@ -398,7 +412,7 @@ fn get_local() {
|
|||
|
||||
#[test]
|
||||
fn explicit_return() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
|
@ -424,7 +438,7 @@ fn explicit_return() {
|
|||
|
||||
#[test]
|
||||
fn add_params() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (param i32) (result i32)
|
||||
get_local 0
|
||||
|
@ -454,7 +468,7 @@ fn add_params() {
|
|||
|
||||
#[test]
|
||||
fn drop_locals() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
(local i32)
|
||||
|
@ -478,7 +492,7 @@ fn drop_locals() {
|
|||
|
||||
#[test]
|
||||
fn if_without_else() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
i32.const 1
|
||||
|
@ -495,7 +509,7 @@ fn if_without_else() {
|
|||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: 4,
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -517,7 +531,7 @@ fn if_without_else() {
|
|||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
(local i32)
|
||||
|
@ -537,7 +551,7 @@ fn if_else() {
|
|||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: 5,
|
||||
dst_pc: pcs[5],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -546,7 +560,7 @@ fn if_else() {
|
|||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: 7,
|
||||
dst_pc: pcs[7],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -564,7 +578,7 @@ fn if_else() {
|
|||
|
||||
#[test]
|
||||
fn if_else_returns_result() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -582,7 +596,7 @@ fn if_else_returns_result() {
|
|||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: 4,
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -590,7 +604,7 @@ fn if_else_returns_result() {
|
|||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: 5,
|
||||
dst_pc: pcs[5],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -608,7 +622,7 @@ fn if_else_returns_result() {
|
|||
|
||||
#[test]
|
||||
fn if_else_branch_from_true_branch() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -630,7 +644,7 @@ fn if_else_branch_from_true_branch() {
|
|||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: 8,
|
||||
dst_pc: pcs[8],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -639,7 +653,7 @@ fn if_else_branch_from_true_branch() {
|
|||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: 9,
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
|
@ -648,7 +662,7 @@ fn if_else_branch_from_true_branch() {
|
|||
isa::Instruction::Drop,
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: 9,
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -666,7 +680,7 @@ fn if_else_branch_from_true_branch() {
|
|||
|
||||
#[test]
|
||||
fn if_else_branch_from_false_branch() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -688,7 +702,7 @@ fn if_else_branch_from_false_branch() {
|
|||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: 4,
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -696,7 +710,7 @@ fn if_else_branch_from_false_branch() {
|
|||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: 9,
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -705,7 +719,7 @@ fn if_else_branch_from_false_branch() {
|
|||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: 9,
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
|
@ -724,7 +738,7 @@ fn if_else_branch_from_false_branch() {
|
|||
|
||||
#[test]
|
||||
fn loop_() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop (result i32)
|
||||
|
@ -759,7 +773,7 @@ fn loop_() {
|
|||
|
||||
#[test]
|
||||
fn loop_empty() {
|
||||
let code = compile(r#"
|
||||
let (code, _) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop
|
||||
|
@ -780,7 +794,7 @@ fn loop_empty() {
|
|||
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1
|
||||
|
@ -806,7 +820,7 @@ fn brtable() {
|
|||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: 2,
|
||||
dst_pc: pcs[2],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -824,7 +838,7 @@ fn brtable() {
|
|||
|
||||
#[test]
|
||||
fn brtable_returns_result() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1 (result i32)
|
||||
|
@ -847,14 +861,14 @@ fn brtable_returns_result() {
|
|||
isa::Instruction::BrTable(
|
||||
vec![
|
||||
isa::Target {
|
||||
dst_pc: 3,
|
||||
dst_pc: pcs[3],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: 4,
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
keep: isa::Keep::Single,
|
||||
drop: 0,
|
||||
|
@ -874,7 +888,7 @@ fn brtable_returns_result() {
|
|||
|
||||
#[test]
|
||||
fn wabt_example() {
|
||||
let code = compile(r#"
|
||||
let (code, pcs) = compile(r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
block $exit
|
||||
|
@ -893,7 +907,7 @@ fn wabt_example() {
|
|||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: 4,
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use nan_preserving_float::{F32, F64};
|
||||
use core::mem::transmute;
|
||||
use core::{f32, i32, i64, u32, u64};
|
||||
use TrapKind;
|
||||
|
||||
|
@ -546,11 +545,11 @@ impl TransmuteInto<f64> for i64 {
|
|||
}
|
||||
|
||||
impl TransmuteInto<i32> for u32 {
|
||||
fn transmute_into(self) -> i32 { unsafe { transmute(self) } }
|
||||
fn transmute_into(self) -> i32 { self as _ }
|
||||
}
|
||||
|
||||
impl TransmuteInto<i64> for u64 {
|
||||
fn transmute_into(self) -> i64 { unsafe { transmute(self) } }
|
||||
fn transmute_into(self) -> i64 { self as _ }
|
||||
}
|
||||
|
||||
impl LittleEndianConvert for i8 {
|
||||
|
|
Loading…
Reference in New Issue