Files
wasmtime/crates/api/src/externals.rs
Alex Crichton a45b037bfc Move around some panics in wasmtime (#804)
In preparation for eventual support for wasm interface types this commit
moves around a few panics internally inside of conversions between the
`wasmtime` crate and the underlying jit support crates. This should have
any immediately-visible user changes, but the goal is that this'll help
support interface types which means `wasmtime` will have types that are
not supported by wasmtime itself and we'll be able to more gracefully
support that with error messages instead of accidental panics.
2020-01-10 16:27:52 -06:00

512 lines
15 KiB
Rust

use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable};
use crate::r#ref::AnyRef;
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 std::fmt;
use std::rc::Rc;
use std::slice;
use wasmtime_environ::wasm;
use wasmtime_runtime::InstanceHandle;
// Externals
#[derive(Clone)]
pub enum Extern {
Func(Func),
Global(Global),
Table(Table),
Memory(Memory),
}
impl Extern {
pub fn func(&self) -> Option<&Func> {
match self {
Extern::Func(func) => Some(func),
_ => None,
}
}
pub fn global(&self) -> Option<&Global> {
match self {
Extern::Global(global) => Some(global),
_ => None,
}
}
pub fn table(&self) -> Option<&Table> {
match self {
Extern::Table(table) => Some(table),
_ => None,
}
}
pub fn 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().clone()),
Extern::Memory(ft) => ExternType::Memory(ft.ty().clone()),
Extern::Table(tt) => ExternType::Table(tt.ty().clone()),
Extern::Global(gt) => ExternType::Global(gt.ty().clone()),
}
}
pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export {
match self {
Extern::Func(f) => f.wasmtime_export().clone(),
Extern::Global(g) => g.wasmtime_export().clone(),
Extern::Memory(m) => m.wasmtime_export().clone(),
Extern::Table(t) => t.wasmtime_export().clone(),
}
}
pub(crate) fn from_wasmtime_export(
store: &Store,
instance_handle: InstanceHandle,
export: wasmtime_runtime::Export,
) -> Extern {
match export {
wasmtime_runtime::Export::Function { .. } => {
Extern::Func(Func::from_wasmtime_function(export, store, instance_handle))
}
wasmtime_runtime::Export::Memory { .. } => {
Extern::Memory(Memory::from_wasmtime_memory(export, store, instance_handle))
}
wasmtime_runtime::Export::Global { .. } => {
Extern::Global(Global::from_wasmtime_global(export, store))
}
wasmtime_runtime::Export::Table { .. } => {
Extern::Table(Table::from_wasmtime_table(export, store, instance_handle))
}
}
}
}
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 Func {
_store: Store,
callable: Rc<dyn WrappedCallable + 'static>,
ty: FuncType,
}
impl Func {
pub fn new(store: &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: &Store,
ty: FuncType,
callable: Rc<dyn WrappedCallable + 'static>,
) -> Func {
Func {
_store: store.clone(),
callable,
ty,
}
}
pub fn ty(&self) -> &FuncType {
&self.ty
}
pub fn param_arity(&self) -> usize {
self.ty.params().len()
}
pub fn result_arity(&self) -> usize {
self.ty.results().len()
}
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, Trap> {
let mut results = vec![Val::null(); 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: &Store,
instance_handle: InstanceHandle,
) -> Self {
// This is only called with `Export::Function`, and since it's coming
// from wasmtime_runtime itself we should support all the types coming
// out of it, so assert such here.
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
FuncType::from_wasmtime_signature(signature.clone())
.expect("core wasm signature should be supported")
} else {
panic!("expected function export")
};
let callable = WasmtimeFn::new(store, instance_handle, export);
Func::from_wrapped(store, ty, Rc::new(callable))
}
}
impl fmt::Debug for Func {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Func")
}
}
#[derive(Clone)]
pub struct Global {
inner: Rc<GlobalInner>,
}
struct GlobalInner {
_store: Store,
ty: GlobalType,
wasmtime_export: wasmtime_runtime::Export,
#[allow(dead_code)]
wasmtime_state: Option<crate::trampoline::GlobalState>,
}
impl Global {
pub fn new(store: &Store, ty: GlobalType, val: Val) -> Global {
let (wasmtime_export, wasmtime_state) =
generate_global_export(&ty, val).expect("generated global");
Global {
inner: Rc::new(GlobalInner {
_store: store.clone(),
ty,
wasmtime_export,
wasmtime_state: Some(wasmtime_state),
}),
}
}
pub fn ty(&self) -> &GlobalType {
&self.inner.ty
}
fn wasmtime_global_definition(&self) -> *mut wasmtime_runtime::VMGlobalDefinition {
match self.inner.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.ty().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()),
_ => unimplemented!("Global::get for {:?}", self.ty().content()),
}
}
}
pub fn set(&self, val: Val) {
if val.ty() != *self.ty().content() {
panic!(
"global of type {:?} cannot be set to {:?}",
self.ty().content(),
val.ty()
);
}
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.ty()),
}
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
&self.inner.wasmtime_export
}
pub(crate) fn from_wasmtime_global(export: wasmtime_runtime::Export, store: &Store) -> Global {
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
global
} else {
panic!("wasmtime export is not global")
};
// The original export is coming from wasmtime_runtime itself we should
// support all the types coming out of it, so assert such here.
let ty = GlobalType::from_wasmtime_global(&global)
.expect("core wasm global type should be supported");
Global {
inner: Rc::new(GlobalInner {
_store: store.clone(),
ty: ty,
wasmtime_export: export,
wasmtime_state: None,
}),
}
}
}
#[derive(Clone)]
pub struct Table {
store: Store,
ty: TableType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::Export,
}
fn get_table_item(
handle: &InstanceHandle,
store: &Store,
table_index: 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: &Store,
table_index: 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: &Store, ty: TableType, init: Val) -> Table {
match ty.element() {
ValType::FuncRef => (),
_ => panic!("table is not for funcref"),
}
let (mut wasmtime_handle, wasmtime_export) =
generate_table_export(&ty).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(),
ty,
wasmtime_handle,
wasmtime_export,
}
}
pub fn ty(&self) -> &TableType {
&self.ty
}
fn wasmtime_table_index(&self) -> 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(&self, delta: u32, init: Val) -> bool {
let index = self.wasmtime_table_index();
if let Some(len) = self.wasmtime_handle.clone().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: &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_wasmtime_table(&table.table);
Table {
store: store.clone(),
ty: ty,
wasmtime_handle: instance_handle,
wasmtime_export: export,
}
}
}
#[derive(Clone)]
pub struct Memory {
_store: Store,
ty: MemoryType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::Export,
}
impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Memory {
let (wasmtime_handle, wasmtime_export) =
generate_memory_export(&ty).expect("generated memory");
Memory {
_store: store.clone(),
ty,
wasmtime_handle,
wasmtime_export,
}
}
pub fn ty(&self) -> &MemoryType {
&self.ty
}
fn wasmtime_memory_definition(&self) -> *mut wasmtime_runtime::VMMemoryDefinition {
match self.wasmtime_export {
wasmtime_runtime::Export::Memory { definition, .. } => definition,
_ => panic!("memory definition not found"),
}
}
/// Returns a mutable slice the current memory.
/// # Safety
/// 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(&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
.clone()
.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: &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_wasmtime_memory(&memory.memory);
Memory {
_store: store.clone(),
ty: ty,
wasmtime_handle: instance_handle,
wasmtime_export: export,
}
}
}