Use embedded API in the wasmtime-rust (#540)
This commit is contained in:
committed by
Alex Crichton
parent
2737c5e8e5
commit
98266498af
@@ -255,7 +255,7 @@ impl Global {
|
|||||||
} else {
|
} else {
|
||||||
panic!("wasmtime export is not memory")
|
panic!("wasmtime export is not memory")
|
||||||
};
|
};
|
||||||
let ty = GlobalType::from_cranelift_global(global.clone());
|
let ty = GlobalType::from_cranelift_global(&global);
|
||||||
Global {
|
Global {
|
||||||
_store: store.clone(),
|
_store: store.clone(),
|
||||||
r#type: ty,
|
r#type: ty,
|
||||||
@@ -395,7 +395,7 @@ impl Table {
|
|||||||
} else {
|
} else {
|
||||||
panic!("wasmtime export is not table")
|
panic!("wasmtime export is not table")
|
||||||
};
|
};
|
||||||
let ty = TableType::from_cranelift_table(table.table.clone());
|
let ty = TableType::from_cranelift_table(&table.table);
|
||||||
Table {
|
Table {
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
r#type: ty,
|
r#type: ty,
|
||||||
@@ -479,7 +479,7 @@ impl Memory {
|
|||||||
} else {
|
} else {
|
||||||
panic!("wasmtime export is not memory")
|
panic!("wasmtime export is not memory")
|
||||||
};
|
};
|
||||||
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
|
let ty = MemoryType::from_cranelift_memory(&memory.memory);
|
||||||
Memory {
|
Memory {
|
||||||
_store: store.clone(),
|
_store: store.clone(),
|
||||||
r#type: ty,
|
r#type: ty,
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ use crate::externals::Extern;
|
|||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::r#ref::HostRef;
|
use crate::r#ref::HostRef;
|
||||||
use crate::runtime::Store;
|
use crate::runtime::Store;
|
||||||
|
use crate::types::{ExportType, ExternType, Name};
|
||||||
use crate::{HashMap, HashSet};
|
use crate::{HashMap, HashSet};
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, vec::Vec};
|
use alloc::{boxed::Box, rc::Rc, vec::Vec};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use wasmtime_jit::{instantiate, Resolver};
|
use wasmtime_jit::{instantiate, Resolver};
|
||||||
@@ -49,6 +50,8 @@ pub fn instantiate_in_context(
|
|||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
instance_handle: InstanceHandle,
|
instance_handle: InstanceHandle,
|
||||||
|
|
||||||
|
module: HostRef<Module>,
|
||||||
|
|
||||||
// We need to keep CodeMemory alive.
|
// We need to keep CodeMemory alive.
|
||||||
contexts: HashSet<Context>,
|
contexts: HashSet<Context>,
|
||||||
|
|
||||||
@@ -70,8 +73,12 @@ impl Instance {
|
|||||||
.zip(externs.iter())
|
.zip(externs.iter())
|
||||||
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
|
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let (mut instance_handle, contexts) =
|
let (mut instance_handle, contexts) = instantiate_in_context(
|
||||||
instantiate_in_context(module.borrow().binary(), imports, context, exports)?;
|
module.borrow().binary().expect("binary"),
|
||||||
|
imports,
|
||||||
|
context,
|
||||||
|
exports,
|
||||||
|
)?;
|
||||||
|
|
||||||
let exports = {
|
let exports = {
|
||||||
let module = module.borrow();
|
let module = module.borrow();
|
||||||
@@ -89,6 +96,7 @@ impl Instance {
|
|||||||
};
|
};
|
||||||
Ok(Instance {
|
Ok(Instance {
|
||||||
instance_handle,
|
instance_handle,
|
||||||
|
module: module.clone(),
|
||||||
contexts,
|
contexts,
|
||||||
exports,
|
exports,
|
||||||
})
|
})
|
||||||
@@ -98,14 +106,25 @@ impl Instance {
|
|||||||
&self.exports
|
&self.exports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_export_by_name(&self, name: &str) -> Option<&Extern> {
|
||||||
|
let (i, _) = self
|
||||||
|
.module
|
||||||
|
.borrow()
|
||||||
|
.exports()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, e)| e.name().as_str() == name)?;
|
||||||
|
Some(&self.exports()[i])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_handle(
|
pub fn from_handle(
|
||||||
store: &HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance_handle: InstanceHandle,
|
instance_handle: InstanceHandle,
|
||||||
) -> Result<(Instance, HashMap<String, usize>)> {
|
) -> Result<Instance> {
|
||||||
let contexts = HashSet::new();
|
let contexts = HashSet::new();
|
||||||
|
|
||||||
let mut exports = Vec::new();
|
let mut exports = Vec::new();
|
||||||
let mut export_names_map = HashMap::new();
|
let mut exports_types = Vec::new();
|
||||||
let mut mutable = instance_handle.clone();
|
let mut mutable = instance_handle.clone();
|
||||||
for (name, _) in instance_handle.clone().exports() {
|
for (name, _) in instance_handle.clone().exports() {
|
||||||
let export = mutable.lookup(name).expect("export");
|
let export = mutable.lookup(name).expect("export");
|
||||||
@@ -115,7 +134,8 @@ impl Instance {
|
|||||||
// imported into this store using the from_handle() method.
|
// imported into this store using the from_handle() method.
|
||||||
let _ = store.borrow_mut().register_cranelift_signature(signature);
|
let _ = store.borrow_mut().register_cranelift_signature(signature);
|
||||||
}
|
}
|
||||||
export_names_map.insert(name.to_owned(), exports.len());
|
let extern_type = ExternType::from_wasmtime_export(&export);
|
||||||
|
exports_types.push(ExportType::new(Name::new(name), extern_type));
|
||||||
exports.push(Extern::from_wasmtime_export(
|
exports.push(Extern::from_wasmtime_export(
|
||||||
store,
|
store,
|
||||||
instance_handle.clone(),
|
instance_handle.clone(),
|
||||||
@@ -123,14 +143,17 @@ impl Instance {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
let module = HostRef::new(Module::from_exports(
|
||||||
Instance {
|
store,
|
||||||
|
exports_types.into_boxed_slice(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(Instance {
|
||||||
instance_handle,
|
instance_handle,
|
||||||
|
module,
|
||||||
contexts,
|
contexts,
|
||||||
exports: exports.into_boxed_slice(),
|
exports: exports.into_boxed_slice(),
|
||||||
},
|
})
|
||||||
export_names_map,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(&self) -> &InstanceHandle {
|
pub fn handle(&self) -> &InstanceHandle {
|
||||||
|
|||||||
@@ -170,10 +170,16 @@ fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[Ex
|
|||||||
Ok((imports.into_boxed_slice(), exports.into_boxed_slice()))
|
Ok((imports.into_boxed_slice(), exports.into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) enum ModuleCodeSource {
|
||||||
|
Binary(Box<[u8]>),
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
store: HostRef<Store>,
|
store: HostRef<Store>,
|
||||||
binary: Box<[u8]>,
|
source: ModuleCodeSource,
|
||||||
imports: Box<[ImportType]>,
|
imports: Box<[ImportType]>,
|
||||||
exports: Box<[ExportType]>,
|
exports: Box<[ExportType]>,
|
||||||
}
|
}
|
||||||
@@ -183,13 +189,16 @@ impl Module {
|
|||||||
let (imports, exports) = read_imports_and_exports(binary)?;
|
let (imports, exports) = read_imports_and_exports(binary)?;
|
||||||
Ok(Module {
|
Ok(Module {
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
binary: binary.into(),
|
source: ModuleCodeSource::Binary(binary.into()),
|
||||||
imports,
|
imports,
|
||||||
exports,
|
exports,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn binary(&self) -> &[u8] {
|
pub(crate) fn binary(&self) -> Option<&[u8]> {
|
||||||
&self.binary
|
match &self.source {
|
||||||
|
ModuleCodeSource::Binary(b) => Some(b),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn validate(_store: &Store, binary: &[u8]) -> bool {
|
pub fn validate(_store: &Store, binary: &[u8]) -> bool {
|
||||||
validate(binary, None).is_ok()
|
validate(binary, None).is_ok()
|
||||||
@@ -200,4 +209,12 @@ impl Module {
|
|||||||
pub fn exports(&self) -> &[ExportType] {
|
pub fn exports(&self) -> &[ExportType] {
|
||||||
&self.exports
|
&self.exports
|
||||||
}
|
}
|
||||||
|
pub fn from_exports(store: &HostRef<Store>, exports: Box<[ExportType]>) -> Self {
|
||||||
|
Module {
|
||||||
|
store: store.clone(),
|
||||||
|
source: ModuleCodeSource::Unknown,
|
||||||
|
imports: Box::new([]),
|
||||||
|
exports,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::context::{create_compiler, Context};
|
use crate::context::Context;
|
||||||
use crate::r#ref::HostRef;
|
use crate::r#ref::HostRef;
|
||||||
use crate::HashMap;
|
use crate::HashMap;
|
||||||
use alloc::{boxed::Box, rc::Rc, string::String};
|
use alloc::{rc::Rc, string::String};
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use cranelift_codegen::{ir, settings};
|
use cranelift_codegen::{ir, settings};
|
||||||
use wasmtime_jit::{CompilationStrategy, Features};
|
use wasmtime_jit::{CompilationStrategy, Features};
|
||||||
@@ -81,11 +81,6 @@ impl Engine {
|
|||||||
pub(crate) fn config(&self) -> &Config {
|
pub(crate) fn config(&self) -> &Config {
|
||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_wasmtime_context(&self) -> wasmtime_jit::Context {
|
|
||||||
let flags = self.config.flags().clone();
|
|
||||||
wasmtime_jit::Context::new(Box::new(create_compiler(flags, self.config.strategy())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
|
|||||||
@@ -127,6 +127,22 @@ impl ExternType {
|
|||||||
_ => panic!("ExternType::ExternMemory expected"),
|
_ => panic!("ExternType::ExternMemory expected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub(crate) fn from_wasmtime_export(export: &wasmtime_runtime::Export) -> Self {
|
||||||
|
match export {
|
||||||
|
wasmtime_runtime::Export::Function { signature, .. } => {
|
||||||
|
ExternType::ExternFunc(FuncType::from_cranelift_signature(signature.clone()))
|
||||||
|
}
|
||||||
|
wasmtime_runtime::Export::Memory { memory, .. } => {
|
||||||
|
ExternType::ExternMemory(MemoryType::from_cranelift_memory(&memory.memory))
|
||||||
|
}
|
||||||
|
wasmtime_runtime::Export::Global { global, .. } => {
|
||||||
|
ExternType::ExternGlobal(GlobalType::from_cranelift_global(&global))
|
||||||
|
}
|
||||||
|
wasmtime_runtime::Export::Table { table, .. } => {
|
||||||
|
ExternType::ExternTable(TableType::from_cranelift_table(&table.table))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function Types
|
// Function Types
|
||||||
@@ -224,7 +240,7 @@ impl GlobalType {
|
|||||||
self.mutability
|
self.mutability
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_cranelift_global(global: cranelift_wasm::Global) -> GlobalType {
|
pub(crate) fn from_cranelift_global(global: &cranelift_wasm::Global) -> GlobalType {
|
||||||
let ty = ValType::from_cranelift_type(global.ty);
|
let ty = ValType::from_cranelift_type(global.ty);
|
||||||
let mutability = if global.mutability {
|
let mutability = if global.mutability {
|
||||||
Mutability::Var
|
Mutability::Var
|
||||||
@@ -254,7 +270,7 @@ impl TableType {
|
|||||||
&self.limits
|
&self.limits
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_cranelift_table(table: cranelift_wasm::Table) -> TableType {
|
pub(crate) fn from_cranelift_table(table: &cranelift_wasm::Table) -> TableType {
|
||||||
assert!(if let cranelift_wasm::TableElementType::Func = table.ty {
|
assert!(if let cranelift_wasm::TableElementType::Func = table.ty {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@@ -281,7 +297,7 @@ impl MemoryType {
|
|||||||
&self.limits
|
&self.limits
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_cranelift_memory(memory: cranelift_wasm::Memory) -> MemoryType {
|
pub(crate) fn from_cranelift_memory(memory: &cranelift_wasm::Memory) -> MemoryType {
|
||||||
MemoryType::new(Limits::new(
|
MemoryType::new(Limits::new(
|
||||||
memory.minimum,
|
memory.minimum,
|
||||||
memory.maximum.unwrap_or(::core::u32::MAX),
|
memory.maximum.unwrap_or(::core::u32::MAX),
|
||||||
@@ -294,6 +310,16 @@ impl MemoryType {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Name(String);
|
pub struct Name(String);
|
||||||
|
|
||||||
|
impl Name {
|
||||||
|
pub fn new(value: &str) -> Self {
|
||||||
|
Name(value.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<String> for Name {
|
impl From<String> for Name {
|
||||||
fn from(s: String) -> Name {
|
fn from(s: String) -> Name {
|
||||||
Name(s)
|
Name(s)
|
||||||
|
|||||||
@@ -180,6 +180,34 @@ impl Into<AnyRef> for Val {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<RuntimeValue> for Val {
|
||||||
|
fn from(rv: RuntimeValue) -> Self {
|
||||||
|
match rv {
|
||||||
|
RuntimeValue::I32(i) => Val::I32(i),
|
||||||
|
RuntimeValue::I64(i) => Val::I64(i),
|
||||||
|
RuntimeValue::F32(u) => Val::F32(u),
|
||||||
|
RuntimeValue::F64(u) => Val::F64(u),
|
||||||
|
x => {
|
||||||
|
panic!("unsupported {:?}", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<RuntimeValue> for Val {
|
||||||
|
fn into(self) -> RuntimeValue {
|
||||||
|
match self {
|
||||||
|
Val::I32(i) => RuntimeValue::I32(i),
|
||||||
|
Val::I64(i) => RuntimeValue::I64(i),
|
||||||
|
Val::F32(u) => RuntimeValue::F32(u),
|
||||||
|
Val::F64(u) => RuntimeValue::F64(u),
|
||||||
|
x => {
|
||||||
|
panic!("unsupported {:?}", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn into_checked_anyfunc(
|
pub(crate) fn into_checked_anyfunc(
|
||||||
val: Val,
|
val: Val,
|
||||||
store: &HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ cranelift-codegen = { version = "0.50.0", default-features = false }
|
|||||||
walrus = "0.13"
|
walrus = "0.13"
|
||||||
wasmparser = { version = "0.39.2", default-features = false }
|
wasmparser = { version = "0.39.2", default-features = false }
|
||||||
wasm-webidl-bindings = "0.6"
|
wasm-webidl-bindings = "0.6"
|
||||||
|
wasmtime = { path = '../api' }
|
||||||
wasmtime-jit = { path = '../jit', default-features = false }
|
wasmtime-jit = { path = '../jit', default-features = false }
|
||||||
wasmtime-runtime = { path = '../runtime', default-features = false }
|
wasmtime-runtime = { path = '../runtime', default-features = false }
|
||||||
wasmtime-wasi = { path = '../wasi' }
|
wasmtime-wasi = { path = '../wasi' }
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use core::slice;
|
|||||||
use core::str;
|
use core::str;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use wasm_webidl_bindings::ast;
|
use wasm_webidl_bindings::ast;
|
||||||
|
use wasmtime_api as api;
|
||||||
use wasmtime_jit::{ActionOutcome, Context, RuntimeValue};
|
use wasmtime_jit::{ActionOutcome, Context, RuntimeValue};
|
||||||
use wasmtime_runtime::{Export, InstanceHandle};
|
use wasmtime_runtime::{Export, InstanceHandle};
|
||||||
|
|
||||||
@@ -141,12 +142,55 @@ impl ModuleData {
|
|||||||
let incoming = binding.param_bindings()?;
|
let incoming = binding.param_bindings()?;
|
||||||
let outgoing = binding.result_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)? {
|
let wasm_results = match cx.invoke(handle, export, &wasm_args)? {
|
||||||
ActionOutcome::Returned { values } => values,
|
ActionOutcome::Returned { values } => values,
|
||||||
ActionOutcome::Trapped { message } => bail!("trapped: {}", message),
|
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
|
/// 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(
|
fn translate_incoming(
|
||||||
cx: &mut Context,
|
cx: &mut dyn TranslateContext,
|
||||||
handle: &mut InstanceHandle,
|
|
||||||
bindings: &[ast::IncomingBindingExpression],
|
bindings: &[ast::IncomingBindingExpression],
|
||||||
args: &[Value],
|
args: &[Value],
|
||||||
) -> Result<Vec<RuntimeValue>> {
|
) -> Result<Vec<RuntimeValue>> {
|
||||||
@@ -313,29 +443,11 @@ fn translate_incoming(
|
|||||||
_ => bail!("unsupported incoming binding expr {:?}", expr),
|
_ => 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 len = i32::try_from(bytes.len()).map_err(|_| format_err!("length overflow"))?;
|
||||||
let alloc_args = vec![RuntimeValue::I32(len)];
|
let ptr = cx.invoke_alloc(alloc_func_name, 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`"),
|
|
||||||
};
|
|
||||||
unsafe {
|
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)
|
raw[ptr as usize..][..bytes.len()].copy_from_slice(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,27 +504,12 @@ fn translate_incoming(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn translate_outgoing(
|
fn translate_outgoing(
|
||||||
cx: &mut Context,
|
cx: &mut dyn TranslateContext,
|
||||||
handle: &mut InstanceHandle,
|
|
||||||
bindings: &[ast::OutgoingBindingExpression],
|
bindings: &[ast::OutgoingBindingExpression],
|
||||||
args: &[RuntimeValue],
|
args: &[RuntimeValue],
|
||||||
) -> Result<Vec<Value>> {
|
) -> Result<Vec<Value>> {
|
||||||
let mut values = Vec::new();
|
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| {
|
let get = |idx: u32| {
|
||||||
args.get(idx as usize)
|
args.get(idx as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
@@ -468,11 +565,11 @@ fn translate_outgoing(
|
|||||||
RuntimeValue::I32(a) => a,
|
RuntimeValue::I32(a) => a,
|
||||||
_ => bail!("length must be an i32"),
|
_ => 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()));
|
values.push(Value::String(str::from_utf8(bytes).unwrap().to_string()));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
drop((cx, handle));
|
drop(cx);
|
||||||
bail!("unsupported outgoing binding expr {:?}", expr);
|
bail!("unsupported outgoing binding expr {:?}", expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ wasmtime-interface-types = { path = "../../interface-types" }
|
|||||||
wasmtime-jit = { path = "../../jit" }
|
wasmtime-jit = { path = "../../jit" }
|
||||||
wasmtime-rust-macro = { path = "./macro" }
|
wasmtime-rust-macro = { path = "./macro" }
|
||||||
wasmtime-wasi = { path = "../../wasi" }
|
wasmtime-wasi = { path = "../../wasi" }
|
||||||
|
wasmtime = { path = "../../api" }
|
||||||
anyhow = "1.0.19"
|
anyhow = "1.0.19"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
|
|||||||
@@ -34,8 +34,7 @@ fn generate_struct(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
|||||||
let root = root();
|
let root = root();
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#vis struct #name {
|
#vis struct #name {
|
||||||
cx: #root::wasmtime_jit::Context,
|
instance: #root::wasmtime_api::HostRef<#root::wasmtime_api::Instance>,
|
||||||
handle: #root::wasmtime_jit::InstanceHandle,
|
|
||||||
data: #root::wasmtime_interface_types::ModuleData,
|
data: #root::wasmtime_interface_types::ModuleData,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -49,34 +48,53 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
|||||||
#vis fn load_file(path: impl AsRef<std::path::Path>) -> #root::anyhow::Result<#name> {
|
#vis fn load_file(path: impl AsRef<std::path::Path>) -> #root::anyhow::Result<#name> {
|
||||||
let bytes = std::fs::read(path)?;
|
let bytes = std::fs::read(path)?;
|
||||||
|
|
||||||
let isa = {
|
use #root::wasmtime_api::{HostRef, Config, Extern, Engine, Store, Instance, Module};
|
||||||
let isa_builder = #root::cranelift_native::builder()
|
use #root::anyhow::{bail, format_err};
|
||||||
.map_err(|s| #root::anyhow::format_err!("{}", s))?;
|
|
||||||
let flag_builder = #root::cranelift_codegen::settings::builder();
|
|
||||||
isa_builder.finish(#root::cranelift_codegen::settings::Flags::new(flag_builder))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut cx = #root::wasmtime_jit::Context::with_isa(
|
let config = {
|
||||||
isa,
|
let flag_builder = #root::cranelift_codegen::settings::builder();
|
||||||
#root::wasmtime_jit::CompilationStrategy::Auto
|
let flags = #root::cranelift_codegen::settings::Flags::new(flag_builder);
|
||||||
).with_features(#root::wasmtime_jit::Features {
|
let features = #root::wasmtime_jit::Features {
|
||||||
multi_value: true,
|
multi_value: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
};
|
||||||
|
let strategy = #root::wasmtime_jit::CompilationStrategy::Auto;
|
||||||
|
Config::new(flags, features, false, strategy)
|
||||||
|
};
|
||||||
|
let engine = HostRef::new(Engine::new(config));
|
||||||
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
let global_exports = store.borrow().global_exports().clone();
|
||||||
|
|
||||||
let data = #root::wasmtime_interface_types::ModuleData::new(&bytes)?;
|
let data = #root::wasmtime_interface_types::ModuleData::new(&bytes)?;
|
||||||
|
|
||||||
|
let module = HostRef::new(Module::new(&store, &bytes)?);
|
||||||
|
|
||||||
|
let mut imports: Vec<Extern> = Vec::new();
|
||||||
if let Some(module_name) = data.find_wasi_module_name() {
|
if let Some(module_name) = data.find_wasi_module_name() {
|
||||||
let wasi_handle = wasmtime_wasi::instantiate_wasi(
|
let wasi_handle = wasmtime_wasi::instantiate_wasi(
|
||||||
"",
|
"",
|
||||||
cx.get_global_exports(),
|
global_exports,
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
)?;
|
)?;
|
||||||
cx.name_instance(module_name, wasi_handle);
|
let wasi_instance = Instance::from_handle(&store, wasi_handle)?;
|
||||||
|
for i in module.borrow().imports().iter() {
|
||||||
|
if i.module().as_str() != module_name {
|
||||||
|
bail!("unknown import module {}", i.module().as_str());
|
||||||
}
|
}
|
||||||
let handle = cx.instantiate_module(None, &bytes)?;
|
if let Some(export) = wasi_instance.find_export_by_name(i.name().as_str()) {
|
||||||
|
imports.push(export.clone());
|
||||||
|
} else {
|
||||||
|
bail!("unknown import {}:{}", i.module().as_str(), i.name().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let instance = HostRef::new(
|
||||||
|
Instance::new(&store, &module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?
|
||||||
|
);
|
||||||
|
|
||||||
Ok(#name { cx, handle, data })
|
Ok(#name { instance, data })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -156,9 +174,8 @@ fn generate_methods(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
|||||||
let args = [
|
let args = [
|
||||||
#(#args),*
|
#(#args),*
|
||||||
];
|
];
|
||||||
let results = self.data.invoke(
|
let results = self.data.invoke_export(
|
||||||
&mut self.cx,
|
&self.instance,
|
||||||
&mut self.handle,
|
|
||||||
stringify!(#name),
|
stringify!(#name),
|
||||||
&args,
|
&args,
|
||||||
).expect("wasm execution failed");
|
).expect("wasm execution failed");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub mod __rt {
|
|||||||
pub use anyhow;
|
pub use anyhow;
|
||||||
pub use cranelift_codegen;
|
pub use cranelift_codegen;
|
||||||
pub use cranelift_native;
|
pub use cranelift_native;
|
||||||
|
pub use wasmtime_api;
|
||||||
pub use wasmtime_interface_types;
|
pub use wasmtime_interface_types;
|
||||||
pub use wasmtime_jit;
|
pub use wasmtime_jit;
|
||||||
|
|
||||||
|
|||||||
@@ -73,11 +73,11 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
|||||||
.imports()
|
.imports()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let module_name = i.module().to_string();
|
let module_name = i.module().as_str();
|
||||||
if let Some((instance, map)) = module_registry.get(&module_name) {
|
if let Some(instance) = module_registry.get(module_name) {
|
||||||
let field_name = i.name().to_string();
|
let field_name = i.name().as_str();
|
||||||
if let Some(export_index) = map.get(&field_name) {
|
if let Some(export) = instance.find_export_by_name(field_name) {
|
||||||
Ok(instance.exports()[*export_index].clone())
|
Ok(export.clone())
|
||||||
} else {
|
} else {
|
||||||
bail!(
|
bail!(
|
||||||
"import {} was not found in module {}",
|
"import {} was not found in module {}",
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
fn instantiate_module(
|
fn instantiate_module(
|
||||||
store: &HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
module_registry: &HashMap<String, Instance>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<(HostRef<Instance>, HostRef<Module>, Vec<u8>)> {
|
) -> Result<(HostRef<Instance>, HostRef<Module>, Vec<u8>)> {
|
||||||
// Read the wasm module binary either as `*.wat` or a raw binary
|
// Read the wasm module binary either as `*.wat` or a raw binary
|
||||||
@@ -342,11 +342,11 @@ fn instantiate_module(
|
|||||||
.imports()
|
.imports()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let module_name = i.module().to_string();
|
let module_name = i.module().as_str();
|
||||||
if let Some((instance, map)) = module_registry.get(&module_name) {
|
if let Some(instance) = module_registry.get(module_name) {
|
||||||
let field_name = i.name().to_string();
|
let field_name = i.name().as_str();
|
||||||
if let Some(export_index) = map.get(&field_name) {
|
if let Some(export) = instance.find_export_by_name(field_name) {
|
||||||
Ok(instance.exports()[*export_index].clone())
|
Ok(export.clone())
|
||||||
} else {
|
} else {
|
||||||
bail!(
|
bail!(
|
||||||
"Import {} was not found in module {}",
|
"Import {} was not found in module {}",
|
||||||
@@ -367,7 +367,7 @@ fn instantiate_module(
|
|||||||
|
|
||||||
fn handle_module(
|
fn handle_module(
|
||||||
store: &HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
module_registry: &HashMap<String, Instance>,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -376,14 +376,13 @@ fn handle_module(
|
|||||||
// If a function to invoke was given, invoke it.
|
// If a function to invoke was given, invoke it.
|
||||||
if let Some(f) = &args.flag_invoke {
|
if let Some(f) = &args.flag_invoke {
|
||||||
let data = ModuleData::new(&data)?;
|
let data = ModuleData::new(&data)?;
|
||||||
invoke_export(store, instance, &data, f, args)?;
|
invoke_export(instance, &data, f, args)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invoke_export(
|
fn invoke_export(
|
||||||
store: &HostRef<Store>,
|
|
||||||
instance: HostRef<Instance>,
|
instance: HostRef<Instance>,
|
||||||
data: &ModuleData,
|
data: &ModuleData,
|
||||||
name: &str,
|
name: &str,
|
||||||
@@ -434,9 +433,8 @@ fn invoke_export(
|
|||||||
|
|
||||||
// Invoke the function and then afterwards print all the results that came
|
// Invoke the function and then afterwards print all the results that came
|
||||||
// out, if there are any.
|
// out, if there are any.
|
||||||
let mut context = store.borrow().engine().borrow().create_wasmtime_context();
|
|
||||||
let results = data
|
let results = data
|
||||||
.invoke(&mut context, &mut handle, name, &values)
|
.invoke_export(&instance, name, &values)
|
||||||
.with_context(|| format!("failed to invoke `{}`", name))?;
|
.with_context(|| format!("failed to invoke `{}`", name))?;
|
||||||
if results.len() > 0 {
|
if results.len() > 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
Reference in New Issue
Block a user