Use embedded API in the wasmtime-rust (#540)
This commit is contained in:
committed by
Alex Crichton
parent
2737c5e8e5
commit
98266498af
@@ -16,6 +16,7 @@ cranelift-codegen = { version = "0.50.0", default-features = false }
|
||||
walrus = "0.13"
|
||||
wasmparser = { version = "0.39.2", default-features = false }
|
||||
wasm-webidl-bindings = "0.6"
|
||||
wasmtime = { path = '../api' }
|
||||
wasmtime-jit = { path = '../jit', default-features = false }
|
||||
wasmtime-runtime = { path = '../runtime', default-features = false }
|
||||
wasmtime-wasi = { path = '../wasi' }
|
||||
|
||||
@@ -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,9 +344,95 @@ fn abi2ast(param: &ir::AbiParam) -> Result<ast::WebidlScalarType> {
|
||||
})
|
||||
}
|
||||
|
||||
trait TranslateContext {
|
||||
fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result<i32>;
|
||||
unsafe fn get_memory(&mut self) -> Result<&mut [u8]>;
|
||||
}
|
||||
|
||||
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 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");
|
||||
}
|
||||
Ok(match results[0] {
|
||||
RuntimeValue::I32(i) => i,
|
||||
_ => bail!("allocator function bad return type"),
|
||||
})
|
||||
}
|
||||
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 Context,
|
||||
handle: &mut InstanceHandle,
|
||||
cx: &mut dyn TranslateContext,
|
||||
bindings: &[ast::IncomingBindingExpression],
|
||||
args: &[Value],
|
||||
) -> Result<Vec<RuntimeValue>> {
|
||||
@@ -313,29 +443,11 @@ fn translate_incoming(
|
||||
_ => bail!("unsupported incoming binding expr {:?}", expr),
|
||||
};
|
||||
|
||||
let mut copy = |alloc_func_name: &str, bytes: &[u8]| {
|
||||
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 alloc_args = vec![RuntimeValue::I32(len)];
|
||||
let results = match cx.invoke(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] {
|
||||
RuntimeValue::I32(i) => i,
|
||||
_ => bail!("allocator function bad return type"),
|
||||
};
|
||||
let memory = 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`"),
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user