use crate::memory::Memory;
use crate::trampoline::{generate_global_export, generate_table_export, StoreInstanceHandle};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use crate::{
ExternRef, ExternType, Func, GlobalType, Instance, Module, Mutability, Store, TableType, Trap,
ValType,
};
use anyhow::{anyhow, bail, Result};
use std::mem;
use std::ptr;
use wasmtime_environ::wasm;
use wasmtime_runtime::{self as runtime, InstanceHandle};
#[derive(Clone)]
pub enum Extern {
Func(Func),
Global(Global),
Table(Table),
Memory(Memory),
Instance(Instance),
Module(Module),
}
impl Extern {
pub fn into_func(self) -> Option<Func> {
match self {
Extern::Func(func) => Some(func),
_ => None,
}
}
pub fn into_global(self) -> Option<Global> {
match self {
Extern::Global(global) => Some(global),
_ => None,
}
}
pub fn into_table(self) -> Option<Table> {
match self {
Extern::Table(table) => Some(table),
_ => None,
}
}
pub fn into_memory(self) -> Option<Memory> {
match self {
Extern::Memory(memory) => Some(memory),
_ => None,
}
}
pub fn into_instance(self) -> Option<Instance> {
match self {
Extern::Instance(instance) => Some(instance),
_ => None,
}
}
pub fn into_module(self) -> Option<Module> {
match self {
Extern::Module(module) => Some(module),
_ => None,
}
}
pub fn ty(&self) -> ExternType {
match self {
Extern::Func(ft) => ExternType::Func(ft.ty()),
Extern::Memory(ft) => ExternType::Memory(ft.ty()),
Extern::Table(tt) => ExternType::Table(tt.ty()),
Extern::Global(gt) => ExternType::Global(gt.ty()),
Extern::Instance(i) => ExternType::Instance(i.ty()),
Extern::Module(m) => ExternType::Module(m.ty()),
}
}
pub(crate) unsafe fn from_wasmtime_export(
wasmtime_export: &wasmtime_runtime::Export,
store: &Store,
) -> Extern {
match wasmtime_export {
wasmtime_runtime::Export::Function(f) => {
Extern::Func(Func::from_wasmtime_function(f, store))
}
wasmtime_runtime::Export::Memory(m) => {
Extern::Memory(Memory::from_wasmtime_memory(m, store))
}
wasmtime_runtime::Export::Global(g) => {
Extern::Global(Global::from_wasmtime_global(g, store))
}
wasmtime_runtime::Export::Table(t) => {
Extern::Table(Table::from_wasmtime_table(t, store))
}
wasmtime_runtime::Export::Instance(i) => {
Extern::Instance(Instance::from_wasmtime(i, store))
}
wasmtime_runtime::Export::Module(m) => {
Extern::Module(m.downcast_ref::<Module>().unwrap().clone())
}
}
}
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
let my_store = match self {
Extern::Func(f) => f.store(),
Extern::Global(g) => &g.instance.store,
Extern::Memory(m) => &m.instance.store,
Extern::Table(t) => &t.instance.store,
Extern::Instance(i) => i.store(),
Extern::Module(_) => return true,
};
Store::same(my_store, store)
}
pub(crate) fn desc(&self) -> &'static str {
match self {
Extern::Func(_) => "function",
Extern::Table(_) => "table",
Extern::Memory(_) => "memory",
Extern::Global(_) => "global",
Extern::Instance(_) => "instance",
Extern::Module(_) => "module",
}
}
pub(crate) fn wasmtime_export(&self) -> wasmtime_runtime::Export {
match self {
Extern::Func(f) => f.wasmtime_export().clone().into(),
Extern::Global(f) => f.wasmtime_export().clone().into(),
Extern::Table(f) => f.wasmtime_export().clone().into(),
Extern::Memory(f) => f.wasmtime_export().clone().into(),
Extern::Instance(f) => wasmtime_runtime::Export::Instance(f.wasmtime_export().clone()),
Extern::Module(f) => wasmtime_runtime::Export::Module(Box::new(f.clone())),
}
}
}
impl From<Func> for Extern {
fn from(r: Func) -> Self {
Extern::Func(r)
}
}
impl From<Global> for Extern {
fn from(r: Global) -> Self {
Extern::Global(r)
}
}
impl From<Memory> for Extern {
fn from(r: Memory) -> Self {
Extern::Memory(r)
}
}
impl From<Table> for Extern {
fn from(r: Table) -> Self {
Extern::Table(r)
}
}
impl From<Instance> for Extern {
fn from(r: Instance) -> Self {
Extern::Instance(r)
}
}
impl From<Module> for Extern {
fn from(r: Module) -> Self {
Extern::Module(r)
}
}
#[derive(Clone)]
pub struct Global {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportGlobal,
}
impl Global {
pub fn new(store: &Store, ty: GlobalType, val: Val) -> Result<Global> {
if !val.comes_from_same_store(store) {
bail!("cross-`Store` globals are not supported");
}
if val.ty() != *ty.content() {
bail!("value provided does not match the type of this global");
}
let (instance, wasmtime_export) = generate_global_export(store, &ty, val)?;
Ok(Global {
instance,
wasmtime_export,
})
}
pub fn ty(&self) -> GlobalType {
GlobalType::from_wasmtime_global(&self.wasmtime_export.global)
}
pub fn val_type(&self) -> ValType {
ValType::from_wasm_type(&self.wasmtime_export.global.wasm_ty)
}
pub fn mutability(&self) -> Mutability {
if self.wasmtime_export.global.mutability {
Mutability::Var
} else {
Mutability::Const
}
}
pub fn get(&self) -> Val {
unsafe {
let definition = &mut *self.wasmtime_export.definition;
match self.val_type() {
ValType::I32 => Val::from(*definition.as_i32()),
ValType::I64 => Val::from(*definition.as_i64()),
ValType::F32 => Val::F32(*definition.as_u32()),
ValType::F64 => Val::F64(*definition.as_u64()),
ValType::ExternRef => Val::ExternRef(
definition
.as_externref()
.clone()
.map(|inner| ExternRef { inner }),
),
ValType::FuncRef => {
from_checked_anyfunc(definition.as_anyfunc() as *mut _, &self.instance.store)
}
ty => unimplemented!("Global::get for {:?}", ty),
}
}
}
pub fn set(&self, val: Val) -> Result<()> {
if self.mutability() != Mutability::Var {
bail!("immutable global cannot be set");
}
let ty = self.val_type();
if val.ty() != ty {
bail!("global of type {:?} cannot be set to {:?}", ty, val.ty());
}
if !val.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` values are not supported");
}
unsafe {
let definition = &mut *self.wasmtime_export.definition;
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,
Val::FuncRef(f) => {
*definition.as_anyfunc_mut() = f.map_or(ptr::null(), |f| {
f.caller_checked_anyfunc().as_ptr() as *const _
});
}
Val::ExternRef(x) => {
let old = mem::replace(definition.as_externref_mut(), x.map(|x| x.inner));
drop(old);
}
_ => unimplemented!("Global::set for {:?}", val.ty()),
}
}
Ok(())
}
pub(crate) unsafe fn from_wasmtime_global(
wasmtime_export: &wasmtime_runtime::ExportGlobal,
store: &Store,
) -> Global {
Global {
instance: store.existing_vmctx(wasmtime_export.vmctx),
wasmtime_export: wasmtime_export.clone(),
}
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Global {
&self.wasmtime_export.global
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport {
wasmtime_runtime::VMGlobalImport {
from: self.wasmtime_export.definition,
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportGlobal {
&self.wasmtime_export
}
}
#[derive(Clone)]
pub struct Table {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportTable,
}
fn set_table_item(
instance: &InstanceHandle,
table_index: wasm::DefinedTableIndex,
item_index: u32,
item: runtime::TableElement,
) -> Result<()> {
instance
.table_set(table_index, item_index, item)
.map_err(|()| anyhow!("table element index out of bounds"))
}
impl Table {
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table> {
let (instance, wasmtime_export) = generate_table_export(store, &ty)?;
let init: runtime::TableElement = match ty.element() {
ValType::FuncRef => into_checked_anyfunc(init, store)?.into(),
ValType::ExternRef => init
.externref()
.ok_or_else(|| {
anyhow!("table initialization value does not have expected type `externref`")
})?
.map(|x| x.inner)
.into(),
ty => bail!("unsupported table element type: {:?}", ty),
};
let definition = unsafe { &*wasmtime_export.definition };
let index = instance.table_index(definition);
for i in 0..definition.current_elements {
set_table_item(&instance, index, i, init.clone())?;
}
Ok(Table {
instance,
wasmtime_export,
})
}
pub fn ty(&self) -> TableType {
TableType::from_wasmtime_table(&self.wasmtime_export.table.table)
}
fn wasmtime_table_index(&self) -> wasm::DefinedTableIndex {
unsafe { self.instance.table_index(&*self.wasmtime_export.definition) }
}
pub fn get(&self, index: u32) -> Option<Val> {
let table_index = self.wasmtime_table_index();
let item = self.instance.table_get(table_index, index)?;
match item {
runtime::TableElement::FuncRef(f) => {
Some(unsafe { from_checked_anyfunc(f, &self.instance.store) })
}
runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)),
runtime::TableElement::ExternRef(Some(x)) => {
Some(Val::ExternRef(Some(ExternRef { inner: x })))
}
}
}
pub fn set(&self, index: u32, val: Val) -> Result<()> {
if !val.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` values are not supported in tables");
}
let table_index = self.wasmtime_table_index();
set_table_item(
&self.instance,
table_index,
index,
val.into_table_element()?,
)
}
pub fn size(&self) -> u32 {
unsafe { (*self.wasmtime_export.definition).current_elements }
}
pub fn grow(&self, delta: u32, init: Val) -> Result<u32> {
let index = self.wasmtime_table_index();
let orig_size = match self.ty().element() {
ValType::FuncRef => {
let init = into_checked_anyfunc(init, &self.instance.store)?;
self.instance.defined_table_grow(index, delta, init.into())
}
ValType::ExternRef => {
let init = match init {
Val::ExternRef(Some(x)) => Some(x.inner),
Val::ExternRef(None) => None,
_ => bail!("incorrect init value for growing table"),
};
self.instance.defined_table_grow(
index,
delta,
runtime::TableElement::ExternRef(init),
)
}
_ => unreachable!("only `funcref` and `externref` tables are supported"),
};
if let Some(size) = orig_size {
Ok(size)
} else {
bail!("failed to grow table by `{}`", delta)
}
}
pub fn copy(
dst_table: &Table,
dst_index: u32,
src_table: &Table,
src_index: u32,
len: u32,
) -> Result<()> {
if !Store::same(&dst_table.instance.store, &src_table.instance.store) {
bail!("cross-`Store` table copies are not supported");
}
let dst_table_index = dst_table.wasmtime_table_index();
let dst_table_index = dst_table.instance.get_defined_table(dst_table_index);
let src_table_index = src_table.wasmtime_table_index();
let src_table_index = src_table.instance.get_defined_table(src_table_index);
runtime::Table::copy(dst_table_index, src_table_index, dst_index, src_index, len)
.map_err(|e| Trap::from_runtime(&dst_table.instance.store, e))?;
Ok(())
}
pub fn fill(&self, dst: u32, val: Val, len: u32) -> Result<()> {
if !val.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` table fills are not supported");
}
let table_index = self.wasmtime_table_index();
self.instance
.handle
.defined_table_fill(table_index, dst, val.into_table_element()?, len)
.map_err(|e| Trap::from_runtime(&self.instance.store, e))?;
Ok(())
}
pub(crate) unsafe fn from_wasmtime_table(
wasmtime_export: &wasmtime_runtime::ExportTable,
store: &Store,
) -> Table {
Table {
instance: store.existing_vmctx(wasmtime_export.vmctx),
wasmtime_export: wasmtime_export.clone(),
}
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Table {
&self.wasmtime_export.table.table
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport {
wasmtime_runtime::VMTableImport {
from: self.wasmtime_export.definition,
vmctx: self.wasmtime_export.vmctx,
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportTable {
&self.wasmtime_export
}
}
#[derive(Clone)]
pub struct Export<'instance> {
name: &'instance str,
definition: Extern,
}
impl<'instance> Export<'instance> {
pub(crate) fn new(name: &'instance str, definition: Extern) -> Export<'instance> {
Export { name, definition }
}
pub fn name(&self) -> &'instance str {
self.name
}
pub fn ty(&self) -> ExternType {
self.definition.ty()
}
pub fn into_extern(self) -> Extern {
self.definition
}
pub fn into_func(self) -> Option<Func> {
self.definition.into_func()
}
pub fn into_table(self) -> Option<Table> {
self.definition.into_table()
}
pub fn into_memory(self) -> Option<Memory> {
self.definition.into_memory()
}
pub fn into_global(self) -> Option<Global> {
self.definition.into_global()
}
}