Initial reorg.
This is largely the same as #305, but updated for the current tree.
This commit is contained in:
155
crates/api/src/callable.rs
Normal file
155
crates/api/src/callable.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
use crate::r#ref::HostRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::trap::Trap;
|
||||
use crate::types::FuncType;
|
||||
use crate::values::Val;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::trampoline::generate_func_export;
|
||||
use cranelift_codegen::ir;
|
||||
use wasmtime_jit::InstanceHandle;
|
||||
use wasmtime_runtime::Export;
|
||||
|
||||
pub trait Callable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
|
||||
}
|
||||
|
||||
pub(crate) trait WrappedCallable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
|
||||
fn signature(&self) -> &ir::Signature {
|
||||
match self.wasmtime_export() {
|
||||
Export::Function { signature, .. } => signature,
|
||||
_ => panic!("unexpected export type in Callable"),
|
||||
}
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle;
|
||||
fn wasmtime_export(&self) -> &Export;
|
||||
}
|
||||
|
||||
pub(crate) struct WasmtimeFn {
|
||||
store: HostRef<Store>,
|
||||
instance: InstanceHandle,
|
||||
export: Export,
|
||||
}
|
||||
|
||||
impl WasmtimeFn {
|
||||
pub fn new(store: &HostRef<Store>, instance: InstanceHandle, export: Export) -> WasmtimeFn {
|
||||
WasmtimeFn {
|
||||
store: store.clone(),
|
||||
instance,
|
||||
export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedCallable for WasmtimeFn {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
use core::cmp::max;
|
||||
use core::{mem, ptr};
|
||||
|
||||
let (vmctx, body, signature) = match self.wasmtime_export() {
|
||||
Export::Function {
|
||||
vmctx,
|
||||
address,
|
||||
signature,
|
||||
} => (*vmctx, *address, signature.clone()),
|
||||
_ => panic!("unexpected export type in Callable"),
|
||||
};
|
||||
|
||||
let value_size = mem::size_of::<u64>();
|
||||
let mut values_vec: Vec<u64> = vec![0; max(params.len(), results.len())];
|
||||
|
||||
// Store the argument values into `values_vec`.
|
||||
for (index, arg) in params.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_mut_ptr().add(index);
|
||||
|
||||
match arg {
|
||||
Val::I32(x) => ptr::write(ptr as *mut i32, *x),
|
||||
Val::I64(x) => ptr::write(ptr as *mut i64, *x),
|
||||
Val::F32(x) => ptr::write(ptr as *mut u32, *x),
|
||||
Val::F64(x) => ptr::write(ptr as *mut u64, *x),
|
||||
_ => unimplemented!("WasmtimeFn arg"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the trampoline to call for this function.
|
||||
let exec_code_buf = self
|
||||
.store
|
||||
.borrow_mut()
|
||||
.context()
|
||||
.compiler()
|
||||
.get_published_trampoline(body, &signature, value_size)
|
||||
.map_err(|_| HostRef::new(Trap::fake()))?; //was ActionError::Setup)?;
|
||||
|
||||
// Call the trampoline.
|
||||
if let Err(message) = unsafe {
|
||||
wasmtime_runtime::wasmtime_call_trampoline(
|
||||
vmctx,
|
||||
exec_code_buf,
|
||||
values_vec.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
} {
|
||||
return Err(HostRef::new(Trap::new(message)));
|
||||
}
|
||||
|
||||
// Load the return values out of `values_vec`.
|
||||
for (index, abi_param) in signature.returns.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_ptr().add(index);
|
||||
|
||||
results[index] = match abi_param.value_type {
|
||||
ir::types::I32 => Val::I32(ptr::read(ptr as *const i32)),
|
||||
ir::types::I64 => Val::I64(ptr::read(ptr as *const i64)),
|
||||
ir::types::F32 => Val::F32(ptr::read(ptr as *const u32)),
|
||||
ir::types::F64 => Val::F64(ptr::read(ptr as *const u64)),
|
||||
other => panic!("unsupported value type {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle {
|
||||
&self.instance
|
||||
}
|
||||
fn wasmtime_export(&self) -> &Export {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeCallable {
|
||||
callable: Rc<dyn Callable + 'static>,
|
||||
instance: InstanceHandle,
|
||||
export: Export,
|
||||
}
|
||||
|
||||
impl NativeCallable {
|
||||
pub(crate) fn new(
|
||||
callable: Rc<dyn Callable + 'static>,
|
||||
ft: &FuncType,
|
||||
store: &HostRef<Store>,
|
||||
) -> Self {
|
||||
let (instance, export) =
|
||||
generate_func_export(ft, &callable, store).expect("generated func");
|
||||
NativeCallable {
|
||||
callable,
|
||||
instance,
|
||||
export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedCallable for NativeCallable {
|
||||
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
|
||||
self.callable.call(params, results)
|
||||
}
|
||||
fn wasmtime_handle(&self) -> &InstanceHandle {
|
||||
&self.instance
|
||||
}
|
||||
fn wasmtime_export(&self) -> &Export {
|
||||
&self.export
|
||||
}
|
||||
}
|
||||
68
crates/api/src/context.rs
Normal file
68
crates/api/src/context.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::{RefCell, RefMut};
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use wasmtime_jit::{CompilationStrategy, Compiler, Features};
|
||||
|
||||
use cranelift_codegen::settings;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
compiler: Rc<RefCell<Compiler>>,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(compiler: Compiler, features: Features, debug_info: bool) -> Context {
|
||||
Context {
|
||||
compiler: Rc::new(RefCell::new(compiler)),
|
||||
features,
|
||||
debug_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
flags: settings::Flags,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
strategy: CompilationStrategy,
|
||||
) -> Context {
|
||||
Context::new(create_compiler(flags, strategy), features, debug_info)
|
||||
}
|
||||
|
||||
pub(crate) fn debug_info(&self) -> bool {
|
||||
self.debug_info
|
||||
}
|
||||
|
||||
pub(crate) fn compiler(&mut self) -> RefMut<Compiler> {
|
||||
self.compiler.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Context {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.compiler.as_ptr().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Context {}
|
||||
|
||||
impl PartialEq for Context {
|
||||
fn eq(&self, other: &Context) -> bool {
|
||||
Rc::ptr_eq(&self.compiler, &other.compiler)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_compiler(flags: settings::Flags, strategy: CompilationStrategy) -> Compiler {
|
||||
let isa = {
|
||||
let isa_builder =
|
||||
cranelift_native::builder().expect("host machine is not a supported target");
|
||||
isa_builder.finish(flags)
|
||||
};
|
||||
|
||||
Compiler::new(isa, strategy)
|
||||
}
|
||||
490
crates/api/src/externals.rs
Normal file
490
crates/api/src/externals.rs
Normal file
@@ -0,0 +1,490 @@
|
||||
use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable};
|
||||
use crate::r#ref::{AnyRef, HostRef};
|
||||
use crate::runtime::Store;
|
||||
use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export};
|
||||
use crate::trap::Trap;
|
||||
use crate::types::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType};
|
||||
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use core::result::Result;
|
||||
use core::slice;
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
// Externals
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Extern {
|
||||
Func(HostRef<Func>),
|
||||
Global(HostRef<Global>),
|
||||
Table(HostRef<Table>),
|
||||
Memory(HostRef<Memory>),
|
||||
}
|
||||
|
||||
impl Extern {
|
||||
pub fn func(&self) -> Option<&HostRef<Func>> {
|
||||
match self {
|
||||
Extern::Func(func) => Some(func),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn global(&self) -> Option<&HostRef<Global>> {
|
||||
match self {
|
||||
Extern::Global(global) => Some(global),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn table(&self) -> Option<&HostRef<Table>> {
|
||||
match self {
|
||||
Extern::Table(table) => Some(table),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn memory(&self) -> Option<&HostRef<Memory>> {
|
||||
match self {
|
||||
Extern::Memory(memory) => Some(memory),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> ExternType {
|
||||
match self {
|
||||
Extern::Func(ft) => ExternType::ExternFunc(ft.borrow().r#type().clone()),
|
||||
Extern::Memory(ft) => ExternType::ExternMemory(ft.borrow().r#type().clone()),
|
||||
Extern::Table(tt) => ExternType::ExternTable(tt.borrow().r#type().clone()),
|
||||
Extern::Global(gt) => ExternType::ExternGlobal(gt.borrow().r#type().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_wasmtime_export(&mut self) -> wasmtime_runtime::Export {
|
||||
match self {
|
||||
Extern::Func(f) => f.borrow().wasmtime_export().clone(),
|
||||
Extern::Global(g) => g.borrow().wasmtime_export().clone(),
|
||||
Extern::Memory(m) => m.borrow().wasmtime_export().clone(),
|
||||
Extern::Table(t) => t.borrow().wasmtime_export().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_export(
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: InstanceHandle,
|
||||
export: wasmtime_runtime::Export,
|
||||
) -> Extern {
|
||||
match export {
|
||||
wasmtime_runtime::Export::Function { .. } => Extern::Func(HostRef::new(
|
||||
Func::from_wasmtime_function(export, store, instance_handle),
|
||||
)),
|
||||
wasmtime_runtime::Export::Memory { .. } => Extern::Memory(HostRef::new(
|
||||
Memory::from_wasmtime_memory(export, store, instance_handle),
|
||||
)),
|
||||
wasmtime_runtime::Export::Global { .. } => {
|
||||
Extern::Global(HostRef::new(Global::from_wasmtime_global(export, store)))
|
||||
}
|
||||
wasmtime_runtime::Export::Table { .. } => Extern::Table(HostRef::new(
|
||||
Table::from_wasmtime_table(export, store, instance_handle),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Func>> for Extern {
|
||||
fn from(r: HostRef<Func>) -> Self {
|
||||
Extern::Func(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Global>> for Extern {
|
||||
fn from(r: HostRef<Global>) -> Self {
|
||||
Extern::Global(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Memory>> for Extern {
|
||||
fn from(r: HostRef<Memory>) -> Self {
|
||||
Extern::Memory(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Table>> for Extern {
|
||||
fn from(r: HostRef<Table>) -> Self {
|
||||
Extern::Table(r)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Func {
|
||||
_store: HostRef<Store>,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
r#type: FuncType,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
pub fn new(store: &HostRef<Store>, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
|
||||
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
|
||||
Func::from_wrapped(store, ty, callable)
|
||||
}
|
||||
|
||||
fn from_wrapped(
|
||||
store: &HostRef<Store>,
|
||||
r#type: FuncType,
|
||||
callable: Rc<dyn WrappedCallable + 'static>,
|
||||
) -> Func {
|
||||
Func {
|
||||
_store: store.clone(),
|
||||
callable,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &FuncType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
pub fn param_arity(&self) -> usize {
|
||||
self.r#type.params().len()
|
||||
}
|
||||
|
||||
pub fn result_arity(&self) -> usize {
|
||||
self.r#type.results().len()
|
||||
}
|
||||
|
||||
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, HostRef<Trap>> {
|
||||
let mut results = vec![Val::default(); self.result_arity()];
|
||||
self.callable.call(params, &mut results)?;
|
||||
Ok(results.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
self.callable.wasmtime_export()
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_function(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: InstanceHandle,
|
||||
) -> Self {
|
||||
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
||||
FuncType::from_cranelift_signature(signature.clone())
|
||||
} else {
|
||||
panic!("expected function export")
|
||||
};
|
||||
let callable = WasmtimeFn::new(store, instance_handle, export.clone());
|
||||
Func::from_wrapped(store, ty, Rc::new(callable))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Func {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Func")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Global {
|
||||
_store: HostRef<Store>,
|
||||
r#type: GlobalType,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
#[allow(dead_code)]
|
||||
wasmtime_state: Option<crate::trampoline::GlobalState>,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
pub fn new(store: &HostRef<Store>, r#type: GlobalType, val: Val) -> Global {
|
||||
let (wasmtime_export, wasmtime_state) =
|
||||
generate_global_export(&r#type, val).expect("generated global");
|
||||
Global {
|
||||
_store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_export,
|
||||
wasmtime_state: Some(wasmtime_state),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &GlobalType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
fn wasmtime_global_definition(&self) -> *mut wasmtime_runtime::VMGlobalDefinition {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Global { definition, .. } => definition,
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Val {
|
||||
let definition = unsafe { &mut *self.wasmtime_global_definition() };
|
||||
unsafe {
|
||||
match self.r#type().content() {
|
||||
ValType::I32 => Val::from(*definition.as_i32()),
|
||||
ValType::I64 => Val::from(*definition.as_i64()),
|
||||
ValType::F32 => Val::from_f32_bits(*definition.as_u32()),
|
||||
ValType::F64 => Val::from_f64_bits(*definition.as_u64()),
|
||||
_ => unimplemented!("Global::get for {:?}", self.r#type().content()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, val: Val) {
|
||||
if val.r#type() != *self.r#type().content() {
|
||||
panic!(
|
||||
"global of type {:?} cannot be set to {:?}",
|
||||
self.r#type().content(),
|
||||
val.r#type()
|
||||
);
|
||||
}
|
||||
let definition = unsafe { &mut *self.wasmtime_global_definition() };
|
||||
unsafe {
|
||||
match val {
|
||||
Val::I32(i) => *definition.as_i32_mut() = i,
|
||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||
_ => unimplemented!("Global::set for {:?}", val.r#type()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_global(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
) -> Global {
|
||||
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
|
||||
global
|
||||
} else {
|
||||
panic!("wasmtime export is not memory")
|
||||
};
|
||||
let ty = GlobalType::from_cranelift_global(global.clone());
|
||||
Global {
|
||||
_store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_export: export,
|
||||
wasmtime_state: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Table {
|
||||
store: HostRef<Store>,
|
||||
r#type: TableType,
|
||||
wasmtime_handle: InstanceHandle,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
}
|
||||
|
||||
fn get_table_item(
|
||||
handle: &InstanceHandle,
|
||||
store: &HostRef<Store>,
|
||||
table_index: cranelift_wasm::DefinedTableIndex,
|
||||
item_index: u32,
|
||||
) -> Val {
|
||||
if let Some(item) = handle.table_get(table_index, item_index) {
|
||||
from_checked_anyfunc(item, store)
|
||||
} else {
|
||||
AnyRef::null().into()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_table_item(
|
||||
handle: &mut InstanceHandle,
|
||||
store: &HostRef<Store>,
|
||||
table_index: cranelift_wasm::DefinedTableIndex,
|
||||
item_index: u32,
|
||||
val: Val,
|
||||
) -> bool {
|
||||
let item = into_checked_anyfunc(val, store);
|
||||
if let Some(item_ref) = handle.table_get_mut(table_index, item_index) {
|
||||
*item_ref = item;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn new(store: &HostRef<Store>, r#type: TableType, init: Val) -> Table {
|
||||
match r#type.element() {
|
||||
ValType::FuncRef => (),
|
||||
_ => panic!("table is not for funcref"),
|
||||
}
|
||||
let (mut wasmtime_handle, wasmtime_export) =
|
||||
generate_table_export(&r#type).expect("generated table");
|
||||
|
||||
// Initialize entries with the init value.
|
||||
match wasmtime_export {
|
||||
wasmtime_runtime::Export::Table { definition, .. } => {
|
||||
let index = wasmtime_handle.table_index(unsafe { &*definition });
|
||||
let len = unsafe { (*definition).current_elements };
|
||||
for i in 0..len {
|
||||
let _success =
|
||||
set_table_item(&mut wasmtime_handle, store, index, i, init.clone());
|
||||
assert!(_success);
|
||||
}
|
||||
}
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
|
||||
Table {
|
||||
store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_handle,
|
||||
wasmtime_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &TableType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
fn wasmtime_table_index(&self) -> cranelift_wasm::DefinedTableIndex {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Table { definition, .. } => {
|
||||
self.wasmtime_handle.table_index(unsafe { &*definition })
|
||||
}
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, index: u32) -> Val {
|
||||
let table_index = self.wasmtime_table_index();
|
||||
get_table_item(&self.wasmtime_handle, &self.store, table_index, index)
|
||||
}
|
||||
|
||||
pub fn set(&self, index: u32, val: Val) -> bool {
|
||||
let table_index = self.wasmtime_table_index();
|
||||
let mut wasmtime_handle = self.wasmtime_handle.clone();
|
||||
set_table_item(&mut wasmtime_handle, &self.store, table_index, index, val)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Table { definition, .. } => unsafe {
|
||||
(*definition).current_elements
|
||||
},
|
||||
_ => panic!("global definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: u32, init: Val) -> bool {
|
||||
let index = self.wasmtime_table_index();
|
||||
if let Some(len) = self.wasmtime_handle.table_grow(index, delta) {
|
||||
let mut wasmtime_handle = self.wasmtime_handle.clone();
|
||||
for i in 0..delta {
|
||||
let i = len - (delta - i);
|
||||
let _success =
|
||||
set_table_item(&mut wasmtime_handle, &self.store, index, i, init.clone());
|
||||
assert!(_success);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_table(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: wasmtime_runtime::InstanceHandle,
|
||||
) -> Table {
|
||||
let table = if let wasmtime_runtime::Export::Table { ref table, .. } = export {
|
||||
table
|
||||
} else {
|
||||
panic!("wasmtime export is not table")
|
||||
};
|
||||
let ty = TableType::from_cranelift_table(table.table.clone());
|
||||
Table {
|
||||
store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_handle: instance_handle,
|
||||
wasmtime_export: export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Memory {
|
||||
_store: HostRef<Store>,
|
||||
r#type: MemoryType,
|
||||
wasmtime_handle: InstanceHandle,
|
||||
wasmtime_export: wasmtime_runtime::Export,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new(store: &HostRef<Store>, r#type: MemoryType) -> Memory {
|
||||
let (wasmtime_handle, wasmtime_export) =
|
||||
generate_memory_export(&r#type).expect("generated memory");
|
||||
Memory {
|
||||
_store: store.clone(),
|
||||
r#type,
|
||||
wasmtime_handle,
|
||||
wasmtime_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> &MemoryType {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
fn wasmtime_memory_definition(&self) -> *mut wasmtime_runtime::VMMemoryDefinition {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Memory { definition, .. } => definition,
|
||||
_ => panic!("memory definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
// Marked unsafe due to posibility that wasmtime can resize internal memory
|
||||
// from other threads.
|
||||
pub unsafe fn data(&self) -> &mut [u8] {
|
||||
let definition = &*self.wasmtime_memory_definition();
|
||||
slice::from_raw_parts_mut(definition.base, definition.current_length)
|
||||
}
|
||||
|
||||
pub fn data_ptr(&self) -> *mut u8 {
|
||||
unsafe { (*self.wasmtime_memory_definition()).base }
|
||||
}
|
||||
|
||||
pub fn data_size(&self) -> usize {
|
||||
unsafe { (*self.wasmtime_memory_definition()).current_length }
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, delta: u32) -> bool {
|
||||
match self.wasmtime_export {
|
||||
wasmtime_runtime::Export::Memory { definition, .. } => {
|
||||
let definition = unsafe { &(*definition) };
|
||||
let index = self.wasmtime_handle.memory_index(definition);
|
||||
self.wasmtime_handle.memory_grow(index, delta).is_some()
|
||||
}
|
||||
_ => panic!("memory definition not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
|
||||
&self.wasmtime_export
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime_memory(
|
||||
export: wasmtime_runtime::Export,
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: wasmtime_runtime::InstanceHandle,
|
||||
) -> Memory {
|
||||
let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export {
|
||||
memory
|
||||
} else {
|
||||
panic!("wasmtime export is not memory")
|
||||
};
|
||||
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
|
||||
Memory {
|
||||
_store: store.clone(),
|
||||
r#type: ty,
|
||||
wasmtime_handle: instance_handle,
|
||||
wasmtime_export: export,
|
||||
}
|
||||
}
|
||||
}
|
||||
148
crates/api/src/instance.rs
Normal file
148
crates/api/src/instance.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use crate::context::Context;
|
||||
use crate::externals::Extern;
|
||||
use crate::module::Module;
|
||||
use crate::r#ref::HostRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::{HashMap, HashSet};
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use anyhow::Result;
|
||||
use core::cell::RefCell;
|
||||
|
||||
use wasmtime_jit::{instantiate, Resolver};
|
||||
use wasmtime_runtime::{Export, InstanceHandle};
|
||||
|
||||
struct SimpleResolver {
|
||||
imports: Vec<(String, String, Extern)>,
|
||||
}
|
||||
|
||||
impl Resolver for SimpleResolver {
|
||||
fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
|
||||
// TODO speedup lookup
|
||||
self.imports
|
||||
.iter_mut()
|
||||
.find(|(n, f, _)| name == n && field == f)
|
||||
.map(|(_, _, e)| e.get_wasmtime_export())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_in_context(
|
||||
data: &[u8],
|
||||
imports: Vec<(String, String, Extern)>,
|
||||
mut context: Context,
|
||||
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||
) -> Result<(InstanceHandle, HashSet<Context>)> {
|
||||
let mut contexts = HashSet::new();
|
||||
let debug_info = context.debug_info();
|
||||
let mut resolver = SimpleResolver { imports };
|
||||
let instance = instantiate(
|
||||
&mut context.compiler(),
|
||||
data,
|
||||
&mut resolver,
|
||||
exports,
|
||||
debug_info,
|
||||
)?;
|
||||
contexts.insert(context);
|
||||
Ok((instance, contexts))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
instance_handle: InstanceHandle,
|
||||
|
||||
// We need to keep CodeMemory alive.
|
||||
contexts: HashSet<Context>,
|
||||
|
||||
exports: Box<[Extern]>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub fn new(
|
||||
store: &HostRef<Store>,
|
||||
module: &HostRef<Module>,
|
||||
externs: &[Extern],
|
||||
) -> Result<Instance> {
|
||||
let context = store.borrow_mut().context().clone();
|
||||
let exports = store.borrow_mut().global_exports().clone();
|
||||
let imports = module
|
||||
.borrow()
|
||||
.imports()
|
||||
.iter()
|
||||
.zip(externs.iter())
|
||||
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
let (mut instance_handle, contexts) =
|
||||
instantiate_in_context(module.borrow().binary(), imports, context, exports)?;
|
||||
|
||||
let exports = {
|
||||
let module = module.borrow();
|
||||
let mut exports = Vec::with_capacity(module.exports().len());
|
||||
for export in module.exports() {
|
||||
let name = export.name().to_string();
|
||||
let export = instance_handle.lookup(&name).expect("export");
|
||||
exports.push(Extern::from_wasmtime_export(
|
||||
store,
|
||||
instance_handle.clone(),
|
||||
export,
|
||||
));
|
||||
}
|
||||
exports.into_boxed_slice()
|
||||
};
|
||||
Ok(Instance {
|
||||
instance_handle,
|
||||
contexts,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn exports(&self) -> &[Extern] {
|
||||
&self.exports
|
||||
}
|
||||
|
||||
pub fn from_handle(
|
||||
store: &HostRef<Store>,
|
||||
instance_handle: InstanceHandle,
|
||||
) -> Result<(Instance, HashMap<String, usize>)> {
|
||||
let contexts = HashSet::new();
|
||||
|
||||
let mut exports = Vec::new();
|
||||
let mut export_names_map = HashMap::new();
|
||||
let mut mutable = instance_handle.clone();
|
||||
for (name, _) in instance_handle.clone().exports() {
|
||||
let export = mutable.lookup(name).expect("export");
|
||||
if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
||||
// HACK ensure all handles, instantiated outside Store, present in
|
||||
// the store's SignatureRegistry, e.g. WASI instances that are
|
||||
// imported into this store using the from_handle() method.
|
||||
let _ = store.borrow_mut().register_cranelift_signature(signature);
|
||||
}
|
||||
export_names_map.insert(name.to_owned(), exports.len());
|
||||
exports.push(Extern::from_wasmtime_export(
|
||||
store,
|
||||
instance_handle.clone(),
|
||||
export.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok((
|
||||
Instance {
|
||||
instance_handle,
|
||||
contexts,
|
||||
exports: exports.into_boxed_slice(),
|
||||
},
|
||||
export_names_map,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> &InstanceHandle {
|
||||
&self.instance_handle
|
||||
}
|
||||
|
||||
pub fn get_wasmtime_memory(&self) -> Option<wasmtime_runtime::Export> {
|
||||
let mut instance_handle = self.instance_handle.clone();
|
||||
instance_handle.lookup("memory")
|
||||
}
|
||||
}
|
||||
35
crates/api/src/lib.rs
Normal file
35
crates/api/src/lib.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//! Wasmtime embed API. Based on wasm-c-api.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
mod callable;
|
||||
mod context;
|
||||
mod externals;
|
||||
mod instance;
|
||||
mod module;
|
||||
mod r#ref;
|
||||
mod runtime;
|
||||
mod trampoline;
|
||||
mod trap;
|
||||
mod types;
|
||||
mod values;
|
||||
|
||||
pub mod wasm;
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
pub use crate::callable::Callable;
|
||||
pub use crate::externals::*;
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::module::Module;
|
||||
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
|
||||
pub use crate::runtime::{Config, Engine, Store};
|
||||
pub use crate::trap::Trap;
|
||||
pub use crate::types::*;
|
||||
pub use crate::values::*;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashbrown::{hash_map, HashMap, HashSet};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{hash_map, HashMap, HashSet};
|
||||
206
crates/api/src/module.rs
Normal file
206
crates/api/src/module.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use crate::r#ref::HostRef;
|
||||
use crate::runtime::Store;
|
||||
use crate::types::{
|
||||
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
|
||||
TableType, ValType,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use anyhow::Result;
|
||||
|
||||
use wasmparser::{validate, ExternalKind, ImportSectionEntryType, ModuleReader, SectionCode};
|
||||
|
||||
fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType {
|
||||
assert!(!mt.shared);
|
||||
MemoryType::new(Limits::new(
|
||||
mt.limits.initial,
|
||||
mt.limits.maximum.unwrap_or(::core::u32::MAX),
|
||||
))
|
||||
}
|
||||
|
||||
fn into_global_type(gt: &wasmparser::GlobalType) -> GlobalType {
|
||||
let mutability = if gt.mutable {
|
||||
Mutability::Var
|
||||
} else {
|
||||
Mutability::Const
|
||||
};
|
||||
GlobalType::new(into_valtype(>.content_type), mutability)
|
||||
}
|
||||
|
||||
fn into_valtype(ty: &wasmparser::Type) -> ValType {
|
||||
use wasmparser::Type::*;
|
||||
match ty {
|
||||
I32 => ValType::I32,
|
||||
I64 => ValType::I64,
|
||||
F32 => ValType::F32,
|
||||
F64 => ValType::F64,
|
||||
V128 => ValType::V128,
|
||||
AnyFunc => ValType::FuncRef,
|
||||
AnyRef => ValType::AnyRef,
|
||||
_ => unimplemented!("types in into_valtype"),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_func_type(mt: wasmparser::FuncType) -> FuncType {
|
||||
assert!(mt.form == wasmparser::Type::Func);
|
||||
let params = mt.params.iter().map(into_valtype).collect::<Vec<_>>();
|
||||
let returns = mt.returns.iter().map(into_valtype).collect::<Vec<_>>();
|
||||
FuncType::new(params.into_boxed_slice(), returns.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn into_table_type(tt: wasmparser::TableType) -> TableType {
|
||||
assert!(
|
||||
tt.element_type == wasmparser::Type::AnyFunc || tt.element_type == wasmparser::Type::AnyRef
|
||||
);
|
||||
let ty = into_valtype(&tt.element_type);
|
||||
let limits = Limits::new(
|
||||
tt.limits.initial,
|
||||
tt.limits.maximum.unwrap_or(::core::u32::MAX),
|
||||
);
|
||||
TableType::new(ty, limits)
|
||||
}
|
||||
|
||||
fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[ExportType]>)> {
|
||||
let mut reader = ModuleReader::new(binary)?;
|
||||
let mut imports = Vec::new();
|
||||
let mut exports = Vec::new();
|
||||
let mut memories = Vec::new();
|
||||
let mut tables = Vec::new();
|
||||
let mut func_sig = Vec::new();
|
||||
let mut sigs = Vec::new();
|
||||
let mut globals = Vec::new();
|
||||
while !reader.eof() {
|
||||
let section = reader.read()?;
|
||||
match section.code {
|
||||
SectionCode::Memory => {
|
||||
let section = section.get_memory_section_reader()?;
|
||||
memories.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
memories.push(into_memory_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Type => {
|
||||
let section = section.get_type_section_reader()?;
|
||||
sigs.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
sigs.push(into_func_type(entry?));
|
||||
}
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let section = section.get_function_section_reader()?;
|
||||
func_sig.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
func_sig.push(entry?);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let section = section.get_global_section_reader()?;
|
||||
globals.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
globals.push(into_global_type(&entry?.ty));
|
||||
}
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let section = section.get_table_section_reader()?;
|
||||
tables.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
tables.push(into_table_type(entry?))
|
||||
}
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let section = section.get_import_section_reader()?;
|
||||
imports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let module = String::from(entry.module).into();
|
||||
let name = String::from(entry.field).into();
|
||||
let r#type = match entry.ty {
|
||||
ImportSectionEntryType::Function(index) => {
|
||||
func_sig.push(index);
|
||||
let sig = &sigs[index as usize];
|
||||
ExternType::ExternFunc(sig.clone())
|
||||
}
|
||||
ImportSectionEntryType::Table(tt) => {
|
||||
let table = into_table_type(tt);
|
||||
tables.push(table.clone());
|
||||
ExternType::ExternTable(table)
|
||||
}
|
||||
ImportSectionEntryType::Memory(mt) => {
|
||||
let memory = into_memory_type(mt);
|
||||
memories.push(memory.clone());
|
||||
ExternType::ExternMemory(memory)
|
||||
}
|
||||
ImportSectionEntryType::Global(gt) => {
|
||||
let global = into_global_type(>);
|
||||
globals.push(global.clone());
|
||||
ExternType::ExternGlobal(global)
|
||||
}
|
||||
};
|
||||
imports.push(ImportType::new(module, name, r#type));
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let section = section.get_export_section_reader()?;
|
||||
exports.reserve_exact(section.get_count() as usize);
|
||||
for entry in section {
|
||||
let entry = entry?;
|
||||
let name = String::from(entry.field).into();
|
||||
let r#type = match entry.kind {
|
||||
ExternalKind::Function => {
|
||||
let sig_index = func_sig[entry.index as usize] as usize;
|
||||
let sig = &sigs[sig_index];
|
||||
ExternType::ExternFunc(sig.clone())
|
||||
}
|
||||
ExternalKind::Table => {
|
||||
ExternType::ExternTable(tables[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Memory => {
|
||||
ExternType::ExternMemory(memories[entry.index as usize].clone())
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExternType::ExternGlobal(globals[entry.index as usize].clone())
|
||||
}
|
||||
};
|
||||
exports.push(ExportType::new(name, r#type));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// skip other sections
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((imports.into_boxed_slice(), exports.into_boxed_slice()))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Module {
|
||||
store: HostRef<Store>,
|
||||
binary: Box<[u8]>,
|
||||
imports: Box<[ImportType]>,
|
||||
exports: Box<[ExportType]>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(store: &HostRef<Store>, binary: &[u8]) -> Result<Module> {
|
||||
let (imports, exports) = read_imports_and_exports(binary)?;
|
||||
Ok(Module {
|
||||
store: store.clone(),
|
||||
binary: binary.into(),
|
||||
imports,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
pub(crate) fn binary(&self) -> &[u8] {
|
||||
&self.binary
|
||||
}
|
||||
pub fn validate(_store: &Store, binary: &[u8]) -> bool {
|
||||
validate(binary, None).is_ok()
|
||||
}
|
||||
pub fn imports(&self) -> &[ImportType] {
|
||||
&self.imports
|
||||
}
|
||||
pub fn exports(&self) -> &[ExportType] {
|
||||
&self.exports
|
||||
}
|
||||
}
|
||||
211
crates/api/src/ref.rs
Normal file
211
crates/api/src/ref.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::any::Any;
|
||||
use core::cell::{self, RefCell};
|
||||
use core::fmt;
|
||||
|
||||
pub trait HostInfo {
|
||||
fn finalize(&mut self) {}
|
||||
}
|
||||
|
||||
trait InternalRefBase: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>>;
|
||||
fn set_host_info(&self, info: Option<Box<dyn HostInfo>>);
|
||||
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InternalRef(Rc<dyn InternalRefBase>);
|
||||
|
||||
impl InternalRef {
|
||||
pub fn is_ref<T: 'static>(&self) -> bool {
|
||||
let r = self.0.as_any();
|
||||
Any::is::<HostRef<T>>(r)
|
||||
}
|
||||
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
|
||||
let r = self.0.as_any();
|
||||
r.downcast_ref::<HostRef<T>>()
|
||||
.expect("reference is not T type")
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct AnyAndHostInfo {
|
||||
any: Box<dyn Any>,
|
||||
host_info: Option<Box<dyn HostInfo>>,
|
||||
}
|
||||
|
||||
impl Drop for AnyAndHostInfo {
|
||||
fn drop(&mut self) {
|
||||
if let Some(info) = &mut self.host_info {
|
||||
info.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AnyRef {
|
||||
Null,
|
||||
Ref(InternalRef),
|
||||
Other(OtherRef),
|
||||
}
|
||||
|
||||
impl AnyRef {
|
||||
pub fn new(data: Box<dyn Any>) -> Self {
|
||||
let info = AnyAndHostInfo {
|
||||
any: data,
|
||||
host_info: None,
|
||||
};
|
||||
AnyRef::Other(OtherRef(Rc::new(RefCell::new(info))))
|
||||
}
|
||||
|
||||
pub fn null() -> Self {
|
||||
AnyRef::Null
|
||||
}
|
||||
|
||||
pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
|
||||
match self {
|
||||
AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
|
||||
_ => panic!("expected AnyRef::Other"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ptr_eq(&self, other: &AnyRef) -> bool {
|
||||
match (self, other) {
|
||||
(AnyRef::Null, AnyRef::Null) => true,
|
||||
(AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => {
|
||||
a.ptr_eq(b.as_ref())
|
||||
}
|
||||
(AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>> {
|
||||
match self {
|
||||
AnyRef::Null => panic!("null"),
|
||||
AnyRef::Ref(r) => r.0.host_info(),
|
||||
AnyRef::Other(r) => {
|
||||
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
|
||||
if info.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_host_info(&self, info: Option<Box<dyn HostInfo>>) {
|
||||
match self {
|
||||
AnyRef::Null => panic!("null"),
|
||||
AnyRef::Ref(r) => r.0.set_host_info(info),
|
||||
AnyRef::Other(r) => {
|
||||
r.0.borrow_mut().host_info = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AnyRef::Null => write!(f, "null"),
|
||||
AnyRef::Ref(_) => write!(f, "anyref"),
|
||||
AnyRef::Other(_) => write!(f, "other ref"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentBox<T> {
|
||||
content: T,
|
||||
host_info: Option<Box<dyn HostInfo>>,
|
||||
anyref_data: Weak<dyn InternalRefBase>,
|
||||
}
|
||||
|
||||
impl<T> Drop for ContentBox<T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(info) = &mut self.host_info {
|
||||
info.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
|
||||
|
||||
impl<T: 'static> HostRef<T> {
|
||||
pub fn new(item: T) -> HostRef<T> {
|
||||
let anyref_data: Weak<HostRef<T>> = Weak::new();
|
||||
let content = ContentBox {
|
||||
content: item,
|
||||
host_info: None,
|
||||
anyref_data,
|
||||
};
|
||||
HostRef(Rc::new(RefCell::new(content)))
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> cell::Ref<T> {
|
||||
cell::Ref::map(self.0.borrow(), |b| &b.content)
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> cell::RefMut<T> {
|
||||
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content)
|
||||
}
|
||||
|
||||
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
|
||||
pub fn anyref(&self) -> AnyRef {
|
||||
let r = self.0.borrow_mut().anyref_data.upgrade();
|
||||
if let Some(r) = r {
|
||||
return AnyRef::Ref(InternalRef(r));
|
||||
}
|
||||
let anyref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
|
||||
self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data);
|
||||
AnyRef::Ref(InternalRef(anyref_data))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> InternalRefBase for HostRef<T> {
|
||||
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref() {
|
||||
self.ptr_eq(other)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>> {
|
||||
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
|
||||
if info.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
|
||||
}
|
||||
|
||||
fn set_host_info(&self, info: Option<Box<dyn HostInfo>>) {
|
||||
self.0.borrow_mut().host_info = info;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for HostRef<T> {
|
||||
fn clone(&self) -> HostRef<T> {
|
||||
HostRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Ref(")?;
|
||||
self.0.borrow().content.fmt(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
154
crates/api/src/runtime.rs
Normal file
154
crates/api/src/runtime.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use crate::HashMap;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::String;
|
||||
use core::cell::RefCell;
|
||||
|
||||
use crate::context::{create_compiler, Context};
|
||||
use crate::r#ref::HostRef;
|
||||
|
||||
use cranelift_codegen::{ir, settings};
|
||||
use wasmtime_jit::{CompilationStrategy, Features};
|
||||
|
||||
// Runtime Environment
|
||||
|
||||
// Configuration
|
||||
|
||||
fn default_flags() -> settings::Flags {
|
||||
let flag_builder = settings::builder();
|
||||
settings::Flags::new(flag_builder)
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
flags: settings::Flags,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
strategy: CompilationStrategy,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default() -> Config {
|
||||
Config {
|
||||
debug_info: false,
|
||||
features: Default::default(),
|
||||
flags: default_flags(),
|
||||
strategy: CompilationStrategy::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
flags: settings::Flags,
|
||||
features: Features,
|
||||
debug_info: bool,
|
||||
strategy: CompilationStrategy,
|
||||
) -> Config {
|
||||
Config {
|
||||
flags,
|
||||
features,
|
||||
debug_info,
|
||||
strategy,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn debug_info(&self) -> bool {
|
||||
self.debug_info
|
||||
}
|
||||
|
||||
pub(crate) fn flags(&self) -> &settings::Flags {
|
||||
&self.flags
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
pub(crate) fn strategy(&self) -> CompilationStrategy {
|
||||
self.strategy
|
||||
}
|
||||
}
|
||||
|
||||
// Engine
|
||||
|
||||
pub struct Engine {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new(config: Config) -> Engine {
|
||||
Engine { config }
|
||||
}
|
||||
|
||||
pub fn default() -> Engine {
|
||||
Engine::new(Config::default())
|
||||
}
|
||||
|
||||
pub(crate) fn 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
|
||||
|
||||
pub struct Store {
|
||||
engine: HostRef<Engine>,
|
||||
context: Context,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||
signature_cache: HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn new(engine: &HostRef<Engine>) -> Store {
|
||||
let flags = engine.borrow().config().flags().clone();
|
||||
let features = engine.borrow().config().features().clone();
|
||||
let debug_info = engine.borrow().config().debug_info();
|
||||
let strategy = engine.borrow().config().strategy();
|
||||
Store {
|
||||
engine: engine.clone(),
|
||||
context: Context::create(flags, features, debug_info, strategy),
|
||||
global_exports: Rc::new(RefCell::new(HashMap::new())),
|
||||
signature_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn engine(&self) -> &HostRef<Engine> {
|
||||
&self.engine
|
||||
}
|
||||
|
||||
pub(crate) fn context(&mut self) -> &mut Context {
|
||||
&mut self.context
|
||||
}
|
||||
|
||||
// Specific to wasmtime: hack to pass memory around to wasi
|
||||
pub fn global_exports(
|
||||
&self,
|
||||
) -> &Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> {
|
||||
&self.global_exports
|
||||
}
|
||||
|
||||
pub(crate) fn register_cranelift_signature(
|
||||
&mut self,
|
||||
signature: &ir::Signature,
|
||||
) -> wasmtime_runtime::VMSharedSignatureIndex {
|
||||
use crate::hash_map::Entry;
|
||||
let index = self.context().compiler().signatures().register(signature);
|
||||
match self.signature_cache.entry(index) {
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(signature.clone());
|
||||
}
|
||||
Entry::Occupied(_) => (),
|
||||
}
|
||||
index
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_cranelift_signature(
|
||||
&self,
|
||||
type_index: wasmtime_runtime::VMSharedSignatureIndex,
|
||||
) -> Option<&ir::Signature> {
|
||||
self.signature_cache.get(&type_index)
|
||||
}
|
||||
}
|
||||
62
crates/api/src/trampoline/create_handle.rs
Normal file
62
crates/api/src/trampoline/create_handle.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
//! Support for a calling of an imported function.
|
||||
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::DefinedFuncIndex;
|
||||
//use target_lexicon::HOST;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody};
|
||||
|
||||
use crate::{HashMap, HashSet};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::any::Any;
|
||||
use core::cell::{RefCell, RefMut};
|
||||
|
||||
use crate::runtime::Store;
|
||||
|
||||
pub(crate) fn create_handle(
|
||||
module: Module,
|
||||
signature_registry: Option<RefMut<Store>>,
|
||||
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
|
||||
state: Box<dyn Any>,
|
||||
) -> Result<InstanceHandle> {
|
||||
let global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> =
|
||||
Rc::new(RefCell::new(HashMap::new()));
|
||||
|
||||
let imports = Imports::new(
|
||||
HashSet::new(),
|
||||
PrimaryMap::new(),
|
||||
PrimaryMap::new(),
|
||||
PrimaryMap::new(),
|
||||
PrimaryMap::new(),
|
||||
);
|
||||
let data_initializers = Vec::new();
|
||||
|
||||
// Compute indices into the shared signature table.
|
||||
let signatures = signature_registry
|
||||
.and_then(|mut signature_registry| {
|
||||
Some(
|
||||
module
|
||||
.signatures
|
||||
.values()
|
||||
.map(|sig| signature_registry.register_cranelift_signature(sig))
|
||||
.collect::<PrimaryMap<_, _>>(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| PrimaryMap::new());
|
||||
|
||||
Ok(InstanceHandle::new(
|
||||
Rc::new(module),
|
||||
global_exports,
|
||||
finished_functions.into_boxed_slice(),
|
||||
imports,
|
||||
&data_initializers,
|
||||
signatures.into_boxed_slice(),
|
||||
None,
|
||||
state,
|
||||
)
|
||||
.expect("instance"))
|
||||
}
|
||||
291
crates/api/src/trampoline/func.rs
Normal file
291
crates/api/src/trampoline/func.rs
Normal file
@@ -0,0 +1,291 @@
|
||||
//! Support for a calling of an imported function.
|
||||
|
||||
use crate::r#ref::HostRef;
|
||||
use anyhow::Result;
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode};
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_codegen::{binemit, ir, isa};
|
||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
||||
use wasmtime_environ::{CompiledFunction, Export, Module};
|
||||
use wasmtime_jit::CodeMemory;
|
||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp;
|
||||
|
||||
use crate::{Callable, FuncType, Store, Trap, Val};
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
|
||||
struct TrampolineState {
|
||||
func: Rc<dyn Callable + 'static>,
|
||||
trap: Option<HostRef<Trap>>,
|
||||
#[allow(dead_code)]
|
||||
code_memory: CodeMemory,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i64) -> u32 {
|
||||
let mut instance = InstanceHandle::from_vmctx(vmctx);
|
||||
|
||||
let (args, returns_len) = {
|
||||
let module = instance.module_ref();
|
||||
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
|
||||
|
||||
let mut args = Vec::new();
|
||||
for i in 1..signature.params.len() {
|
||||
args.push(Val::read_value_from(
|
||||
values_vec.offset(i as isize - 1),
|
||||
signature.params[i].value_type,
|
||||
))
|
||||
}
|
||||
(args, signature.returns.len())
|
||||
};
|
||||
|
||||
let mut returns = vec![Val::default(); returns_len];
|
||||
let func = &instance
|
||||
.host_state()
|
||||
.downcast_mut::<TrampolineState>()
|
||||
.expect("state")
|
||||
.func;
|
||||
|
||||
match func.call(&args, &mut returns) {
|
||||
Ok(()) => {
|
||||
for i in 0..returns_len {
|
||||
// TODO check signature.returns[i].value_type ?
|
||||
returns[i].write_value_to(values_vec.offset(i as isize));
|
||||
}
|
||||
0
|
||||
}
|
||||
Err(trap) => {
|
||||
// TODO read custom exception
|
||||
InstanceHandle::from_vmctx(vmctx)
|
||||
.host_state()
|
||||
.downcast_mut::<TrampolineState>()
|
||||
.expect("state")
|
||||
.trap = Some(trap);
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a Callable.
|
||||
fn make_trampoline(
|
||||
isa: &dyn isa::TargetIsa,
|
||||
code_memory: &mut CodeMemory,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
call_id: u32,
|
||||
signature: &ir::Signature,
|
||||
) -> *const VMFunctionBody {
|
||||
// Mostly reverse copy of the similar method from wasmtime's
|
||||
// wasmtime-jit/src/compiler.rs.
|
||||
let pointer_type = isa.pointer_type();
|
||||
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
|
||||
|
||||
// Add the `vmctx` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::special(
|
||||
pointer_type,
|
||||
ir::ArgumentPurpose::VMContext,
|
||||
));
|
||||
|
||||
// Add the `call_id` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(types::I32));
|
||||
|
||||
// Add the `values_vec` parameter.
|
||||
stub_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Add error/trap return.
|
||||
stub_sig.returns.push(ir::AbiParam::new(types::I32));
|
||||
|
||||
let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32;
|
||||
|
||||
let mut context = Context::new();
|
||||
context.func =
|
||||
ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone());
|
||||
|
||||
let ss = context.func.create_stack_slot(StackSlotData::new(
|
||||
StackSlotKind::ExplicitSlot,
|
||||
values_vec_len,
|
||||
));
|
||||
let value_size = 8;
|
||||
|
||||
{
|
||||
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
|
||||
let block0 = builder.create_ebb();
|
||||
|
||||
builder.append_ebb_params_for_function_params(block0);
|
||||
builder.switch_to_block(block0);
|
||||
builder.seal_block(block0);
|
||||
|
||||
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
for i in 1..signature.params.len() {
|
||||
if i == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let val = builder.func.dfg.ebb_params(block0)[i];
|
||||
builder.ins().store(
|
||||
mflags,
|
||||
val,
|
||||
values_vec_ptr_val,
|
||||
((i - 1) * value_size) as i32,
|
||||
);
|
||||
}
|
||||
|
||||
let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0];
|
||||
let call_id_val = builder.ins().iconst(types::I32, call_id as i64);
|
||||
|
||||
let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val];
|
||||
|
||||
let new_sig = builder.import_signature(stub_sig.clone());
|
||||
|
||||
let callee_value = builder
|
||||
.ins()
|
||||
.iconst(pointer_type, stub_fn as *const VMFunctionBody as i64);
|
||||
let call = builder
|
||||
.ins()
|
||||
.call_indirect(new_sig, callee_value, &callee_args);
|
||||
|
||||
let call_result = builder.func.dfg.inst_results(call)[0];
|
||||
builder.ins().trapnz(call_result, TrapCode::User(0));
|
||||
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
let mut results = Vec::new();
|
||||
for (i, r) in signature.returns.iter().enumerate() {
|
||||
let load = builder.ins().load(
|
||||
r.value_type,
|
||||
mflags,
|
||||
values_vec_ptr_val,
|
||||
(i * value_size) as i32,
|
||||
);
|
||||
results.push(load);
|
||||
}
|
||||
builder.ins().return_(&results);
|
||||
builder.finalize()
|
||||
}
|
||||
|
||||
let mut code_buf: Vec<u8> = Vec::new();
|
||||
let mut reloc_sink = RelocSink {};
|
||||
let mut trap_sink = binemit::NullTrapSink {};
|
||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
.expect("compile_and_emit");
|
||||
|
||||
let mut unwind_info = Vec::new();
|
||||
context.emit_unwind_info(isa, &mut unwind_info);
|
||||
|
||||
code_memory
|
||||
.allocate_for_function(&CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: context.func.jt_offsets,
|
||||
unwind_info,
|
||||
})
|
||||
.expect("allocate_for_function")
|
||||
.as_ptr()
|
||||
}
|
||||
|
||||
pub fn create_handle_with_function(
|
||||
ft: &FuncType,
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
store: &HostRef<Store>,
|
||||
) -> Result<InstanceHandle> {
|
||||
let sig = ft.get_cranelift_signature().clone();
|
||||
|
||||
let isa = {
|
||||
let isa_builder =
|
||||
cranelift_native::builder().expect("host machine is not a supported target");
|
||||
let flag_builder = cranelift_codegen::settings::builder();
|
||||
isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))
|
||||
};
|
||||
|
||||
let mut fn_builder_ctx = FunctionBuilderContext::new();
|
||||
let mut module = Module::new();
|
||||
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =
|
||||
PrimaryMap::new();
|
||||
let mut code_memory = CodeMemory::new();
|
||||
|
||||
//let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||
//let call_conv = isa::CallConv::triple_default(&HOST);
|
||||
|
||||
let sig_id = module.signatures.push(sig.clone());
|
||||
let func_id = module.functions.push(sig_id);
|
||||
module
|
||||
.exports
|
||||
.insert("trampoline".to_string(), Export::Function(func_id));
|
||||
let trampoline = make_trampoline(
|
||||
isa.as_ref(),
|
||||
&mut code_memory,
|
||||
&mut fn_builder_ctx,
|
||||
func_id.index() as u32,
|
||||
&sig,
|
||||
);
|
||||
code_memory.publish();
|
||||
|
||||
finished_functions.push(trampoline);
|
||||
|
||||
let trampoline_state = TrampolineState {
|
||||
func: func.clone(),
|
||||
trap: None,
|
||||
code_memory,
|
||||
};
|
||||
|
||||
create_handle(
|
||||
module,
|
||||
Some(store.borrow_mut()),
|
||||
finished_functions,
|
||||
Box::new(trampoline_state),
|
||||
)
|
||||
}
|
||||
|
||||
/// We don't expect trampoline compilation to produce any relocations, so
|
||||
/// this `RelocSink` just asserts that it doesn't recieve any.
|
||||
struct RelocSink {}
|
||||
|
||||
impl binemit::RelocSink for RelocSink {
|
||||
fn reloc_ebb(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_ebb_offset: binemit::CodeOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce ebb relocs");
|
||||
}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_name: &ir::ExternalName,
|
||||
_addend: binemit::Addend,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce external symbol relocs");
|
||||
}
|
||||
fn reloc_constant(
|
||||
&mut self,
|
||||
_code_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_constant_offset: ir::ConstantOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce constant relocs");
|
||||
}
|
||||
fn reloc_jt(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_jt: ir::JumpTable,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce jump table relocs");
|
||||
}
|
||||
}
|
||||
46
crates/api/src/trampoline/global.rs
Normal file
46
crates/api/src/trampoline/global.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use alloc::boxed::Box;
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::{InstanceHandle, VMGlobalDefinition};
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{GlobalType, Mutability, Val};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct GlobalState {
|
||||
definition: Box<VMGlobalDefinition>,
|
||||
handle: InstanceHandle,
|
||||
}
|
||||
|
||||
pub fn create_global(gt: &GlobalType, val: Val) -> Result<(wasmtime_runtime::Export, GlobalState)> {
|
||||
let mut definition = Box::new(VMGlobalDefinition::new());
|
||||
unsafe {
|
||||
match val {
|
||||
Val::I32(i) => *definition.as_i32_mut() = i,
|
||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||
_ => unimplemented!("create_global for {:?}", gt),
|
||||
}
|
||||
}
|
||||
|
||||
let global = cranelift_wasm::Global {
|
||||
ty: gt.content().get_cranelift_type(),
|
||||
mutability: match gt.mutability() {
|
||||
Mutability::Const => false,
|
||||
Mutability::Var => true,
|
||||
},
|
||||
initializer: cranelift_wasm::GlobalInit::Import, // TODO is it right?
|
||||
};
|
||||
let mut handle =
|
||||
create_handle(Module::new(), None, PrimaryMap::new(), Box::new(())).expect("handle");
|
||||
Ok((
|
||||
wasmtime_runtime::Export::Global {
|
||||
definition: definition.as_mut(),
|
||||
vmctx: handle.vmctx_mut_ptr(),
|
||||
global,
|
||||
},
|
||||
GlobalState { definition, handle },
|
||||
))
|
||||
}
|
||||
35
crates/api/src/trampoline/memory.rs
Normal file
35
crates/api/src/trampoline/memory.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::ToString;
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::MemoryType;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
pub fn create_handle_with_memory(memory: &MemoryType) -> Result<InstanceHandle> {
|
||||
let mut module = Module::new();
|
||||
|
||||
let memory = cranelift_wasm::Memory {
|
||||
minimum: memory.limits().min(),
|
||||
maximum: if memory.limits().max() == core::u32::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(memory.limits().max())
|
||||
},
|
||||
shared: false, // TODO
|
||||
};
|
||||
let tunable = Default::default();
|
||||
|
||||
let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable);
|
||||
let memory_id = module.memory_plans.push(memory_plan);
|
||||
module.exports.insert(
|
||||
"memory".to_string(),
|
||||
wasmtime_environ::Export::Memory(memory_id),
|
||||
);
|
||||
|
||||
create_handle(module, None, PrimaryMap::new(), Box::new(()))
|
||||
}
|
||||
52
crates/api/src/trampoline/mod.rs
Normal file
52
crates/api/src/trampoline/mod.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
//! Utility module to create trampolines in/out WebAssembly module.
|
||||
|
||||
mod create_handle;
|
||||
mod func;
|
||||
mod global;
|
||||
mod memory;
|
||||
mod table;
|
||||
|
||||
use crate::r#ref::HostRef;
|
||||
use alloc::rc::Rc;
|
||||
use anyhow::Result;
|
||||
|
||||
use self::func::create_handle_with_function;
|
||||
use self::global::create_global;
|
||||
use self::memory::create_handle_with_memory;
|
||||
use self::table::create_handle_with_table;
|
||||
use super::{Callable, FuncType, GlobalType, MemoryType, Store, TableType, Val};
|
||||
|
||||
pub use self::global::GlobalState;
|
||||
|
||||
pub fn generate_func_export(
|
||||
ft: &FuncType,
|
||||
func: &Rc<dyn Callable + 'static>,
|
||||
store: &HostRef<Store>,
|
||||
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
|
||||
let mut instance = create_handle_with_function(ft, func, store)?;
|
||||
let export = instance.lookup("trampoline").expect("trampoline export");
|
||||
Ok((instance, export))
|
||||
}
|
||||
|
||||
pub fn generate_global_export(
|
||||
gt: &GlobalType,
|
||||
val: Val,
|
||||
) -> Result<(wasmtime_runtime::Export, GlobalState)> {
|
||||
create_global(gt, val)
|
||||
}
|
||||
|
||||
pub fn generate_memory_export(
|
||||
m: &MemoryType,
|
||||
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
|
||||
let mut instance = create_handle_with_memory(m)?;
|
||||
let export = instance.lookup("memory").expect("memory export");
|
||||
Ok((instance, export))
|
||||
}
|
||||
|
||||
pub fn generate_table_export(
|
||||
t: &TableType,
|
||||
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
|
||||
let mut instance = create_handle_with_table(t)?;
|
||||
let export = instance.lookup("table").expect("table export");
|
||||
Ok((instance, export))
|
||||
}
|
||||
37
crates/api/src/trampoline/table.rs
Normal file
37
crates/api/src/trampoline/table.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::ToString;
|
||||
use anyhow::Result;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::TableElementType;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::InstanceHandle;
|
||||
|
||||
use super::create_handle::create_handle;
|
||||
use crate::{TableType, ValType};
|
||||
|
||||
pub fn create_handle_with_table(table: &TableType) -> Result<InstanceHandle> {
|
||||
let mut module = Module::new();
|
||||
|
||||
let table = cranelift_wasm::Table {
|
||||
minimum: table.limits().min(),
|
||||
maximum: if table.limits().max() == core::u32::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(table.limits().max())
|
||||
},
|
||||
ty: match table.element() {
|
||||
ValType::FuncRef => TableElementType::Func,
|
||||
_ => TableElementType::Val(table.element().get_cranelift_type()),
|
||||
},
|
||||
};
|
||||
let tunable = Default::default();
|
||||
|
||||
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
|
||||
let table_id = module.table_plans.push(table_plan);
|
||||
module.exports.insert(
|
||||
"table".to_string(),
|
||||
wasmtime_environ::Export::Table(table_id),
|
||||
);
|
||||
|
||||
create_handle(module, None, PrimaryMap::new(), Box::new(()))
|
||||
}
|
||||
22
crates/api/src/trap.rs
Normal file
22
crates/api/src/trap.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use alloc::string::{String, ToString};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Wasm trap: {message}")]
|
||||
pub struct Trap {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl Trap {
|
||||
pub fn new(message: String) -> Trap {
|
||||
Trap { message }
|
||||
}
|
||||
|
||||
pub fn fake() -> Trap {
|
||||
Trap::new("TODO trap".to_string())
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
353
crates/api/src/types.rs
Normal file
353
crates/api/src/types.rs
Normal file
@@ -0,0 +1,353 @@
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use cranelift_codegen::ir;
|
||||
|
||||
// Type Representations
|
||||
|
||||
// Type attributes
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Mutability {
|
||||
Const,
|
||||
Var,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Limits {
|
||||
min: u32,
|
||||
max: u32,
|
||||
}
|
||||
|
||||
impl Limits {
|
||||
pub fn new(min: u32, max: u32) -> Limits {
|
||||
Limits { min, max }
|
||||
}
|
||||
|
||||
pub fn at_least(min: u32) -> Limits {
|
||||
Limits {
|
||||
min,
|
||||
max: ::core::u32::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min(&self) -> u32 {
|
||||
self.min
|
||||
}
|
||||
|
||||
pub fn max(&self) -> u32 {
|
||||
self.max
|
||||
}
|
||||
}
|
||||
|
||||
// Value Types
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ValType {
|
||||
I32,
|
||||
I64,
|
||||
F32,
|
||||
F64,
|
||||
V128,
|
||||
AnyRef, /* = 128 */
|
||||
FuncRef,
|
||||
}
|
||||
|
||||
impl ValType {
|
||||
pub fn is_num(&self) -> bool {
|
||||
match self {
|
||||
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ref(&self) -> bool {
|
||||
match self {
|
||||
ValType::AnyRef | ValType::FuncRef => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_cranelift_type(&self) -> ir::Type {
|
||||
match self {
|
||||
ValType::I32 => ir::types::I32,
|
||||
ValType::I64 => ir::types::I64,
|
||||
ValType::F32 => ir::types::F32,
|
||||
ValType::F64 => ir::types::F64,
|
||||
ValType::V128 => ir::types::I8X16,
|
||||
_ => unimplemented!("get_cranelift_type other"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_type(ty: ir::Type) -> ValType {
|
||||
match ty {
|
||||
ir::types::I32 => ValType::I32,
|
||||
ir::types::I64 => ValType::I64,
|
||||
ir::types::F32 => ValType::F32,
|
||||
ir::types::F64 => ValType::F64,
|
||||
ir::types::I8X16 => ValType::V128,
|
||||
_ => unimplemented!("from_cranelift_type other"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// External Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExternType {
|
||||
ExternFunc(FuncType),
|
||||
ExternGlobal(GlobalType),
|
||||
ExternTable(TableType),
|
||||
ExternMemory(MemoryType),
|
||||
}
|
||||
|
||||
impl ExternType {
|
||||
pub fn func(&self) -> &FuncType {
|
||||
match self {
|
||||
ExternType::ExternFunc(func) => func,
|
||||
_ => panic!("ExternType::ExternFunc expected"),
|
||||
}
|
||||
}
|
||||
pub fn global(&self) -> &GlobalType {
|
||||
match self {
|
||||
ExternType::ExternGlobal(func) => func,
|
||||
_ => panic!("ExternType::ExternGlobal expected"),
|
||||
}
|
||||
}
|
||||
pub fn table(&self) -> &TableType {
|
||||
match self {
|
||||
ExternType::ExternTable(table) => table,
|
||||
_ => panic!("ExternType::ExternTable expected"),
|
||||
}
|
||||
}
|
||||
pub fn memory(&self) -> &MemoryType {
|
||||
match self {
|
||||
ExternType::ExternMemory(memory) => memory,
|
||||
_ => panic!("ExternType::ExternMemory expected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function Types
|
||||
fn from_cranelift_abiparam(param: &ir::AbiParam) -> ValType {
|
||||
assert!(param.purpose == ir::ArgumentPurpose::Normal);
|
||||
ValType::from_cranelift_type(param.value_type)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FuncType {
|
||||
params: Box<[ValType]>,
|
||||
results: Box<[ValType]>,
|
||||
signature: ir::Signature,
|
||||
}
|
||||
|
||||
impl FuncType {
|
||||
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
|
||||
use cranelift_codegen::ir::*;
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use target_lexicon::HOST;
|
||||
let call_conv = CallConv::triple_default(&HOST);
|
||||
let signature: Signature = {
|
||||
let mut params = params
|
||||
.iter()
|
||||
.map(|p| AbiParam::new(p.get_cranelift_type()))
|
||||
.collect::<Vec<_>>();
|
||||
let returns = results
|
||||
.iter()
|
||||
.map(|p| AbiParam::new(p.get_cranelift_type()))
|
||||
.collect::<Vec<_>>();
|
||||
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
|
||||
|
||||
Signature {
|
||||
params,
|
||||
returns,
|
||||
call_conv,
|
||||
}
|
||||
};
|
||||
FuncType {
|
||||
params,
|
||||
results,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
pub fn params(&self) -> &[ValType] {
|
||||
&self.params
|
||||
}
|
||||
pub fn results(&self) -> &[ValType] {
|
||||
&self.results
|
||||
}
|
||||
|
||||
pub(crate) fn get_cranelift_signature(&self) -> &ir::Signature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_signature(signature: ir::Signature) -> FuncType {
|
||||
let params = signature
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| p.purpose == ir::ArgumentPurpose::Normal)
|
||||
.map(|p| from_cranelift_abiparam(p))
|
||||
.collect::<Vec<_>>();
|
||||
let results = signature
|
||||
.returns
|
||||
.iter()
|
||||
.map(|p| from_cranelift_abiparam(p))
|
||||
.collect::<Vec<_>>();
|
||||
FuncType {
|
||||
params: params.into_boxed_slice(),
|
||||
results: results.into_boxed_slice(),
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlobalType {
|
||||
content: ValType,
|
||||
mutability: Mutability,
|
||||
}
|
||||
|
||||
impl GlobalType {
|
||||
pub fn new(content: ValType, mutability: Mutability) -> GlobalType {
|
||||
GlobalType {
|
||||
content,
|
||||
mutability,
|
||||
}
|
||||
}
|
||||
pub fn content(&self) -> &ValType {
|
||||
&self.content
|
||||
}
|
||||
pub fn mutability(&self) -> Mutability {
|
||||
self.mutability
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_global(global: cranelift_wasm::Global) -> GlobalType {
|
||||
let ty = ValType::from_cranelift_type(global.ty);
|
||||
let mutability = if global.mutability {
|
||||
Mutability::Var
|
||||
} else {
|
||||
Mutability::Const
|
||||
};
|
||||
GlobalType::new(ty, mutability)
|
||||
}
|
||||
}
|
||||
|
||||
// Table Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TableType {
|
||||
element: ValType,
|
||||
limits: Limits,
|
||||
}
|
||||
|
||||
impl TableType {
|
||||
pub fn new(element: ValType, limits: Limits) -> TableType {
|
||||
TableType { element, limits }
|
||||
}
|
||||
pub fn element(&self) -> &ValType {
|
||||
&self.element
|
||||
}
|
||||
pub fn limits(&self) -> &Limits {
|
||||
&self.limits
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_table(table: cranelift_wasm::Table) -> TableType {
|
||||
assert!(if let cranelift_wasm::TableElementType::Func = table.ty {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
});
|
||||
let ty = ValType::FuncRef;
|
||||
let limits = Limits::new(table.minimum, table.maximum.unwrap_or(::core::u32::MAX));
|
||||
TableType::new(ty, limits)
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemoryType {
|
||||
limits: Limits,
|
||||
}
|
||||
|
||||
impl MemoryType {
|
||||
pub fn new(limits: Limits) -> MemoryType {
|
||||
MemoryType { limits }
|
||||
}
|
||||
pub fn limits(&self) -> &Limits {
|
||||
&self.limits
|
||||
}
|
||||
|
||||
pub(crate) fn from_cranelift_memory(memory: cranelift_wasm::Memory) -> MemoryType {
|
||||
MemoryType::new(Limits::new(
|
||||
memory.minimum,
|
||||
memory.maximum.unwrap_or(::core::u32::MAX),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Import Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Name(String);
|
||||
|
||||
impl From<String> for Name {
|
||||
fn from(s: String) -> Name {
|
||||
Name(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::alloc::string::ToString for Name {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImportType {
|
||||
module: Name,
|
||||
name: Name,
|
||||
r#type: ExternType,
|
||||
}
|
||||
|
||||
impl ImportType {
|
||||
pub fn new(module: Name, name: Name, r#type: ExternType) -> ImportType {
|
||||
ImportType {
|
||||
module,
|
||||
name,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
pub fn module(&self) -> &Name {
|
||||
&self.module
|
||||
}
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.name
|
||||
}
|
||||
pub fn r#type(&self) -> &ExternType {
|
||||
&self.r#type
|
||||
}
|
||||
}
|
||||
|
||||
// Export Types
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportType {
|
||||
name: Name,
|
||||
r#type: ExternType,
|
||||
}
|
||||
|
||||
impl ExportType {
|
||||
pub fn new(name: Name, r#type: ExternType) -> ExportType {
|
||||
ExportType { name, r#type }
|
||||
}
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.name
|
||||
}
|
||||
pub fn r#type(&self) -> &ExternType {
|
||||
&self.r#type
|
||||
}
|
||||
}
|
||||
235
crates/api/src/values.rs
Normal file
235
crates/api/src/values.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use crate::externals::Func;
|
||||
use crate::r#ref::{AnyRef, HostRef};
|
||||
use crate::runtime::Store;
|
||||
use crate::types::ValType;
|
||||
use core::ptr;
|
||||
|
||||
use cranelift_codegen::ir;
|
||||
use wasmtime_jit::RuntimeValue;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Val {
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
F32(u32),
|
||||
F64(u64),
|
||||
AnyRef(AnyRef),
|
||||
FuncRef(HostRef<Func>),
|
||||
}
|
||||
|
||||
impl Val {
|
||||
pub fn default() -> Val {
|
||||
Val::AnyRef(AnyRef::null())
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> ValType {
|
||||
match self {
|
||||
Val::I32(_) => ValType::I32,
|
||||
Val::I64(_) => ValType::I64,
|
||||
Val::F32(_) => ValType::F32,
|
||||
Val::F64(_) => ValType::F64,
|
||||
Val::AnyRef(_) => ValType::AnyRef,
|
||||
Val::FuncRef(_) => ValType::FuncRef,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn write_value_to(&self, p: *mut i64) {
|
||||
match self {
|
||||
Val::I32(i) => ptr::write(p as *mut i32, *i),
|
||||
Val::I64(i) => ptr::write(p as *mut i64, *i),
|
||||
Val::F32(u) => ptr::write(p as *mut u32, *u),
|
||||
Val::F64(u) => ptr::write(p as *mut u64, *u),
|
||||
_ => unimplemented!("Val::write_value_to"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn read_value_from(p: *const i64, ty: ir::Type) -> Val {
|
||||
match ty {
|
||||
ir::types::I32 => Val::I32(ptr::read(p as *const i32)),
|
||||
ir::types::I64 => Val::I64(ptr::read(p as *const i64)),
|
||||
ir::types::F32 => Val::F32(ptr::read(p as *const u32)),
|
||||
ir::types::F64 => Val::F64(ptr::read(p as *const u64)),
|
||||
_ => unimplemented!("Val::read_value_from"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_f32_bits(v: u32) -> Val {
|
||||
Val::F32(v)
|
||||
}
|
||||
|
||||
pub fn from_f64_bits(v: u64) -> Val {
|
||||
Val::F64(v)
|
||||
}
|
||||
|
||||
pub fn i32(&self) -> i32 {
|
||||
if let Val::I32(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to i32.", self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn i64(&self) -> i64 {
|
||||
if let Val::I64(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to i64.", self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f32(&self) -> f32 {
|
||||
RuntimeValue::F32(self.f32_bits()).unwrap_f32()
|
||||
}
|
||||
|
||||
pub fn f64(&self) -> f64 {
|
||||
RuntimeValue::F64(self.f64_bits()).unwrap_f64()
|
||||
}
|
||||
|
||||
pub fn f32_bits(&self) -> u32 {
|
||||
if let Val::F32(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to f32.", self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f64_bits(&self) -> u64 {
|
||||
if let Val::F64(i) = self {
|
||||
*i
|
||||
} else {
|
||||
panic!("Invalid conversion of {:?} to f64.", self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Val {
|
||||
fn from(val: i32) -> Val {
|
||||
Val::I32(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Val {
|
||||
fn from(val: i64) -> Val {
|
||||
Val::I64(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Val {
|
||||
fn from(val: f32) -> Val {
|
||||
Val::F32(val.to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Val {
|
||||
fn from(val: f64) -> Val {
|
||||
Val::F64(val.to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i32> for Val {
|
||||
fn into(self) -> i32 {
|
||||
self.i32()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i64> for Val {
|
||||
fn into(self) -> i64 {
|
||||
self.i64()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<f32> for Val {
|
||||
fn into(self) -> f32 {
|
||||
self.f32()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<f64> for Val {
|
||||
fn into(self) -> f64 {
|
||||
self.f64()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnyRef> for Val {
|
||||
fn from(val: AnyRef) -> Val {
|
||||
match &val {
|
||||
AnyRef::Ref(r) => {
|
||||
if r.is_ref::<Func>() {
|
||||
Val::FuncRef(r.get_ref())
|
||||
} else {
|
||||
Val::AnyRef(val)
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("AnyRef::Other"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostRef<Func>> for Val {
|
||||
fn from(val: HostRef<Func>) -> Val {
|
||||
Val::FuncRef(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<AnyRef> for Val {
|
||||
fn into(self) -> AnyRef {
|
||||
match self {
|
||||
Val::AnyRef(r) => r,
|
||||
Val::FuncRef(f) => f.anyref(),
|
||||
_ => panic!("Invalid conversion of {:?} to anyref.", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_checked_anyfunc(
|
||||
val: Val,
|
||||
store: &HostRef<Store>,
|
||||
) -> wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
match val {
|
||||
Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
func_ptr: ptr::null(),
|
||||
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
|
||||
vmctx: ptr::null_mut(),
|
||||
},
|
||||
Val::FuncRef(f) => {
|
||||
let f = f.borrow();
|
||||
let (vmctx, func_ptr, signature) = match f.wasmtime_export() {
|
||||
wasmtime_runtime::Export::Function {
|
||||
vmctx,
|
||||
address,
|
||||
signature,
|
||||
} => (*vmctx, *address, signature),
|
||||
_ => panic!("expected function export"),
|
||||
};
|
||||
let type_index = store.borrow_mut().register_cranelift_signature(signature);
|
||||
wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
func_ptr,
|
||||
type_index,
|
||||
vmctx,
|
||||
}
|
||||
}
|
||||
_ => panic!("val is not funcref"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_checked_anyfunc(
|
||||
item: &wasmtime_runtime::VMCallerCheckedAnyfunc,
|
||||
store: &HostRef<Store>,
|
||||
) -> Val {
|
||||
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
|
||||
return Val::AnyRef(AnyRef::Null);
|
||||
}
|
||||
let signature = store
|
||||
.borrow()
|
||||
.lookup_cranelift_signature(item.type_index)
|
||||
.expect("signature")
|
||||
.clone();
|
||||
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
|
||||
let export = wasmtime_runtime::Export::Function {
|
||||
address: item.func_ptr,
|
||||
signature,
|
||||
vmctx: item.vmctx,
|
||||
};
|
||||
let f = Func::from_wasmtime_function(export, store, instance_handle);
|
||||
Val::FuncRef(HostRef::new(f))
|
||||
}
|
||||
1655
crates/api/src/wasm.rs
Normal file
1655
crates/api/src/wasm.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user