mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-14 07:29:02 +03:00
wasm_interp: Handle calls to imports
This commit is contained in:
parent
b10ac827f1
commit
c866ce6b09
@ -10,6 +10,7 @@ use roc_wasm_module::{Value, ValueType};
|
||||
|
||||
use crate::call_stack::CallStack;
|
||||
use crate::value_stack::ValueStack;
|
||||
use crate::ImportDispatcher;
|
||||
|
||||
pub enum Action {
|
||||
Continue,
|
||||
@ -17,7 +18,7 @@ pub enum Action {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Instance<'a> {
|
||||
pub struct Instance<'a, I: ImportDispatcher> {
|
||||
/// Contents of the WebAssembly instance's memory
|
||||
pub memory: Vec<'a, u8>,
|
||||
/// Metadata for every currently-active function call
|
||||
@ -34,12 +35,22 @@ pub struct Instance<'a> {
|
||||
outermost_block: u32,
|
||||
/// Signature indices (in the TypeSection) of all imported (non-WebAssembly) functions
|
||||
import_signatures: Vec<'a, u32>,
|
||||
/// Import dispatcher from user code
|
||||
import_dispatcher: I,
|
||||
/// Temporary storage for import arguments
|
||||
import_arguments: Vec<'a, Value>,
|
||||
/// temporary storage for output using the --debug option
|
||||
debug_string: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> Instance<'a> {
|
||||
pub fn new<G>(arena: &'a Bump, memory_pages: u32, program_counter: usize, globals: G) -> Self
|
||||
impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||
pub fn new<G>(
|
||||
arena: &'a Bump,
|
||||
memory_pages: u32,
|
||||
program_counter: usize,
|
||||
globals: G,
|
||||
import_dispatcher: I,
|
||||
) -> Self
|
||||
where
|
||||
G: IntoIterator<Item = Value>,
|
||||
{
|
||||
@ -53,6 +64,8 @@ impl<'a> Instance<'a> {
|
||||
block_loop_addrs: Vec::new_in(arena),
|
||||
outermost_block: 0,
|
||||
import_signatures: Vec::new_in(arena),
|
||||
import_dispatcher,
|
||||
import_arguments: Vec::new_in(arena),
|
||||
debug_string: Some(String::new()),
|
||||
}
|
||||
}
|
||||
@ -60,6 +73,7 @@ impl<'a> Instance<'a> {
|
||||
pub fn for_module(
|
||||
arena: &'a Bump,
|
||||
module: &WasmModule<'a>,
|
||||
import_dispatcher: I,
|
||||
is_debug_mode: bool,
|
||||
) -> Result<Self, std::string::String> {
|
||||
let mem_bytes = module.memory.min_bytes().map_err(|e| {
|
||||
@ -101,6 +115,8 @@ impl<'a> Instance<'a> {
|
||||
block_loop_addrs: Vec::new_in(arena),
|
||||
outermost_block: 0,
|
||||
import_signatures,
|
||||
import_dispatcher,
|
||||
import_arguments: Vec::new_in(arena),
|
||||
debug_string,
|
||||
})
|
||||
}
|
||||
@ -336,22 +352,51 @@ impl<'a> Instance<'a> {
|
||||
|
||||
let arg_type_bytes = module.types.look_up_arg_type_bytes(signature_index);
|
||||
|
||||
let return_addr = self.program_counter as u32;
|
||||
self.program_counter = module.code.function_offsets[fn_index] as usize;
|
||||
if fn_index < n_imports {
|
||||
// Call an imported non-Wasm function
|
||||
let mut import_fns = module.import.imports.iter().filter(|imp| imp.is_function());
|
||||
let import = import_fns
|
||||
.nth(fn_index)
|
||||
.unwrap_or_else(|| unreachable!("Imported function {} not found", fn_index));
|
||||
|
||||
let return_block_depth = self.outermost_block;
|
||||
self.outermost_block = self.block_loop_addrs.len() as u32;
|
||||
self.import_arguments.clear();
|
||||
self.import_arguments
|
||||
.extend(std::iter::repeat(Value::I64(0)).take(arg_type_bytes.len()));
|
||||
for (i, type_byte) in arg_type_bytes.iter().copied().enumerate().rev() {
|
||||
let arg = self.value_stack.pop();
|
||||
assert_eq!(ValueType::from(arg), ValueType::from(type_byte));
|
||||
self.import_arguments[i] = arg;
|
||||
}
|
||||
|
||||
let _function_byte_length =
|
||||
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
||||
self.call_stack.push_frame(
|
||||
return_addr,
|
||||
return_block_depth,
|
||||
arg_type_bytes,
|
||||
&mut self.value_stack,
|
||||
&module.code.bytes,
|
||||
&mut self.program_counter,
|
||||
);
|
||||
let optional_return_val = self.import_dispatcher.dispatch(
|
||||
import.module,
|
||||
import.name,
|
||||
&self.import_arguments,
|
||||
&mut self.memory,
|
||||
);
|
||||
if let Some(return_val) = optional_return_val {
|
||||
self.value_stack.push(return_val);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
// call an internal Wasm function
|
||||
let return_addr = self.program_counter as u32;
|
||||
self.program_counter = module.code.function_offsets[fn_index] as usize;
|
||||
|
||||
let return_block_depth = self.outermost_block;
|
||||
self.outermost_block = self.block_loop_addrs.len() as u32;
|
||||
|
||||
let _function_byte_length =
|
||||
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
|
||||
self.call_stack.push_frame(
|
||||
return_addr,
|
||||
return_block_depth,
|
||||
arg_type_bytes,
|
||||
&mut self.value_stack,
|
||||
&module.code.bytes,
|
||||
&mut self.program_counter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_next_instruction(&mut self, module: &WasmModule<'a>) -> Action {
|
||||
|
@ -24,3 +24,26 @@ pub trait ImportDispatcher {
|
||||
memory: &mut [u8],
|
||||
) -> Option<Value>;
|
||||
}
|
||||
|
||||
pub const DEFAULT_IMPORTS: DefaultImportDispatcher = DefaultImportDispatcher {};
|
||||
|
||||
pub struct DefaultImportDispatcher {}
|
||||
|
||||
impl ImportDispatcher for DefaultImportDispatcher {
|
||||
fn dispatch(
|
||||
&mut self,
|
||||
module_name: &str,
|
||||
function_name: &str,
|
||||
arguments: &[Value],
|
||||
memory: &mut [u8],
|
||||
) -> Option<Value> {
|
||||
if module_name == wasi::MODULE_NAME {
|
||||
wasi::dispatch(function_name, arguments, memory)
|
||||
} else {
|
||||
panic!(
|
||||
"DefaultImportDispatcher does not implement {}.{}",
|
||||
module_name, function_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use std::fs;
|
||||
use std::io;
|
||||
use std::process;
|
||||
|
||||
use roc_wasm_interp::Instance;
|
||||
use roc_wasm_interp::{Instance, DEFAULT_IMPORTS};
|
||||
use roc_wasm_module::WasmModule;
|
||||
|
||||
pub const FLAG_FUNCTION: &str = "function";
|
||||
@ -88,10 +88,11 @@ fn main() -> io::Result<()> {
|
||||
|
||||
// Create an execution instance
|
||||
|
||||
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap_or_else(|e| {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2);
|
||||
});
|
||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode)
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2);
|
||||
});
|
||||
|
||||
// Run
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::Instance;
|
||||
use crate::{DefaultImportDispatcher, Instance, DEFAULT_IMPORTS};
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_wasm_module::{
|
||||
opcodes::OpCode, Export, ExportType, SerialBuffer, Signature, Value, ValueType, WasmModule,
|
||||
};
|
||||
|
||||
pub fn default_state(arena: &Bump) -> Instance {
|
||||
pub fn default_state(arena: &Bump) -> Instance<DefaultImportDispatcher> {
|
||||
let pages = 1;
|
||||
let program_counter = 0;
|
||||
let globals = [];
|
||||
Instance::new(arena, pages, program_counter, globals)
|
||||
Instance::new(arena, pages, program_counter, globals, DEFAULT_IMPORTS)
|
||||
}
|
||||
|
||||
pub fn const_value(buf: &mut Vec<'_, u8>, value: Value) {
|
||||
@ -75,7 +75,7 @@ where
|
||||
std::fs::write(&filename, outfile_buf).unwrap();
|
||||
}
|
||||
|
||||
let mut inst = Instance::for_module(&arena, &module, true).unwrap();
|
||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, true).unwrap();
|
||||
|
||||
let return_val = inst.call_export(&module, "test", []).unwrap().unwrap();
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_wasm_interp::test_utils::{const_value, create_exported_function_no_locals, default_state};
|
||||
use roc_wasm_interp::{Action, Instance, ValueStack};
|
||||
use roc_wasm_interp::{Action, Instance, ValueStack, DEFAULT_IMPORTS};
|
||||
use roc_wasm_module::{
|
||||
opcodes::OpCode, sections::ElementSegment, Export, ExportType, SerialBuffer, Serialize,
|
||||
Signature, Value, ValueType, WasmModule,
|
||||
@ -517,7 +517,7 @@ fn test_call_return_no_args() {
|
||||
println!("Wrote to {}", filename);
|
||||
}
|
||||
|
||||
let mut inst = Instance::for_module(&arena, &module, true).unwrap();
|
||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, true).unwrap();
|
||||
|
||||
let return_val = inst
|
||||
.call_export(&module, start_fn_name, [])
|
||||
@ -656,7 +656,7 @@ fn test_call_indirect_help(table_index: u32, elem_index: u32) -> Value {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap();
|
||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
|
||||
inst.call_export(&module, start_fn_name, [])
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_wasm_interp::{test_utils::create_exported_function_no_locals, Instance};
|
||||
use roc_wasm_interp::{test_utils::create_exported_function_no_locals, Instance, DEFAULT_IMPORTS};
|
||||
use roc_wasm_module::{
|
||||
opcodes::OpCode,
|
||||
sections::{DataMode, DataSegment, MemorySection},
|
||||
@ -16,7 +16,7 @@ fn test_currentmemory() {
|
||||
module.memory = MemorySection::new(&arena, pages * MemorySection::PAGE_SIZE);
|
||||
module.code.bytes.push(OpCode::CURRENTMEMORY as u8);
|
||||
|
||||
let mut state = Instance::new(&arena, pages, pc, []);
|
||||
let mut state = Instance::new(&arena, pages, pc, [], DEFAULT_IMPORTS);
|
||||
state.execute_next_instruction(&module);
|
||||
assert_eq!(state.value_stack.pop(), Value::I32(3))
|
||||
}
|
||||
@ -34,7 +34,7 @@ fn test_growmemory() {
|
||||
module.code.bytes.encode_i32(grow_pages);
|
||||
module.code.bytes.push(OpCode::GROWMEMORY as u8);
|
||||
|
||||
let mut state = Instance::new(&arena, existing_pages, pc, []);
|
||||
let mut state = Instance::new(&arena, existing_pages, pc, [], DEFAULT_IMPORTS);
|
||||
state.execute_next_instruction(&module);
|
||||
state.execute_next_instruction(&module);
|
||||
assert_eq!(state.memory.len(), 5 * MemorySection::PAGE_SIZE as usize);
|
||||
@ -76,7 +76,7 @@ fn test_load(load_op: OpCode, ty: ValueType, data: &[u8], addr: u32, offset: u32
|
||||
std::fs::write("/tmp/roc/interp_load_test.wasm", outfile_buf).unwrap();
|
||||
}
|
||||
|
||||
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap();
|
||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
|
||||
inst.call_export(&module, start_fn_name, [])
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
@ -273,7 +273,7 @@ fn test_store<'a>(
|
||||
buf.append_u8(OpCode::END as u8);
|
||||
});
|
||||
|
||||
let mut inst = Instance::for_module(&arena, &module, is_debug_mode).unwrap();
|
||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode).unwrap();
|
||||
inst.call_export(&module, start_fn_name, []).unwrap();
|
||||
|
||||
inst.memory
|
||||
|
Loading…
Reference in New Issue
Block a user