Use embedded API in the wasmtime-rust (#540)

This commit is contained in:
Yury Delendik
2019-11-13 09:15:37 -06:00
committed by Alex Crichton
parent 2737c5e8e5
commit 98266498af
13 changed files with 316 additions and 112 deletions

View File

@@ -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' }

View File

@@ -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);
}
}