|
|
@ -18,6 +18,7 @@ use core::slice; |
|
|
|
use core::str; |
|
|
|
use cranelift_codegen::ir; |
|
|
|
use wasm_webidl_bindings::ast; |
|
|
|
use wasmtime_api as api; |
|
|
|
use wasmtime_jit::{ActionOutcome, Context, RuntimeValue}; |
|
|
|
use wasmtime_runtime::{Export, InstanceHandle}; |
|
|
|
|
|
|
@ -141,12 +142,55 @@ impl ModuleData { |
|
|
|
let incoming = binding.param_bindings()?; |
|
|
|
let outgoing = binding.result_bindings()?; |
|
|
|
|
|
|
|
let wasm_args = translate_incoming(cx, handle, &incoming, args)?; |
|
|
|
let wasm_args = |
|
|
|
translate_incoming(&mut RawTranslateContext::new(cx, handle), &incoming, args)?; |
|
|
|
let wasm_results = match cx.invoke(handle, export, &wasm_args)? { |
|
|
|
ActionOutcome::Returned { values } => values, |
|
|
|
ActionOutcome::Trapped { message } => bail!("trapped: {}", message), |
|
|
|
}; |
|
|
|
translate_outgoing(cx, handle, &outgoing, &wasm_results) |
|
|
|
translate_outgoing( |
|
|
|
&mut RawTranslateContext::new(cx, handle), |
|
|
|
&outgoing, |
|
|
|
&wasm_results, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
/// Invokes wasmtime function with a `&[Value]` list. `Value` the set of
|
|
|
|
/// wasm interface types.
|
|
|
|
pub fn invoke_export( |
|
|
|
&self, |
|
|
|
instance: &api::HostRef<api::Instance>, |
|
|
|
export: &str, |
|
|
|
args: &[Value], |
|
|
|
) -> Result<Vec<Value>> { |
|
|
|
let mut handle = instance.borrow().handle().clone(); |
|
|
|
|
|
|
|
let binding = self.binding_for_export(&mut handle, export)?; |
|
|
|
let incoming = binding.param_bindings()?; |
|
|
|
let outgoing = binding.result_bindings()?; |
|
|
|
|
|
|
|
let f = instance |
|
|
|
.borrow() |
|
|
|
.find_export_by_name(export) |
|
|
|
.ok_or_else(|| format_err!("failed to find export `{}`", export))? |
|
|
|
.func() |
|
|
|
.ok_or_else(|| format_err!("`{}` is not a function", export))? |
|
|
|
.clone(); |
|
|
|
|
|
|
|
let mut cx = InstanceTranslateContext(instance.clone()); |
|
|
|
let wasm_args = translate_incoming(&mut cx, &incoming, args)? |
|
|
|
.into_iter() |
|
|
|
.map(|rv| rv.into()) |
|
|
|
.collect::<Vec<_>>(); |
|
|
|
let wasm_results = match f.borrow().call(&wasm_args) { |
|
|
|
Ok(values) => values |
|
|
|
.to_vec() |
|
|
|
.into_iter() |
|
|
|
.map(|v: api::Val| v.into()) |
|
|
|
.collect::<Vec<RuntimeValue>>(), |
|
|
|
Err(trap) => bail!("trapped: {:?}", trap), |
|
|
|
}; |
|
|
|
translate_outgoing(&mut cx, &outgoing, &wasm_results) |
|
|
|
} |
|
|
|
|
|
|
|
/// Returns an appropriate binding for the `name` export in this module
|
|
|
@ -300,42 +344,110 @@ fn abi2ast(param: &ir::AbiParam) -> Result<ast::WebidlScalarType> { |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
fn translate_incoming( |
|
|
|
cx: &mut Context, |
|
|
|
handle: &mut InstanceHandle, |
|
|
|
bindings: &[ast::IncomingBindingExpression], |
|
|
|
args: &[Value], |
|
|
|
) -> Result<Vec<RuntimeValue>> { |
|
|
|
let get = |expr: &ast::IncomingBindingExpression| match expr { |
|
|
|
ast::IncomingBindingExpression::Get(g) => args |
|
|
|
.get(g.idx as usize) |
|
|
|
.ok_or_else(|| format_err!("argument index out of bounds: {}", g.idx)), |
|
|
|
_ => bail!("unsupported incoming binding expr {:?}", expr), |
|
|
|
}; |
|
|
|
trait TranslateContext { |
|
|
|
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32>; |
|
|
|
unsafe fn get_memory(&mut self) -> Result<&mut [u8]>; |
|
|
|
} |
|
|
|
|
|
|
|
let mut copy = |alloc_func_name: &str, bytes: &[u8]| { |
|
|
|
let len = i32::try_from(bytes.len()).map_err(|_| format_err!("length overflow"))?; |
|
|
|
struct RawTranslateContext<'a> { |
|
|
|
cx: &'a mut Context, |
|
|
|
handle: &'a mut InstanceHandle, |
|
|
|
} |
|
|
|
|
|
|
|
impl<'a> RawTranslateContext<'a> { |
|
|
|
fn new(cx: &'a mut Context, handle: &'a mut InstanceHandle) -> Self { |
|
|
|
RawTranslateContext { cx, handle } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl TranslateContext for RawTranslateContext<'_> { |
|
|
|
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32> { |
|
|
|
let alloc_args = vec![RuntimeValue::I32(len)]; |
|
|
|
let results = match cx.invoke(handle, alloc_func_name, &alloc_args)? { |
|
|
|
let results = match self.cx.invoke(self.handle, alloc_func_name, &alloc_args)? { |
|
|
|
ActionOutcome::Returned { values } => values, |
|
|
|
ActionOutcome::Trapped { message } => bail!("trapped: {}", message), |
|
|
|
}; |
|
|
|
if results.len() != 1 { |
|
|
|
bail!("allocator function wrong number of results"); |
|
|
|
} |
|
|
|
let ptr = match results[0] { |
|
|
|
Ok(match results[0] { |
|
|
|
RuntimeValue::I32(i) => i, |
|
|
|
_ => bail!("allocator function bad return type"), |
|
|
|
}; |
|
|
|
let memory = handle |
|
|
|
}) |
|
|
|
} |
|
|
|
unsafe fn get_memory(&mut self) -> Result<&mut [u8]> { |
|
|
|
let memory = self |
|
|
|
.handle |
|
|
|
.lookup("memory") |
|
|
|
.ok_or_else(|| format_err!("no exported `memory`"))?; |
|
|
|
let definition = match memory { |
|
|
|
wasmtime_runtime::Export::Memory { definition, .. } => definition, |
|
|
|
_ => bail!("export `memory` wasn't a `Memory`"), |
|
|
|
}; |
|
|
|
Ok(slice::from_raw_parts_mut( |
|
|
|
(*definition).base, |
|
|
|
(*definition).current_length, |
|
|
|
)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
struct InstanceTranslateContext(pub api::HostRef<api::Instance>); |
|
|
|
|
|
|
|
impl TranslateContext for InstanceTranslateContext { |
|
|
|
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32> { |
|
|
|
let alloc = self |
|
|
|
.0 |
|
|
|
.borrow() |
|
|
|
.find_export_by_name(alloc_func_name) |
|
|
|
.ok_or_else(|| format_err!("failed to find alloc function `{}`", alloc_func_name))? |
|
|
|
.func() |
|
|
|
.ok_or_else(|| format_err!("`{}` is not a (alloc) function", alloc_func_name))? |
|
|
|
.clone(); |
|
|
|
let alloc_args = vec![api::Val::I32(len)]; |
|
|
|
let results = match alloc.borrow().call(&alloc_args) { |
|
|
|
Ok(values) => values, |
|
|
|
Err(trap) => bail!("trapped: {:?}", trap), |
|
|
|
}; |
|
|
|
if results.len() != 1 { |
|
|
|
bail!("allocator function wrong number of results"); |
|
|
|
} |
|
|
|
Ok(match results[0] { |
|
|
|
api::Val::I32(i) => i, |
|
|
|
_ => bail!("allocator function bad return type"), |
|
|
|
}) |
|
|
|
} |
|
|
|
unsafe fn get_memory(&mut self) -> Result<&mut [u8]> { |
|
|
|
let memory = self |
|
|
|
.0 |
|
|
|
.borrow() |
|
|
|
.find_export_by_name("memory") |
|
|
|
.ok_or_else(|| format_err!("failed to find `memory` export"))? |
|
|
|
.memory() |
|
|
|
.ok_or_else(|| format_err!("`memory` is not a memory"))? |
|
|
|
.clone(); |
|
|
|
let ptr = memory.borrow().data_ptr(); |
|
|
|
let len = memory.borrow().data_size(); |
|
|
|
Ok(std::slice::from_raw_parts_mut(ptr, len)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn translate_incoming( |
|
|
|
cx: &mut dyn TranslateContext, |
|
|
|
bindings: &[ast::IncomingBindingExpression], |
|
|
|
args: &[Value], |
|
|
|
) -> Result<Vec<RuntimeValue>> { |
|
|
|
let get = |expr: &ast::IncomingBindingExpression| match expr { |
|
|
|
ast::IncomingBindingExpression::Get(g) => args |
|
|
|
.get(g.idx as usize) |
|
|
|
.ok_or_else(|| format_err!("argument index out of bounds: {}", g.idx)), |
|
|
|
_ => bail!("unsupported incoming binding expr {:?}", expr), |
|
|
|
}; |
|
|
|
|
|
|
|
let mut copy = |alloc_func_name: &str, bytes: &[u8]| -> Result<(i32, i32)> { |
|
|
|
let len = i32::try_from(bytes.len()).map_err(|_| format_err!("length overflow"))?; |
|
|
|
let ptr = cx.invoke_alloc(alloc_func_name, len)?; |
|
|
|
unsafe { |
|
|
|
let raw = slice::from_raw_parts_mut((*definition).base, (*definition).current_length); |
|
|
|
let raw = cx.get_memory()?; |
|
|
|
raw[ptr as usize..][..bytes.len()].copy_from_slice(bytes) |
|
|
|
} |
|
|
|
|
|
|
@ -392,27 +504,12 @@ fn translate_incoming( |
|
|
|
} |
|
|
|
|
|
|
|
fn translate_outgoing( |
|
|
|
cx: &mut Context, |
|
|
|
handle: &mut InstanceHandle, |
|
|
|
cx: &mut dyn TranslateContext, |
|
|
|
bindings: &[ast::OutgoingBindingExpression], |
|
|
|
args: &[RuntimeValue], |
|
|
|
) -> Result<Vec<Value>> { |
|
|
|
let mut values = Vec::new(); |
|
|
|
|
|
|
|
let raw_memory = || unsafe { |
|
|
|
let memory = handle |
|
|
|
.lookup_immutable("memory") |
|
|
|
.ok_or_else(|| format_err!("no exported `memory`"))?; |
|
|
|
let definition = match memory { |
|
|
|
wasmtime_runtime::Export::Memory { definition, .. } => definition, |
|
|
|
_ => bail!("export `memory` wasn't a `Memory`"), |
|
|
|
}; |
|
|
|
Ok(slice::from_raw_parts_mut( |
|
|
|
(*definition).base, |
|
|
|
(*definition).current_length, |
|
|
|
)) |
|
|
|
}; |
|
|
|
|
|
|
|
let get = |idx: u32| { |
|
|
|
args.get(idx as usize) |
|
|
|
.cloned() |
|
|
@ -468,11 +565,11 @@ fn translate_outgoing( |
|
|
|
RuntimeValue::I32(a) => a, |
|
|
|
_ => bail!("length must be an i32"), |
|
|
|
}; |
|
|
|
let bytes = &raw_memory()?[offset as usize..][..length as usize]; |
|
|
|
let bytes = unsafe { &cx.get_memory()?[offset as usize..][..length as usize] }; |
|
|
|
values.push(Value::String(str::from_utf8(bytes).unwrap().to_string())); |
|
|
|
} |
|
|
|
_ => { |
|
|
|
drop((cx, handle)); |
|
|
|
drop(cx); |
|
|
|
bail!("unsupported outgoing binding expr {:?}", expr); |
|
|
|
} |
|
|
|
} |
|
|
|