mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
commit
74e1bc412f
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4160,6 +4160,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitvec 1.0.1",
|
||||
"bumpalo",
|
||||
"clap 3.2.20",
|
||||
"roc_wasm_module",
|
||||
]
|
||||
|
||||
|
@ -56,7 +56,8 @@ impl Env<'_> {
|
||||
/// Parse the preprocessed host binary
|
||||
/// If successful, the module can be passed to build_app_binary
|
||||
pub fn parse_host<'a>(arena: &'a Bump, host_bytes: &[u8]) -> Result<WasmModule<'a>, ParseError> {
|
||||
WasmModule::preload(arena, host_bytes)
|
||||
let require_relocatable = true;
|
||||
WasmModule::preload(arena, host_bytes, require_relocatable)
|
||||
}
|
||||
|
||||
/// Generate a Wasm module in binary form, ready to write to a file. Entry point from roc_build.
|
||||
|
@ -3,10 +3,13 @@ name = "roc_wasm_interp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bin]]
|
||||
name = "roc_wasm_interp"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
roc_wasm_module = { path = "../wasm_module" }
|
||||
|
||||
bitvec.workspace = true
|
||||
bumpalo.workspace = true
|
||||
clap.workspace = true
|
||||
|
@ -14,6 +14,8 @@ pub struct CallStack<'a> {
|
||||
return_addrs_and_block_depths: Vec<'a, (u32, u32)>,
|
||||
/// frame offsets into the `locals`, `is_float`, and `is_64` vectors (one entry per frame)
|
||||
frame_offsets: Vec<'a, u32>,
|
||||
/// base size of the value stack before executing (one entry per frame)
|
||||
value_stack_bases: Vec<'a, u32>,
|
||||
/// binary data for local variables (one entry per local)
|
||||
locals_data: Vec<'a, u64>,
|
||||
/// int/float type info (one entry per local)
|
||||
@ -37,6 +39,7 @@ impl<'a> CallStack<'a> {
|
||||
CallStack {
|
||||
return_addrs_and_block_depths: Vec::with_capacity_in(256, arena),
|
||||
frame_offsets: Vec::with_capacity_in(256, arena),
|
||||
value_stack_bases: Vec::with_capacity_in(256, arena),
|
||||
locals_data: Vec::with_capacity_in(16 * 256, arena),
|
||||
is_float: BitVec::with_capacity(256),
|
||||
is_64: BitVec::with_capacity(256),
|
||||
@ -70,6 +73,8 @@ impl<'a> CallStack<'a> {
|
||||
self.set_local_help(i, arg);
|
||||
}
|
||||
|
||||
self.value_stack_bases.push(value_stack.len() as u32);
|
||||
|
||||
// Parse local variable declarations in the function header. They're grouped by type.
|
||||
let local_group_count = u32::parse((), code_bytes, pc).unwrap();
|
||||
for _ in 0..local_group_count {
|
||||
@ -87,6 +92,7 @@ impl<'a> CallStack<'a> {
|
||||
/// On returning from a Wasm call, drop its locals and retrieve the return address
|
||||
pub fn pop_frame(&mut self) -> Option<(u32, u32)> {
|
||||
let frame_offset = self.frame_offsets.pop()? as usize;
|
||||
self.value_stack_bases.pop()?;
|
||||
self.locals_data.truncate(frame_offset);
|
||||
self.is_64.truncate(frame_offset);
|
||||
self.is_64.truncate(frame_offset);
|
||||
@ -142,6 +148,14 @@ impl<'a> CallStack<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_stack_base(&self) -> u32 {
|
||||
*self.value_stack_bases.last().unwrap_or(&0)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.is_64.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,4 +1,6 @@
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use std::fmt::Write;
|
||||
|
||||
use roc_wasm_module::opcodes::OpCode;
|
||||
use roc_wasm_module::parse::Parse;
|
||||
use roc_wasm_module::sections::{ImportDesc, MemorySection};
|
||||
@ -24,6 +26,7 @@ pub struct ExecutionState<'a> {
|
||||
pub program_counter: usize,
|
||||
block_depth: u32,
|
||||
import_signatures: Vec<'a, u32>,
|
||||
debug_string: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> ExecutionState<'a> {
|
||||
@ -40,6 +43,7 @@ impl<'a> ExecutionState<'a> {
|
||||
program_counter,
|
||||
block_depth: 0,
|
||||
import_signatures: Vec::new_in(arena),
|
||||
debug_string: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,10 +51,11 @@ impl<'a> ExecutionState<'a> {
|
||||
arena: &'a Bump,
|
||||
module: &WasmModule<'a>,
|
||||
start_fn_name: &str,
|
||||
) -> Result<Self, String> {
|
||||
is_debug_mode: bool,
|
||||
) -> Result<Self, std::string::String> {
|
||||
let mem_bytes = module.memory.min_bytes().map_err(|e| {
|
||||
format!(
|
||||
"Error parsing Memory section at offset 0x{:x}:\n{}",
|
||||
"Error parsing Memory section at offset {:#x}:\n{}",
|
||||
e.offset, e.message
|
||||
)
|
||||
})?;
|
||||
@ -67,7 +72,7 @@ impl<'a> ExecutionState<'a> {
|
||||
Vec::from_iter_in(sig_iter, arena)
|
||||
};
|
||||
|
||||
let program_counter = {
|
||||
let mut program_counter = {
|
||||
let mut export_iter = module.export.exports.iter();
|
||||
let start_fn_index = export_iter
|
||||
.find_map(|ex| {
|
||||
@ -87,27 +92,55 @@ impl<'a> ExecutionState<'a> {
|
||||
cursor
|
||||
};
|
||||
|
||||
let mut value_stack = ValueStack::new(arena);
|
||||
let mut call_stack = CallStack::new(arena);
|
||||
call_stack.push_frame(
|
||||
0, // return_addr
|
||||
0, // return_block_depth
|
||||
0, // n_args
|
||||
&mut value_stack,
|
||||
&module.code.bytes,
|
||||
&mut program_counter,
|
||||
);
|
||||
|
||||
let debug_string = if is_debug_mode {
|
||||
Some(String::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ExecutionState {
|
||||
memory: Vec::with_capacity_in(mem_bytes as usize, arena),
|
||||
call_stack: CallStack::new(arena),
|
||||
value_stack: ValueStack::new(arena),
|
||||
call_stack,
|
||||
value_stack,
|
||||
globals,
|
||||
program_counter,
|
||||
block_depth: 0,
|
||||
import_signatures,
|
||||
debug_string,
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_immediate_u32(&mut self, module: &WasmModule<'a>) -> u32 {
|
||||
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap()
|
||||
let x = u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
||||
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||
write!(debug_string, "{}", x).unwrap();
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
fn do_return(&mut self) -> Action {
|
||||
if let Some((return_addr, block_depth)) = self.call_stack.pop_frame() {
|
||||
self.program_counter = return_addr as usize;
|
||||
self.block_depth = block_depth;
|
||||
Action::Continue
|
||||
if self.call_stack.is_empty() {
|
||||
// We just popped the stack frame for the entry function. Terminate the program.
|
||||
Action::Break
|
||||
} else {
|
||||
self.program_counter = return_addr as usize;
|
||||
self.block_depth = block_depth;
|
||||
Action::Continue
|
||||
}
|
||||
} else {
|
||||
// We should never get here with real programs but maybe in tests. Terminate the program.
|
||||
Action::Break
|
||||
}
|
||||
}
|
||||
@ -115,47 +148,48 @@ impl<'a> ExecutionState<'a> {
|
||||
pub fn execute_next_instruction(&mut self, module: &WasmModule<'a>) -> Action {
|
||||
use OpCode::*;
|
||||
|
||||
let file_offset = self.program_counter as u32 + module.code.section_offset;
|
||||
let op_code = OpCode::from(module.code.bytes[self.program_counter]);
|
||||
self.program_counter += 1;
|
||||
|
||||
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||
debug_string.clear();
|
||||
write!(debug_string, "{:?} ", op_code).unwrap();
|
||||
}
|
||||
|
||||
let mut action = Action::Continue;
|
||||
|
||||
match op_code {
|
||||
UNREACHABLE => {
|
||||
unreachable!("WebAssembly tried to execute an `unreachable` instruction.");
|
||||
unreachable!(
|
||||
"WebAssembly `unreachable` instruction at file offset {:#x?}.",
|
||||
file_offset
|
||||
);
|
||||
}
|
||||
NOP => {}
|
||||
BLOCK => {
|
||||
self.block_depth += 1;
|
||||
todo!("{:?}", op_code);
|
||||
todo!("{:?} @ {:#x}", op_code, file_offset);
|
||||
}
|
||||
LOOP => {
|
||||
self.block_depth += 1;
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
IF => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
ELSE => {
|
||||
todo!("{:?}", op_code);
|
||||
todo!("{:?} @ {:#x}", op_code, file_offset);
|
||||
}
|
||||
IF => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
ELSE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
END => {
|
||||
if self.block_depth == 0 {
|
||||
// implicit RETURN at end of function
|
||||
return self.do_return();
|
||||
action = self.do_return();
|
||||
} else {
|
||||
self.block_depth -= 1;
|
||||
}
|
||||
}
|
||||
BR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
BRIF => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
BRTABLE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
BR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
BRIF => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
BRTABLE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
RETURN => {
|
||||
return self.do_return();
|
||||
action = self.do_return();
|
||||
}
|
||||
CALL => {
|
||||
let index = self.fetch_immediate_u32(module) as usize;
|
||||
@ -185,15 +219,11 @@ impl<'a> ExecutionState<'a> {
|
||||
&mut self.program_counter,
|
||||
);
|
||||
}
|
||||
CALLINDIRECT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
CALLINDIRECT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
DROP => {
|
||||
self.value_stack.pop();
|
||||
}
|
||||
SELECT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
SELECT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
GETLOCAL => {
|
||||
let index = self.fetch_immediate_u32(module);
|
||||
let value = self.call_stack.get_local(index);
|
||||
@ -217,215 +247,105 @@ impl<'a> ExecutionState<'a> {
|
||||
let index = self.fetch_immediate_u32(module);
|
||||
self.globals[index as usize] = self.value_stack.pop();
|
||||
}
|
||||
I32LOAD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32LOAD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64LOAD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LOAD8S => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LOAD8U => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LOAD16S => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LOAD16U => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD8S => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD8U => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD16S => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD16U => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD32S => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LOAD32U => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32STORE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64STORE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32STORE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64STORE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32STORE8 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32STORE16 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64STORE8 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64STORE16 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64STORE32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
CURRENTMEMORY => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
GROWMEMORY => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LOAD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32LOAD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64LOAD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LOAD8S => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LOAD8U => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LOAD16S => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LOAD16U => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD8S => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD8U => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD16S => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD16U => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD32S => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LOAD32U => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32STORE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64STORE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32STORE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64STORE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32STORE8 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32STORE16 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64STORE8 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64STORE16 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64STORE32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
CURRENTMEMORY => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
GROWMEMORY => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32CONST => {
|
||||
let value = i32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
||||
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||
write!(debug_string, "{}", value).unwrap();
|
||||
}
|
||||
self.value_stack.push(Value::I32(value));
|
||||
}
|
||||
I64CONST => {
|
||||
let value = i64::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
||||
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||
write!(debug_string, "{}", value).unwrap();
|
||||
}
|
||||
self.value_stack.push(Value::I64(value));
|
||||
}
|
||||
F32CONST => {
|
||||
let mut bytes = [0; 4];
|
||||
bytes.copy_from_slice(&module.code.bytes[self.program_counter..][..4]);
|
||||
self.value_stack.push(Value::F32(f32::from_le_bytes(bytes)));
|
||||
let value = f32::from_le_bytes(bytes);
|
||||
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||
write!(debug_string, "{}", value).unwrap();
|
||||
}
|
||||
self.value_stack.push(Value::F32(value));
|
||||
self.program_counter += 4;
|
||||
}
|
||||
F64CONST => {
|
||||
let mut bytes = [0; 8];
|
||||
bytes.copy_from_slice(&module.code.bytes[self.program_counter..][..8]);
|
||||
self.value_stack.push(Value::F64(f64::from_le_bytes(bytes)));
|
||||
let value = f64::from_le_bytes(bytes);
|
||||
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||
write!(debug_string, "{}", value).unwrap();
|
||||
}
|
||||
self.value_stack.push(Value::F64(value));
|
||||
self.program_counter += 8;
|
||||
}
|
||||
I32EQZ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32EQ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32NE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LTS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LTU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32GTS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32GTU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LES => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32LEU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32GES => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32GEU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64EQZ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64EQ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64NE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LTS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LTU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64GTS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64GTU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LES => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64LEU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64GES => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64GEU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32EQZ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32EQ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32NE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LTS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LTU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32GTS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32GTU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LES => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32LEU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32GES => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32GEU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64EQZ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64EQ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64NE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LTS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LTU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64GTS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64GTU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LES => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64LEU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64GES => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64GEU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
|
||||
F32EQ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32NE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32LT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32GT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32LE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32GE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32EQ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32NE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32LT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32GT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32LE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32GE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
|
||||
F64EQ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64NE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64LT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64GT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64LE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64GE => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64EQ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64NE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64LT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64GT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64LE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64GE => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
|
||||
I32CLZ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32CTZ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32POPCNT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32CLZ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32CTZ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32POPCNT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32ADD => {
|
||||
let x = self.value_stack.pop_i32();
|
||||
let y = self.value_stack.pop_i32();
|
||||
@ -441,259 +361,100 @@ impl<'a> ExecutionState<'a> {
|
||||
let y = self.value_stack.pop_i32();
|
||||
self.value_stack.push(Value::I32(y * x));
|
||||
}
|
||||
I32DIVS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32DIVU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32REMS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32REMU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32AND => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32OR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32XOR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32SHL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32SHRS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32SHRU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32ROTL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32ROTR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32DIVS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32DIVU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32REMS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32REMU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32AND => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32OR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32XOR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32SHL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32SHRS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32SHRU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32ROTL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32ROTR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
|
||||
I64CLZ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64CTZ => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64POPCNT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64ADD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64SUB => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64MUL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64DIVS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64DIVU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64REMS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64REMU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64AND => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64OR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64XOR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64SHL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64SHRS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64SHRU => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64ROTL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64ROTR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32ABS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32NEG => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32CEIL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32FLOOR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32TRUNC => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32NEAREST => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32SQRT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32ADD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32SUB => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32MUL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32DIV => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32MIN => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32MAX => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32COPYSIGN => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64ABS => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64NEG => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64CEIL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64FLOOR => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64TRUNC => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64NEAREST => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64SQRT => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64ADD => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64SUB => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64MUL => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64DIV => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64MIN => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64MAX => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64COPYSIGN => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64CLZ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64CTZ => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64POPCNT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64ADD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64SUB => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64MUL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64DIVS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64DIVU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64REMS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64REMU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64AND => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64OR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64XOR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64SHL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64SHRS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64SHRU => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64ROTL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64ROTR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32ABS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32NEG => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32CEIL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32FLOOR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32TRUNC => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32NEAREST => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32SQRT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32ADD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32SUB => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32MUL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32DIV => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32MIN => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32MAX => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32COPYSIGN => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64ABS => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64NEG => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64CEIL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64FLOOR => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64TRUNC => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64NEAREST => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64SQRT => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64ADD => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64SUB => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64MUL => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64DIV => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64MIN => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64MAX => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64COPYSIGN => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
|
||||
I32WRAPI64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32TRUNCSF32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32TRUNCUF32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32TRUNCSF64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32TRUNCUF64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64EXTENDSI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64EXTENDUI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64TRUNCSF32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64TRUNCUF32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64TRUNCSF64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64TRUNCUF64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32CONVERTSI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32CONVERTUI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32CONVERTSI64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32CONVERTUI64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32DEMOTEF64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64CONVERTSI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64CONVERTUI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64CONVERTSI64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64CONVERTUI64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64PROMOTEF32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32WRAPI64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32TRUNCSF32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32TRUNCUF32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32TRUNCSF64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I32TRUNCUF64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64EXTENDSI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64EXTENDUI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64TRUNCSF32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64TRUNCUF32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64TRUNCSF64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64TRUNCUF64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32CONVERTSI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32CONVERTUI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32CONVERTSI64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32CONVERTUI64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32DEMOTEF64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64CONVERTSI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64CONVERTUI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64CONVERTSI64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64CONVERTUI64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64PROMOTEF32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
|
||||
I32REINTERPRETF32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I64REINTERPRETF64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F32REINTERPRETI32 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
F64REINTERPRETI64 => {
|
||||
todo!("{:?}", op_code);
|
||||
}
|
||||
I32REINTERPRETF32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
I64REINTERPRETF64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F32REINTERPRETI32 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
F64REINTERPRETI64 => todo!("{:?} @ {:#x}", op_code, file_offset),
|
||||
}
|
||||
Action::Continue
|
||||
|
||||
if let Some(debug_string) = &self.debug_string {
|
||||
let base = self.call_stack.value_stack_base();
|
||||
let slice = self.value_stack.get_slice(base as usize);
|
||||
eprintln!("{:#07x} {:17} {:?}", file_offset, debug_string, slice);
|
||||
}
|
||||
|
||||
action
|
||||
}
|
||||
}
|
||||
|
89
crates/wasm_interp/src/main.rs
Normal file
89
crates/wasm_interp/src/main.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use bumpalo::Bump;
|
||||
use clap::ArgAction;
|
||||
use clap::{Arg, Command};
|
||||
use roc_wasm_interp::Action;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::process;
|
||||
|
||||
use roc_wasm_interp::ExecutionState;
|
||||
use roc_wasm_module::WasmModule;
|
||||
|
||||
pub const FLAG_FUNCTION: &str = "function";
|
||||
pub const FLAG_DEBUG: &str = "debug";
|
||||
pub const WASM_FILE: &str = "WASM_FILE";
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
// Define the command line arguments
|
||||
|
||||
let flag_function = Arg::new(FLAG_FUNCTION)
|
||||
.long(FLAG_FUNCTION)
|
||||
.help("Call a specific function exported from the WebAssembly module")
|
||||
.default_value("_start")
|
||||
.required(false);
|
||||
|
||||
let flag_debug = Arg::new(FLAG_DEBUG)
|
||||
.long(FLAG_DEBUG)
|
||||
.help("Print a log of every instruction executed, for debugging purposes.")
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false);
|
||||
|
||||
let wasm_file_to_run = Arg::new(WASM_FILE)
|
||||
.help("The .wasm file to run")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true);
|
||||
|
||||
let app = Command::new("roc_wasm_interp")
|
||||
.about("Run the given .wasm file")
|
||||
.arg(flag_function)
|
||||
.arg(flag_debug)
|
||||
.arg(wasm_file_to_run);
|
||||
|
||||
// Parse the command line arguments
|
||||
|
||||
let matches = app.get_matches();
|
||||
let start_fn_name = matches.get_one::<String>(FLAG_FUNCTION).unwrap();
|
||||
let is_debug_mode = matches.get_flag(FLAG_DEBUG);
|
||||
|
||||
// Load the WebAssembly binary file
|
||||
|
||||
let wasm_path = matches.get_one::<OsString>(WASM_FILE).unwrap();
|
||||
let module_bytes = fs::read(wasm_path)?;
|
||||
|
||||
// Parse the binary data
|
||||
|
||||
let arena = Bump::new();
|
||||
let require_relocatable = false;
|
||||
let module = match WasmModule::preload(&arena, &module_bytes, require_relocatable) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
eprintln!("I couldn't parse this WebAssembly module! There's something wrong at byte offset 0x{}.", e.offset);
|
||||
eprintln!("{}", e.message);
|
||||
eprintln!("If you think this could be a code generation problem in the Roc compiler, see crates/compiler/gen_wasm/README.md for debugging tips.");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialise the execution state
|
||||
|
||||
let mut state = ExecutionState::for_module(&arena, &module, start_fn_name, is_debug_mode)
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2);
|
||||
});
|
||||
|
||||
// Run
|
||||
|
||||
while let Action::Continue = state.execute_next_instruction(&module) {}
|
||||
|
||||
// Print out return value(s), if any
|
||||
|
||||
match state.value_stack.len() {
|
||||
0 => {}
|
||||
1 => println!("{:?}", state.value_stack.pop()),
|
||||
_ => println!("{:?}", &state.value_stack),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -76,8 +76,8 @@ impl<'a> ValueStack<'a> {
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> Value {
|
||||
let is_64 = self.is_64[self.is_64.len() - 1];
|
||||
let is_float = self.is_float[self.is_float.len() - 1];
|
||||
let is_64 = *self.is_64.last().unwrap();
|
||||
let is_float = *self.is_float.last().unwrap();
|
||||
let size = if is_64 { 8 } else { 4 };
|
||||
let bytes_idx = self.bytes.len() - size;
|
||||
self.get(is_64, is_float, bytes_idx)
|
||||
@ -146,6 +146,36 @@ impl<'a> ValueStack<'a> {
|
||||
_ => panic!("Expected F64 but value stack was empty"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_from_index(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
from_index: usize,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
let mut bytes_index = 0;
|
||||
assert_eq!(self.is_64.len(), self.is_float.len());
|
||||
if from_index < self.is_64.len() {
|
||||
let iter_64 = self.is_64.iter().by_vals();
|
||||
let iter_float = self.is_float.iter().by_vals();
|
||||
for (i, (is_64, is_float)) in iter_64.zip(iter_float).enumerate() {
|
||||
if i < from_index {
|
||||
continue;
|
||||
}
|
||||
let value = self.get(is_64, is_float, bytes_index);
|
||||
bytes_index += if is_64 { 8 } else { 4 };
|
||||
value.fmt(f)?;
|
||||
if i < self.is_64.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
|
||||
pub fn get_slice<'b>(&'b self, index: usize) -> ValueStackSlice<'a, 'b> {
|
||||
ValueStackSlice { stack: self, index }
|
||||
}
|
||||
}
|
||||
|
||||
fn type_from_flags(is_float: bool, is_64: bool) -> ValueType {
|
||||
@ -159,20 +189,18 @@ fn type_from_flags(is_float: bool, is_64: bool) -> ValueType {
|
||||
|
||||
impl Debug for ValueStack<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
let mut index = 0;
|
||||
assert_eq!(self.is_64.len(), self.is_float.len());
|
||||
let iter_64 = self.is_64.iter().by_vals();
|
||||
let iter_float = self.is_float.iter().by_vals();
|
||||
for (i, (is_64, is_float)) in iter_64.zip(iter_float).enumerate() {
|
||||
let value = self.get(is_64, is_float, index);
|
||||
index += if is_64 { 8 } else { 4 };
|
||||
value.fmt(f)?;
|
||||
if i < self.is_64.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
self.fmt_from_index(f, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValueStackSlice<'a, 'b> {
|
||||
stack: &'b ValueStack<'a>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl Debug for ValueStackSlice<'_, '_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.stack.fmt_from_index(f, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,11 @@ impl<'a> WasmModule<'a> {
|
||||
+ self.names.size()
|
||||
}
|
||||
|
||||
pub fn preload(arena: &'a Bump, bytes: &[u8]) -> Result<Self, ParseError> {
|
||||
pub fn preload(
|
||||
arena: &'a Bump,
|
||||
bytes: &[u8],
|
||||
require_relocatable: bool,
|
||||
) -> Result<Self, ParseError> {
|
||||
let is_valid_magic_number = &bytes[0..4] == "\0asm".as_bytes();
|
||||
let is_valid_version = bytes[4..8] == Self::WASM_VERSION.to_le_bytes();
|
||||
if !is_valid_magic_number || !is_valid_version {
|
||||
@ -155,27 +159,36 @@ impl<'a> WasmModule<'a> {
|
||||
if code.bytes.is_empty() {
|
||||
module_errors.push_str("Missing Code section\n");
|
||||
}
|
||||
if linking.symbol_table.is_empty() {
|
||||
module_errors.push_str("Missing \"linking\" Custom section\n");
|
||||
}
|
||||
if reloc_code.entries.is_empty() {
|
||||
module_errors.push_str("Missing \"reloc.CODE\" Custom section\n");
|
||||
}
|
||||
if global.count != 0 {
|
||||
let global_err_msg =
|
||||
|
||||
if require_relocatable {
|
||||
if linking.symbol_table.is_empty() {
|
||||
module_errors.push_str("Missing \"linking\" Custom section\n");
|
||||
}
|
||||
if reloc_code.entries.is_empty() {
|
||||
module_errors.push_str("Missing \"reloc.CODE\" Custom section\n");
|
||||
}
|
||||
if global.count != 0 {
|
||||
let global_err_msg =
|
||||
format!("All globals in a relocatable Wasm module should be imported, but found {} internally defined", global.count);
|
||||
module_errors.push_str(&global_err_msg);
|
||||
module_errors.push_str(&global_err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
if !module_errors.is_empty() {
|
||||
return Err(ParseError {
|
||||
offset: 0,
|
||||
message: format!("{}\n{}\n{}",
|
||||
let message = if require_relocatable {
|
||||
format!(
|
||||
"{}\n{}\n{}",
|
||||
"The host file has the wrong structure. I need a relocatable WebAssembly binary file.",
|
||||
"If you're using wasm-ld, try the --relocatable option.",
|
||||
module_errors,
|
||||
)
|
||||
});
|
||||
} else {
|
||||
format!(
|
||||
"I wasn't able to understand this WebAssembly file.\n{}",
|
||||
module_errors,
|
||||
)
|
||||
};
|
||||
return Err(ParseError { offset: 0, message });
|
||||
}
|
||||
|
||||
Ok(WasmModule {
|
||||
|
@ -214,7 +214,7 @@ impl<'a> Parse<RelocCtx<'a>> for RelocationSection<'a> {
|
||||
fn parse(ctx: RelocCtx<'a>, bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
|
||||
let (arena, name) = ctx;
|
||||
|
||||
if *cursor > bytes.len() || bytes[*cursor] != SectionId::Custom as u8 {
|
||||
if *cursor >= bytes.len() || bytes[*cursor] != SectionId::Custom as u8 {
|
||||
// The section we're looking for is missing, which is the same as being empty.
|
||||
return Ok(RelocationSection::new(arena, name));
|
||||
}
|
||||
@ -626,7 +626,7 @@ impl<'a> LinkingSection<'a> {
|
||||
|
||||
impl<'a> Parse<&'a Bump> for LinkingSection<'a> {
|
||||
fn parse(arena: &'a Bump, bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
|
||||
if *cursor > bytes.len() || bytes[*cursor] != SectionId::Custom as u8 {
|
||||
if *cursor >= bytes.len() || bytes[*cursor] != SectionId::Custom as u8 {
|
||||
return Ok(LinkingSection::new(arena));
|
||||
}
|
||||
*cursor += 1;
|
||||
|
@ -739,6 +739,9 @@ impl SkipBytes for Limits {
|
||||
|
||||
impl Parse<()> for Limits {
|
||||
fn parse(_: (), bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
|
||||
if *cursor >= bytes.len() {
|
||||
return Ok(Limits::Min(0));
|
||||
}
|
||||
let variant_id = bytes[*cursor];
|
||||
*cursor += 1;
|
||||
|
||||
@ -1299,6 +1302,7 @@ impl<'a> Serialize for ElementSection<'a> {
|
||||
#[derive(Debug)]
|
||||
pub struct CodeSection<'a> {
|
||||
pub function_count: u32,
|
||||
pub section_offset: u32,
|
||||
pub bytes: Vec<'a, u8>,
|
||||
/// The start of each function
|
||||
pub function_offsets: Vec<'a, u32>,
|
||||
@ -1310,6 +1314,7 @@ impl<'a> CodeSection<'a> {
|
||||
pub fn new(arena: &'a Bump) -> Self {
|
||||
CodeSection {
|
||||
function_count: 0,
|
||||
section_offset: 0,
|
||||
bytes: Vec::new_in(arena),
|
||||
function_offsets: Vec::new_in(arena),
|
||||
dead_import_dummy_count: 0,
|
||||
@ -1357,6 +1362,7 @@ impl<'a> CodeSection<'a> {
|
||||
|
||||
Ok(CodeSection {
|
||||
function_count,
|
||||
section_offset: section_body_start as u32,
|
||||
bytes,
|
||||
function_offsets,
|
||||
dead_import_dummy_count: 0,
|
||||
@ -1488,6 +1494,13 @@ impl<'a> DataSection<'a> {
|
||||
|
||||
impl<'a> Parse<&'a Bump> for DataSection<'a> {
|
||||
fn parse(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
|
||||
if *cursor >= module_bytes.len() {
|
||||
return Ok(DataSection {
|
||||
end_addr: 0,
|
||||
count: 0,
|
||||
bytes: Vec::<u8>::new_in(arena),
|
||||
});
|
||||
}
|
||||
let (count, range) = parse_section(Self::ID, module_bytes, cursor)?;
|
||||
|
||||
let end = range.end;
|
||||
|
Loading…
Reference in New Issue
Block a user