use crate::trampoline::{
generate_global_export, generate_memory_export, generate_table_export, StoreInstanceHandle,
};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use crate::{ExternType, GlobalType, MemoryType, Mutability, TableType, ValType};
use crate::{Func, Store, Trap};
use anyhow::{anyhow, bail, Result};
use std::slice;
use wasmtime_environ::wasm;
use wasmtime_runtime::{self as runtime, InstanceHandle};
#[derive(Clone)]
pub enum Extern {
Func(Func),
Global(Global),
Table(Table),
Memory(Memory),
}
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 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()),
}
}
pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export {
match self {
Extern::Func(f) => f.wasmtime_function().clone().into(),
Extern::Global(g) => g.wasmtime_export.clone().into(),
Extern::Memory(m) => m.wasmtime_export.clone().into(),
Extern::Table(t) => t.wasmtime_export.clone().into(),
}
}
pub(crate) fn from_wasmtime_export(
wasmtime_export: wasmtime_runtime::Export,
instance: StoreInstanceHandle,
) -> Extern {
match wasmtime_export {
wasmtime_runtime::Export::Function(f) => {
Extern::Func(Func::from_wasmtime_function(f, instance))
}
wasmtime_runtime::Export::Memory(m) => {
Extern::Memory(Memory::from_wasmtime_memory(m, instance))
}
wasmtime_runtime::Export::Global(g) => {
Extern::Global(Global::from_wasmtime_global(g, instance))
}
wasmtime_runtime::Export::Table(t) => {
Extern::Table(Table::from_wasmtime_table(t, instance))
}
}
}
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,
};
Store::same(my_store, store)
}
}
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)
}
}
#[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)
.expect("core wasm global type should be supported")
}
pub fn val_type(&self) -> ValType {
ValType::from_wasmtime_type(self.wasmtime_export.global.ty)
.expect("core wasm type should be supported")
}
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()),
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,
_ => unimplemented!("Global::set for {:?}", val.ty()),
}
}
Ok(())
}
pub(crate) fn from_wasmtime_global(
wasmtime_export: wasmtime_runtime::ExportGlobal,
instance: StoreInstanceHandle,
) -> Global {
Global {
instance,
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: wasmtime_runtime::VMCallerCheckedAnyfunc,
) -> 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 item = into_checked_anyfunc(init, store)?;
let (instance, wasmtime_export) = generate_table_export(store, &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, item.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)?;
Some(from_checked_anyfunc(item, &self.instance.store))
}
pub fn set(&self, index: u32, val: Val) -> Result<()> {
let table_index = self.wasmtime_table_index();
let item = into_checked_anyfunc(val, &self.instance.store)?;
set_table_item(&self.instance, table_index, index, item)
}
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 item = into_checked_anyfunc(init, &self.instance.store)?;
if let Some(len) = self.instance.table_grow(index, delta) {
for i in 0..delta {
set_table_item(&self.instance, index, len + i, item.clone())?;
}
Ok(len)
} 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 = dst_table.instance.get_defined_table(dst_table_index);
let src_table_index = src_table.wasmtime_table_index();
let src_table = src_table.instance.get_defined_table(src_table_index);
runtime::Table::copy(dst_table, src_table, dst_index, src_index, len)
.map_err(Trap::from_runtime)?;
Ok(())
}
pub(crate) fn from_wasmtime_table(
wasmtime_export: wasmtime_runtime::ExportTable,
instance: StoreInstanceHandle,
) -> Table {
Table {
instance,
wasmtime_export,
}
}
}
#[derive(Clone)]
pub struct Memory {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportMemory,
}
impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Memory {
let (instance, wasmtime_export) =
generate_memory_export(store, &ty).expect("generated memory");
Memory {
instance,
wasmtime_export,
}
}
pub fn ty(&self) -> MemoryType {
MemoryType::from_wasmtime_memory(&self.wasmtime_export.memory.memory)
}
pub unsafe fn data_unchecked(&self) -> &[u8] {
self.data_unchecked_mut()
}
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
let definition = &*self.wasmtime_export.definition;
slice::from_raw_parts_mut(definition.base, definition.current_length)
}
pub fn data_ptr(&self) -> *mut u8 {
unsafe { (*self.wasmtime_export.definition).base }
}
pub fn data_size(&self) -> usize {
unsafe { (*self.wasmtime_export.definition).current_length }
}
pub fn size(&self) -> u32 {
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
}
pub fn grow(&self, delta: u32) -> Result<u32> {
let index = self
.instance
.memory_index(unsafe { &*self.wasmtime_export.definition });
self.instance
.memory_grow(index, delta)
.ok_or_else(|| anyhow!("failed to grow memory"))
}
pub(crate) fn from_wasmtime_memory(
wasmtime_export: wasmtime_runtime::ExportMemory,
instance: StoreInstanceHandle,
) -> Memory {
Memory {
instance,
wasmtime_export,
}
}
}
pub unsafe trait LinearMemory {
fn size(&self) -> u32;
fn grow(&self, delta: u32) -> Option<u32>;
fn as_ptr(&self) -> *mut u8;
}
pub unsafe trait MemoryCreator: Send + Sync {
fn new_memory(
&self,
ty: MemoryType,
reserved_size: Option<u64>,
guard_size: u64,
) -> Result<Box<dyn LinearMemory>, String>;
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn respect_tunables() {
let mut cfg = Config::new();
cfg.static_memory_maximum_size(0)
.dynamic_memory_guard_size(0);
let store = Store::new(&Engine::new(&cfg));
let ty = MemoryType::new(Limits::new(1, None));
let mem = Memory::new(&store, ty);
assert_eq!(mem.wasmtime_export.memory.offset_guard_size, 0);
match mem.wasmtime_export.memory.style {
wasmtime_environ::MemoryStyle::Dynamic => {}
other => panic!("unexpected style {:?}", other),
}
}
}
#[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()
}
}