use crate::store::{StoreData, StoreOpaque, Stored};
use crate::trampoline::{generate_global_export, generate_table_export};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc};
use crate::{
AsContext, AsContextMut, ExternRef, ExternType, Func, GlobalType, Instance, Memory, Module,
Mutability, TableType, Trap, Val, ValType,
};
use anyhow::{anyhow, bail, Result};
use std::mem;
use std::ptr;
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, store: impl AsContext) -> ExternType {
let store = store.as_context();
match self {
Extern::Func(ft) => ExternType::Func(ft.ty(store)),
Extern::Memory(ft) => ExternType::Memory(ft.ty(store)),
Extern::Table(tt) => ExternType::Table(tt.ty(store)),
Extern::Global(gt) => ExternType::Global(gt.ty(store)),
Extern::Instance(i) => ExternType::Instance(i.ty(store)),
Extern::Module(m) => ExternType::Module(m.ty()),
}
}
pub(crate) unsafe fn from_wasmtime_export(
wasmtime_export: wasmtime_runtime::Export,
store: &mut StoreOpaque<'_>,
) -> 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))
}
}
}
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque<'_>) -> bool {
match self {
Extern::Func(f) => f.comes_from_same_store(store),
Extern::Global(g) => store.store_data().contains(g.0),
Extern::Memory(m) => m.comes_from_same_store(store),
Extern::Table(t) => store.store_data().contains(t.0),
Extern::Instance(i) => i.comes_from_same_store(store),
Extern::Module(_) => true,
}
}
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",
}
}
}
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(Copy, Clone, Debug)]
#[repr(transparent)] pub struct Global(Stored<wasmtime_runtime::ExportGlobal>);
impl Global {
pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
Global::_new(&mut store.as_context_mut().opaque(), ty, val)
}
fn _new(store: &mut StoreOpaque<'_>, 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");
}
unsafe {
let wasmtime_export = generate_global_export(store, &ty, val)?;
Ok(Global::from_wasmtime_global(wasmtime_export, store))
}
}
pub fn ty(&self, store: impl AsContext) -> GlobalType {
let store = store.as_context();
let ty = &store[self.0].global;
GlobalType::from_wasmtime_global(&ty)
}
pub fn get(&self, mut store: impl AsContextMut) -> Val {
unsafe {
let store = store.as_context_mut();
let definition = &*store[self.0].definition;
match self.ty(&store).content() {
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 _, &mut store.opaque())
}
ty => unimplemented!("Global::get for {:?}", ty),
}
}
}
pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
let store = store.as_context_mut();
let ty = self.ty(&store);
if ty.mutability() != Mutability::Var {
bail!("immutable global cannot be set");
}
let ty = ty.content();
if val.ty() != *ty {
bail!("global of type {:?} cannot be set to {:?}", ty, val.ty());
}
let mut store = store.opaque();
if !val.comes_from_same_store(&store) {
bail!("cross-`Store` values are not supported");
}
unsafe {
let definition = &mut *store[self.0].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(&mut store).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: &mut StoreOpaque<'_>,
) -> Global {
Global(store.store_data_mut().insert(wasmtime_export))
}
pub(crate) fn wasmtime_ty<'a>(
&self,
data: &'a StoreData,
) -> &'a wasmtime_environ::wasm::Global {
&data[self.0].global
}
pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMGlobalImport {
wasmtime_runtime::VMGlobalImport {
from: store[self.0].definition,
}
}
}
#[derive(Copy, Clone, Debug)]
#[repr(transparent)] pub struct Table(Stored<wasmtime_runtime::ExportTable>);
impl Table {
pub fn new(mut store: impl AsContextMut, ty: TableType, init: Val) -> Result<Table> {
Table::_new(&mut store.as_context_mut().opaque(), ty, init)
}
fn _new(store: &mut StoreOpaque, ty: TableType, init: Val) -> Result<Table> {
if init.ty() != *ty.element() {
bail!(
"table initialization value type {:?} does not have expected type {:?}",
init.ty(),
ty.element(),
);
}
let 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),
};
unsafe {
let table = Table::from_wasmtime_table(wasmtime_export, store);
(*table.wasmtime_table(store))
.fill(0, init, ty.limits().min())
.map_err(Trap::from_runtime)?;
Ok(table)
}
}
pub fn ty(&self, store: impl AsContext) -> TableType {
let store = store.as_context();
let ty = &store[self.0].table.table;
TableType::from_wasmtime_table(ty)
}
fn wasmtime_table(&self, store: &mut StoreOpaque<'_>) -> *mut runtime::Table {
unsafe {
let export = &store[self.0];
let mut handle = InstanceHandle::from_vmctx(export.vmctx);
let idx = handle.table_index(&*export.definition);
handle.get_defined_table(idx)
}
}
pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option<Val> {
let mut store = store.as_context_mut().opaque();
let table = self.wasmtime_table(&mut store);
unsafe {
match (*table).get(index)? {
runtime::TableElement::FuncRef(f) => Some(from_checked_anyfunc(f, &mut 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, mut store: impl AsContextMut, index: u32, val: Val) -> Result<()> {
let ty = self.ty(&store).element().clone();
let mut store = store.as_context_mut().opaque();
let val = val.into_table_element(&mut store, ty)?;
let table = self.wasmtime_table(&mut store);
unsafe {
(*table)
.set(index, val)
.map_err(|()| anyhow!("table element index out of bounds"))
}
}
pub fn size(&self, store: impl AsContext) -> u32 {
let store = store.as_context();
unsafe { (*store[self.0].definition).current_elements }
}
pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result<u32> {
let ty = self.ty(&store).element().clone();
let init = init.into_table_element(&mut store.as_context_mut().opaque(), ty)?;
let table = self.wasmtime_table(&mut store.as_context_mut().opaque());
let store = store.as_context_mut();
unsafe {
match (*table).grow(delta, init, store.0.limiter()) {
Some(size) => {
let vm = (*table).vmtable();
*store[self.0].definition = vm;
Ok(size)
}
None => bail!("failed to grow table by `{}`", delta),
}
}
}
pub fn copy(
mut store: impl AsContextMut,
dst_table: &Table,
dst_index: u32,
src_table: &Table,
src_index: u32,
len: u32,
) -> Result<()> {
if dst_table.ty(&store).element() != src_table.ty(&store).element() {
bail!("tables do not have the same element type");
}
let mut store = store.as_context_mut().opaque();
let dst = dst_table.wasmtime_table(&mut store);
let src = src_table.wasmtime_table(&mut store);
unsafe {
runtime::Table::copy(dst, src, dst_index, src_index, len)
.map_err(Trap::from_runtime)?;
}
Ok(())
}
pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Val, len: u32) -> Result<()> {
let ty = self.ty(&store).element().clone();
let mut store = store.as_context_mut().opaque();
let val = val.into_table_element(&mut store, ty)?;
let table = self.wasmtime_table(&mut store);
unsafe {
(*table).fill(dst, val, len).map_err(Trap::from_runtime)?;
}
Ok(())
}
pub(crate) unsafe fn from_wasmtime_table(
wasmtime_export: wasmtime_runtime::ExportTable,
store: &mut StoreOpaque<'_>,
) -> Table {
Table(store.store_data_mut().insert(wasmtime_export))
}
pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::wasm::Table {
&data[self.0].table.table
}
pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMTableImport {
let export = &store[self.0];
wasmtime_runtime::VMTableImport {
from: export.definition,
vmctx: export.vmctx,
}
}
}
#[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, store: impl AsContext) -> ExternType {
self.definition.ty(store)
}
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()
}
pub fn into_instance(self) -> Option<Instance> {
self.definition.into_instance()
}
pub fn into_module(self) -> Option<Module> {
self.definition.into_module()
}
}