use crate::prelude::*;
use crate::runtime::externals::Global as RuntimeGlobal;
use crate::runtime::externals::Table as RuntimeTable;
use crate::runtime::externals::Tag as RuntimeTag;
use crate::{AsContextMut, Extern, Func, Val};
use crate::{Engine, type_registry::RegisteredType};
use core::fmt::{self, Display, Write};
use wasmtime_environ::WasmExnType;
use wasmtime_environ::{
EngineOrModuleTypeIndex, EntityType, Global, IndexType, Limits, Memory, ModuleTypes,
PanicOnOom as _, Table, Tag, TypeTrace, VMSharedTypeIndex, WasmArrayType,
WasmCompositeInnerType, WasmCompositeType, WasmFieldType, WasmFuncType, WasmHeapType,
WasmRefType, WasmStorageType, WasmStructType, WasmSubType, WasmValType,
};
pub(crate) mod matching;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Mutability {
Const,
Var,
}
impl Mutability {
#[inline]
pub fn is_const(&self) -> bool {
*self == Self::Const
}
#[inline]
pub fn is_var(&self) -> bool {
*self == Self::Var
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Finality {
Final,
NonFinal,
}
impl Finality {
#[inline]
pub fn is_final(&self) -> bool {
*self == Self::Final
}
#[inline]
pub fn is_non_final(&self) -> bool {
*self == Self::NonFinal
}
}
#[derive(Clone, Hash)]
pub enum ValType {
I32,
I64,
F32,
F64,
V128,
Ref(RefType),
}
impl fmt::Debug for ValType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl Display for ValType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ValType::I32 => write!(f, "i32"),
ValType::I64 => write!(f, "i64"),
ValType::F32 => write!(f, "f32"),
ValType::F64 => write!(f, "f64"),
ValType::V128 => write!(f, "v128"),
ValType::Ref(r) => Display::fmt(r, f),
}
}
}
impl From<RefType> for ValType {
#[inline]
fn from(r: RefType) -> Self {
ValType::Ref(r)
}
}
impl ValType {
pub const EXTERNREF: Self = ValType::Ref(RefType::EXTERNREF);
pub const NULLEXTERNREF: Self = ValType::Ref(RefType::NULLEXTERNREF);
pub const FUNCREF: Self = ValType::Ref(RefType::FUNCREF);
pub const NULLFUNCREF: Self = ValType::Ref(RefType::NULLFUNCREF);
pub const ANYREF: Self = ValType::Ref(RefType::ANYREF);
pub const EQREF: Self = ValType::Ref(RefType::EQREF);
pub const I31REF: Self = ValType::Ref(RefType::I31REF);
pub const ARRAYREF: Self = ValType::Ref(RefType::ARRAYREF);
pub const STRUCTREF: Self = ValType::Ref(RefType::STRUCTREF);
pub const NULLREF: Self = ValType::Ref(RefType::NULLREF);
pub const CONTREF: Self = ValType::Ref(RefType::CONTREF);
pub const NULLCONTREF: Self = ValType::Ref(RefType::NULLCONTREF);
pub const EXNREF: Self = ValType::Ref(RefType::EXNREF);
pub const NULLEXNREF: Self = ValType::Ref(RefType::NULLEXNREF);
#[inline]
pub fn is_num(&self) -> bool {
match self {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
_ => false,
}
}
#[inline]
pub fn is_i32(&self) -> bool {
matches!(self, ValType::I32)
}
#[inline]
pub fn is_i64(&self) -> bool {
matches!(self, ValType::I64)
}
#[inline]
pub fn is_f32(&self) -> bool {
matches!(self, ValType::F32)
}
#[inline]
pub fn is_f64(&self) -> bool {
matches!(self, ValType::F64)
}
#[inline]
pub fn is_v128(&self) -> bool {
matches!(self, ValType::V128)
}
#[inline]
pub fn is_ref(&self) -> bool {
matches!(self, ValType::Ref(_))
}
#[inline]
pub fn is_funcref(&self) -> bool {
matches!(
self,
ValType::Ref(RefType {
is_nullable: true,
heap_type: HeapType::Func
})
)
}
#[inline]
pub fn is_externref(&self) -> bool {
matches!(
self,
ValType::Ref(RefType {
is_nullable: true,
heap_type: HeapType::Extern
})
)
}
#[inline]
pub fn is_anyref(&self) -> bool {
matches!(
self,
ValType::Ref(RefType {
is_nullable: true,
heap_type: HeapType::Any
})
)
}
#[inline]
pub fn is_contref(&self) -> bool {
matches!(
self,
ValType::Ref(RefType {
is_nullable: true,
heap_type: HeapType::Cont
})
)
}
#[inline]
pub fn as_ref(&self) -> Option<&RefType> {
match self {
ValType::Ref(r) => Some(r),
_ => None,
}
}
#[inline]
pub fn unwrap_ref(&self) -> &RefType {
self.as_ref()
.expect("ValType::unwrap_ref on a non-reference type")
}
pub fn matches(&self, other: &ValType) -> bool {
match (self, other) {
(Self::I32, Self::I32) => true,
(Self::I64, Self::I64) => true,
(Self::F32, Self::F32) => true,
(Self::F64, Self::F64) => true,
(Self::V128, Self::V128) => true,
(Self::Ref(a), Self::Ref(b)) => a.matches(b),
(Self::I32, _)
| (Self::I64, _)
| (Self::F32, _)
| (Self::F64, _)
| (Self::V128, _)
| (Self::Ref(_), _) => false,
}
}
pub fn eq(a: &Self, b: &Self) -> bool {
a.matches(b) && b.matches(a)
}
#[inline]
pub(crate) fn is_vmgcref_type_and_points_to_object(&self) -> bool {
match self {
ValType::Ref(r) => r.is_vmgcref_type_and_points_to_object(),
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
}
}
pub(crate) fn ensure_matches(&self, engine: &Engine, other: &ValType) -> Result<()> {
if !self.comes_from_same_engine(engine) || !other.comes_from_same_engine(engine) {
bail!("type used with wrong engine");
}
if self.matches(other) {
Ok(())
} else {
bail!("type mismatch: expected {other}, found {self}")
}
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
match self {
Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true,
Self::Ref(r) => r.comes_from_same_engine(engine),
}
}
pub(crate) fn to_wasm_type(&self) -> WasmValType {
match self {
Self::I32 => WasmValType::I32,
Self::I64 => WasmValType::I64,
Self::F32 => WasmValType::F32,
Self::F64 => WasmValType::F64,
Self::V128 => WasmValType::V128,
Self::Ref(r) => WasmValType::Ref(r.to_wasm_type()),
}
}
#[inline]
pub(crate) fn from_wasm_type(engine: &Engine, ty: &WasmValType) -> Self {
match ty {
WasmValType::I32 => Self::I32,
WasmValType::I64 => Self::I64,
WasmValType::F32 => Self::F32,
WasmValType::F64 => Self::F64,
WasmValType::V128 => Self::V128,
WasmValType::Ref(r) => Self::Ref(RefType::from_wasm_type(engine, r)),
}
}
pub fn default_value(&self) -> Option<Val> {
match self {
ValType::I32 => Some(Val::I32(0)),
ValType::I64 => Some(Val::I64(0)),
ValType::F32 => Some(Val::F32(0)),
ValType::F64 => Some(Val::F64(0)),
ValType::V128 => Some(Val::V128(0.into())),
ValType::Ref(r) => {
if r.is_nullable() {
Some(Val::null_ref(r.heap_type()))
} else {
None
}
}
}
}
pub(crate) fn into_registered_type(self) -> Option<RegisteredType> {
match self {
ValType::Ref(ty) => ty.into_registered_type(),
_ => None,
}
}
}
#[derive(Clone, Hash)]
pub struct RefType {
is_nullable: bool,
heap_type: HeapType,
}
impl fmt::Debug for RefType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}
impl fmt::Display for RefType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(ref ")?;
if self.is_nullable() {
write!(f, "null ")?;
}
write!(f, "{})", self.heap_type())
}
}
impl RefType {
pub const EXTERNREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Extern,
};
pub const NULLEXTERNREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::NoExtern,
};
pub const FUNCREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Func,
};
pub const NULLFUNCREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::NoFunc,
};
pub const ANYREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Any,
};
pub const EQREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Eq,
};
pub const I31REF: Self = RefType {
is_nullable: true,
heap_type: HeapType::I31,
};
pub const ARRAYREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Array,
};
pub const STRUCTREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Struct,
};
pub const NULLREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::None,
};
pub const CONTREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Cont,
};
pub const NULLCONTREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::NoCont,
};
pub const EXNREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::Exn,
};
pub const NULLEXNREF: Self = RefType {
is_nullable: true,
heap_type: HeapType::NoExn,
};
pub fn new(is_nullable: bool, heap_type: HeapType) -> RefType {
RefType {
is_nullable,
heap_type,
}
}
pub fn is_nullable(&self) -> bool {
self.is_nullable
}
#[inline]
pub fn heap_type(&self) -> &HeapType {
&self.heap_type
}
pub fn matches(&self, other: &RefType) -> bool {
if self.is_nullable() && !other.is_nullable() {
return false;
}
self.heap_type().matches(other.heap_type())
}
pub fn eq(a: &RefType, b: &RefType) -> bool {
a.matches(b) && b.matches(a)
}
pub(crate) fn ensure_matches(&self, engine: &Engine, other: &RefType) -> Result<()> {
if !self.comes_from_same_engine(engine) || !other.comes_from_same_engine(engine) {
bail!("type used with wrong engine");
}
if self.matches(other) {
Ok(())
} else {
bail!("type mismatch: expected {other}, found {self}")
}
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
self.heap_type().comes_from_same_engine(engine)
}
pub(crate) fn to_wasm_type(&self) -> WasmRefType {
WasmRefType {
nullable: self.is_nullable(),
heap_type: self.heap_type().to_wasm_type(),
}
}
pub(crate) fn from_wasm_type(engine: &Engine, ty: &WasmRefType) -> RefType {
RefType {
is_nullable: ty.nullable,
heap_type: HeapType::from_wasm_type(engine, &ty.heap_type),
}
}
pub(crate) fn is_vmgcref_type_and_points_to_object(&self) -> bool {
self.heap_type().is_vmgcref_type_and_points_to_object()
}
pub(crate) fn into_registered_type(self) -> Option<RegisteredType> {
self.heap_type.into_registered_type()
}
}
#[derive(Debug, Clone, Hash)]
pub enum HeapType {
Extern,
NoExtern,
Func,
ConcreteFunc(FuncType),
NoFunc,
Any,
Eq,
I31,
Array,
ConcreteArray(ArrayType),
Struct,
ConcreteStruct(StructType),
Exn,
ConcreteExn(ExnType),
ConcreteCont(ContType),
Cont,
NoCont,
None,
NoExn,
}
impl Display for HeapType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HeapType::Extern => write!(f, "extern"),
HeapType::NoExtern => write!(f, "noextern"),
HeapType::Func => write!(f, "func"),
HeapType::NoFunc => write!(f, "nofunc"),
HeapType::Any => write!(f, "any"),
HeapType::Eq => write!(f, "eq"),
HeapType::I31 => write!(f, "i31"),
HeapType::Array => write!(f, "array"),
HeapType::Struct => write!(f, "struct"),
HeapType::None => write!(f, "none"),
HeapType::ConcreteFunc(ty) => write!(f, "(concrete func {:?})", ty.type_index()),
HeapType::ConcreteArray(ty) => write!(f, "(concrete array {:?})", ty.type_index()),
HeapType::ConcreteStruct(ty) => write!(f, "(concrete struct {:?})", ty.type_index()),
HeapType::ConcreteCont(ty) => write!(f, "(concrete cont {:?})", ty.type_index()),
HeapType::ConcreteExn(ty) => write!(f, "(concrete exn {:?})", ty.type_index()),
HeapType::Cont => write!(f, "cont"),
HeapType::NoCont => write!(f, "nocont"),
HeapType::Exn => write!(f, "exn"),
HeapType::NoExn => write!(f, "noexn"),
}
}
}
impl From<FuncType> for HeapType {
#[inline]
fn from(f: FuncType) -> Self {
HeapType::ConcreteFunc(f)
}
}
impl From<ArrayType> for HeapType {
#[inline]
fn from(a: ArrayType) -> Self {
HeapType::ConcreteArray(a)
}
}
impl From<StructType> for HeapType {
#[inline]
fn from(s: StructType) -> Self {
HeapType::ConcreteStruct(s)
}
}
impl From<ContType> for HeapType {
#[inline]
fn from(f: ContType) -> Self {
HeapType::ConcreteCont(f)
}
}
impl From<ExnType> for HeapType {
#[inline]
fn from(e: ExnType) -> Self {
HeapType::ConcreteExn(e)
}
}
impl HeapType {
pub fn is_extern(&self) -> bool {
matches!(self, HeapType::Extern)
}
pub fn is_func(&self) -> bool {
matches!(self, HeapType::Func)
}
pub fn is_no_func(&self) -> bool {
matches!(self, HeapType::NoFunc)
}
pub fn is_any(&self) -> bool {
matches!(self, HeapType::Any)
}
pub fn is_i31(&self) -> bool {
matches!(self, HeapType::I31)
}
pub fn is_none(&self) -> bool {
matches!(self, HeapType::None)
}
pub fn is_cont(&self) -> bool {
matches!(self, HeapType::Cont)
}
pub fn is_exn(&self) -> bool {
matches!(self, HeapType::Exn)
}
pub fn is_no_exn(&self) -> bool {
matches!(self, HeapType::NoExn)
}
pub fn is_abstract(&self) -> bool {
!self.is_concrete()
}
#[inline]
pub fn is_concrete(&self) -> bool {
matches!(
self,
HeapType::ConcreteFunc(_)
| HeapType::ConcreteArray(_)
| HeapType::ConcreteStruct(_)
| HeapType::ConcreteCont(_)
| HeapType::ConcreteExn(_)
)
}
pub fn is_concrete_func(&self) -> bool {
matches!(self, HeapType::ConcreteFunc(_))
}
pub fn as_concrete_func(&self) -> Option<&FuncType> {
match self {
HeapType::ConcreteFunc(f) => Some(f),
_ => None,
}
}
pub fn unwrap_concrete_func(&self) -> &FuncType {
self.as_concrete_func().unwrap()
}
pub fn is_concrete_array(&self) -> bool {
matches!(self, HeapType::ConcreteArray(_))
}
pub fn as_concrete_array(&self) -> Option<&ArrayType> {
match self {
HeapType::ConcreteArray(f) => Some(f),
_ => None,
}
}
pub fn unwrap_concrete_array(&self) -> &ArrayType {
self.as_concrete_array().unwrap()
}
pub fn is_concrete_cont(&self) -> bool {
matches!(self, HeapType::ConcreteCont(_))
}
pub fn as_concrete_cont(&self) -> Option<&ContType> {
match self {
HeapType::ConcreteCont(f) => Some(f),
_ => None,
}
}
pub fn is_concrete_struct(&self) -> bool {
matches!(self, HeapType::ConcreteStruct(_))
}
pub fn as_concrete_struct(&self) -> Option<&StructType> {
match self {
HeapType::ConcreteStruct(f) => Some(f),
_ => None,
}
}
pub fn unwrap_concrete_cont(&self) -> &ContType {
self.as_concrete_cont().unwrap()
}
pub fn unwrap_concrete_struct(&self) -> &StructType {
self.as_concrete_struct().unwrap()
}
pub fn is_concrete_exn(&self) -> bool {
matches!(self, HeapType::ConcreteExn(_))
}
pub fn as_concrete_exn(&self) -> Option<&ExnType> {
match self {
HeapType::ConcreteExn(e) => Some(e),
_ => None,
}
}
#[inline]
pub fn top(&self) -> HeapType {
match self {
HeapType::Func | HeapType::ConcreteFunc(_) | HeapType::NoFunc => HeapType::Func,
HeapType::Extern | HeapType::NoExtern => HeapType::Extern,
HeapType::Any
| HeapType::Eq
| HeapType::I31
| HeapType::Array
| HeapType::ConcreteArray(_)
| HeapType::Struct
| HeapType::ConcreteStruct(_)
| HeapType::None => HeapType::Any,
HeapType::Cont | HeapType::ConcreteCont(_) | HeapType::NoCont => HeapType::Cont,
HeapType::Exn | HeapType::ConcreteExn(_) | HeapType::NoExn => HeapType::Exn,
}
}
#[inline]
pub fn is_top(&self) -> bool {
match self {
HeapType::Any | HeapType::Extern | HeapType::Func | HeapType::Cont | HeapType::Exn => {
true
}
_ => false,
}
}
#[inline]
pub fn bottom(&self) -> HeapType {
match self {
HeapType::Extern | HeapType::NoExtern => HeapType::NoExtern,
HeapType::Func | HeapType::ConcreteFunc(_) | HeapType::NoFunc => HeapType::NoFunc,
HeapType::Any
| HeapType::Eq
| HeapType::I31
| HeapType::Array
| HeapType::ConcreteArray(_)
| HeapType::Struct
| HeapType::ConcreteStruct(_)
| HeapType::None => HeapType::None,
HeapType::Cont | HeapType::ConcreteCont(_) | HeapType::NoCont => HeapType::NoCont,
HeapType::Exn | HeapType::ConcreteExn(_) | HeapType::NoExn => HeapType::NoExn,
}
}
#[inline]
pub fn is_bottom(&self) -> bool {
match self {
HeapType::None
| HeapType::NoExtern
| HeapType::NoFunc
| HeapType::NoCont
| HeapType::NoExn => true,
_ => false,
}
}
pub fn matches(&self, other: &HeapType) -> bool {
match (self, other) {
(HeapType::Extern, HeapType::Extern) => true,
(HeapType::Extern, _) => false,
(HeapType::NoExtern, HeapType::NoExtern | HeapType::Extern) => true,
(HeapType::NoExtern, _) => false,
(HeapType::NoFunc, HeapType::NoFunc | HeapType::ConcreteFunc(_) | HeapType::Func) => {
true
}
(HeapType::NoFunc, _) => false,
(HeapType::ConcreteFunc(_), HeapType::Func) => true,
(HeapType::ConcreteFunc(a), HeapType::ConcreteFunc(b)) => {
assert!(a.comes_from_same_engine(b.engine()));
a.engine()
.signatures()
.is_subtype(a.type_index(), b.type_index())
}
(HeapType::ConcreteFunc(_), _) => false,
(HeapType::Func, HeapType::Func) => true,
(HeapType::Func, _) => false,
(HeapType::Cont, HeapType::Cont) => true,
(HeapType::Cont, _) => false,
(HeapType::NoCont, HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont) => {
true
}
(HeapType::NoCont, _) => false,
(HeapType::ConcreteCont(_), HeapType::Cont) => true,
(HeapType::ConcreteCont(a), HeapType::ConcreteCont(b)) => a.matches(b),
(HeapType::ConcreteCont(_), _) => false,
(
HeapType::None,
HeapType::None
| HeapType::ConcreteArray(_)
| HeapType::Array
| HeapType::ConcreteStruct(_)
| HeapType::Struct
| HeapType::I31
| HeapType::Eq
| HeapType::Any,
) => true,
(HeapType::None, _) => false,
(HeapType::ConcreteArray(_), HeapType::Array | HeapType::Eq | HeapType::Any) => true,
(HeapType::ConcreteArray(a), HeapType::ConcreteArray(b)) => {
assert!(a.comes_from_same_engine(b.engine()));
a.engine()
.signatures()
.is_subtype(a.type_index(), b.type_index())
}
(HeapType::ConcreteArray(_), _) => false,
(HeapType::Array, HeapType::Array | HeapType::Eq | HeapType::Any) => true,
(HeapType::Array, _) => false,
(HeapType::ConcreteStruct(_), HeapType::Struct | HeapType::Eq | HeapType::Any) => true,
(HeapType::ConcreteStruct(a), HeapType::ConcreteStruct(b)) => {
assert!(a.comes_from_same_engine(b.engine()));
a.engine()
.signatures()
.is_subtype(a.type_index(), b.type_index())
}
(HeapType::ConcreteStruct(_), _) => false,
(HeapType::Struct, HeapType::Struct | HeapType::Eq | HeapType::Any) => true,
(HeapType::Struct, _) => false,
(HeapType::I31, HeapType::I31 | HeapType::Eq | HeapType::Any) => true,
(HeapType::I31, _) => false,
(HeapType::Eq, HeapType::Eq | HeapType::Any) => true,
(HeapType::Eq, _) => false,
(HeapType::Any, HeapType::Any) => true,
(HeapType::Any, _) => false,
(HeapType::NoExn, HeapType::Exn | HeapType::ConcreteExn(_) | HeapType::NoExn) => true,
(HeapType::NoExn, _) => false,
(HeapType::ConcreteExn(_), HeapType::Exn) => true,
(HeapType::ConcreteExn(a), HeapType::ConcreteExn(b)) => a.matches(b),
(HeapType::ConcreteExn(_), _) => false,
(HeapType::Exn, HeapType::Exn) => true,
(HeapType::Exn, _) => false,
}
}
pub fn eq(a: &HeapType, b: &HeapType) -> bool {
a.matches(b) && b.matches(a)
}
pub(crate) fn ensure_matches(&self, engine: &Engine, other: &HeapType) -> Result<()> {
if !self.comes_from_same_engine(engine) || !other.comes_from_same_engine(engine) {
bail!("type used with wrong engine");
}
if self.matches(other) {
Ok(())
} else {
bail!("type mismatch: expected {other}, found {self}");
}
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
match self {
HeapType::Extern
| HeapType::NoExtern
| HeapType::Func
| HeapType::NoFunc
| HeapType::Any
| HeapType::Eq
| HeapType::I31
| HeapType::Array
| HeapType::Struct
| HeapType::Cont
| HeapType::NoCont
| HeapType::Exn
| HeapType::NoExn
| HeapType::None => true,
HeapType::ConcreteFunc(ty) => ty.comes_from_same_engine(engine),
HeapType::ConcreteArray(ty) => ty.comes_from_same_engine(engine),
HeapType::ConcreteStruct(ty) => ty.comes_from_same_engine(engine),
HeapType::ConcreteCont(ty) => ty.comes_from_same_engine(engine),
HeapType::ConcreteExn(ty) => ty.comes_from_same_engine(engine),
}
}
pub(crate) fn to_wasm_type(&self) -> WasmHeapType {
match self {
HeapType::Extern => WasmHeapType::Extern,
HeapType::NoExtern => WasmHeapType::NoExtern,
HeapType::Func => WasmHeapType::Func,
HeapType::NoFunc => WasmHeapType::NoFunc,
HeapType::Any => WasmHeapType::Any,
HeapType::Eq => WasmHeapType::Eq,
HeapType::I31 => WasmHeapType::I31,
HeapType::Array => WasmHeapType::Array,
HeapType::Struct => WasmHeapType::Struct,
HeapType::None => WasmHeapType::None,
HeapType::ConcreteFunc(f) => {
WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(f.type_index()))
}
HeapType::ConcreteArray(a) => {
WasmHeapType::ConcreteArray(EngineOrModuleTypeIndex::Engine(a.type_index()))
}
HeapType::ConcreteStruct(a) => {
WasmHeapType::ConcreteStruct(EngineOrModuleTypeIndex::Engine(a.type_index()))
}
HeapType::Cont => WasmHeapType::Cont,
HeapType::NoCont => WasmHeapType::NoCont,
HeapType::ConcreteCont(c) => {
WasmHeapType::ConcreteCont(EngineOrModuleTypeIndex::Engine(c.type_index()))
}
HeapType::Exn => WasmHeapType::Exn,
HeapType::NoExn => WasmHeapType::NoExn,
HeapType::ConcreteExn(e) => {
WasmHeapType::ConcreteExn(EngineOrModuleTypeIndex::Engine(e.type_index()))
}
}
}
pub(crate) fn from_wasm_type(engine: &Engine, ty: &WasmHeapType) -> HeapType {
match ty {
WasmHeapType::Extern => HeapType::Extern,
WasmHeapType::NoExtern => HeapType::NoExtern,
WasmHeapType::Func => HeapType::Func,
WasmHeapType::NoFunc => HeapType::NoFunc,
WasmHeapType::Any => HeapType::Any,
WasmHeapType::Eq => HeapType::Eq,
WasmHeapType::I31 => HeapType::I31,
WasmHeapType::Array => HeapType::Array,
WasmHeapType::Struct => HeapType::Struct,
WasmHeapType::None => HeapType::None,
WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(idx)) => {
HeapType::ConcreteFunc(FuncType::from_shared_type_index(engine, *idx))
}
WasmHeapType::ConcreteArray(EngineOrModuleTypeIndex::Engine(idx)) => {
HeapType::ConcreteArray(ArrayType::from_shared_type_index(engine, *idx))
}
WasmHeapType::ConcreteStruct(EngineOrModuleTypeIndex::Engine(idx)) => {
HeapType::ConcreteStruct(StructType::from_shared_type_index(engine, *idx))
}
WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(_))
| WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::RecGroup(_))
| WasmHeapType::ConcreteArray(EngineOrModuleTypeIndex::Module(_))
| WasmHeapType::ConcreteArray(EngineOrModuleTypeIndex::RecGroup(_))
| WasmHeapType::ConcreteStruct(EngineOrModuleTypeIndex::Module(_))
| WasmHeapType::ConcreteStruct(EngineOrModuleTypeIndex::RecGroup(_))
| WasmHeapType::ConcreteCont(EngineOrModuleTypeIndex::Module(_))
| WasmHeapType::ConcreteCont(EngineOrModuleTypeIndex::RecGroup(_))
| WasmHeapType::ConcreteExn(EngineOrModuleTypeIndex::Module(_))
| WasmHeapType::ConcreteExn(EngineOrModuleTypeIndex::RecGroup(_)) => {
panic!("HeapType::from_wasm_type on non-canonicalized-for-runtime-usage heap type")
}
WasmHeapType::Cont => HeapType::Cont,
WasmHeapType::NoCont => HeapType::NoCont,
WasmHeapType::ConcreteCont(EngineOrModuleTypeIndex::Engine(idx)) => {
HeapType::ConcreteCont(ContType::from_shared_type_index(engine, *idx))
}
WasmHeapType::Exn => HeapType::Exn,
WasmHeapType::NoExn => HeapType::NoExn,
WasmHeapType::ConcreteExn(EngineOrModuleTypeIndex::Engine(idx)) => {
HeapType::ConcreteExn(ExnType::from_shared_type_index(engine, *idx))
}
}
}
pub(crate) fn as_registered_type(&self) -> Option<&RegisteredType> {
match self {
HeapType::ConcreteCont(c) => Some(&c.registered_type),
HeapType::ConcreteFunc(f) => Some(&f.registered_type),
HeapType::ConcreteArray(a) => Some(&a.registered_type),
HeapType::ConcreteStruct(a) => Some(&a.registered_type),
HeapType::ConcreteExn(e) => Some(&e.registered_type),
HeapType::Extern
| HeapType::NoExtern
| HeapType::Func
| HeapType::NoFunc
| HeapType::Any
| HeapType::Eq
| HeapType::I31
| HeapType::Array
| HeapType::Struct
| HeapType::Cont
| HeapType::NoCont
| HeapType::Exn
| HeapType::NoExn
| HeapType::None => None,
}
}
#[inline]
pub(crate) fn is_vmgcref_type(&self) -> bool {
match self.top() {
Self::Any | Self::Extern | Self::Exn => true,
Self::Func => false,
Self::Cont => false,
ty => unreachable!("not a top type: {ty:?}"),
}
}
#[inline]
pub(crate) fn is_vmgcref_type_and_points_to_object(&self) -> bool {
self.is_vmgcref_type()
&& !matches!(
self,
HeapType::I31 | HeapType::NoExtern | HeapType::NoFunc | HeapType::None
)
}
pub(crate) fn into_registered_type(self) -> Option<RegisteredType> {
use HeapType::*;
match self {
ConcreteFunc(ty) => Some(ty.registered_type),
ConcreteArray(ty) => Some(ty.registered_type),
ConcreteStruct(ty) => Some(ty.registered_type),
ConcreteCont(ty) => Some(ty.registered_type),
ConcreteExn(ty) => Some(ty.registered_type),
Extern | NoExtern | Func | NoFunc | Any | Eq | I31 | Array | Struct | Cont | NoCont
| Exn | NoExn | None => Option::None,
}
}
}
#[derive(Debug, Clone)]
pub enum ExternType {
Func(FuncType),
Global(GlobalType),
Table(TableType),
Memory(MemoryType),
Tag(TagType),
}
macro_rules! extern_type_accessors {
($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
/// Attempt to return the underlying type of this external type,
/// returning `None` if it is a different type.
pub fn $get(&self) -> Option<&$ty> {
if let ExternType::$variant(e) = self {
Some(e)
} else {
None
}
}
pub fn $unwrap(&self) -> &$ty {
self.$get().expect(concat!("expected ", stringify!($ty)))
}
)*)
}
impl ExternType {
extern_type_accessors! {
(Func(FuncType) func unwrap_func)
(Global(GlobalType) global unwrap_global)
(Table(TableType) table unwrap_table)
(Memory(MemoryType) memory unwrap_memory)
(Tag(TagType) tag unwrap_tag)
}
pub(crate) fn from_wasmtime(
engine: &Engine,
types: &ModuleTypes,
ty: &EntityType,
) -> ExternType {
match ty {
EntityType::Function(idx) => match idx {
EngineOrModuleTypeIndex::Engine(e) => {
FuncType::from_shared_type_index(engine, *e).into()
}
EngineOrModuleTypeIndex::Module(m) => {
let subty = &types[*m];
debug_assert!(subty.is_canonicalized_for_runtime_usage());
FuncType::from_wasm_func_type(
engine,
subty.is_final,
subty.supertype,
subty.unwrap_func().clone(),
)
.into()
}
EngineOrModuleTypeIndex::RecGroup(_) => unreachable!(),
},
EntityType::Global(ty) => GlobalType::from_wasmtime_global(engine, ty).into(),
EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
EntityType::Table(ty) => TableType::from_wasmtime_table(engine, ty).into(),
EntityType::Tag(ty) => TagType::from_wasmtime_tag(engine, ty).into(),
}
}
pub fn default_value(&self, store: impl AsContextMut) -> Result<Extern> {
match self {
ExternType::Func(func_ty) => func_ty.default_value(store).map(Extern::Func),
ExternType::Global(global_ty) => global_ty.default_value(store).map(Extern::Global),
ExternType::Table(table_ty) => table_ty.default_value(store).map(Extern::Table),
ExternType::Memory(mem_ty) => mem_ty.default_value(store),
ExternType::Tag(tag_ty) => tag_ty.default_value(store).map(Extern::Tag),
}
}
}
impl From<FuncType> for ExternType {
fn from(ty: FuncType) -> ExternType {
ExternType::Func(ty)
}
}
impl From<GlobalType> for ExternType {
fn from(ty: GlobalType) -> ExternType {
ExternType::Global(ty)
}
}
impl From<MemoryType> for ExternType {
fn from(ty: MemoryType) -> ExternType {
ExternType::Memory(ty)
}
}
impl From<TableType> for ExternType {
fn from(ty: TableType) -> ExternType {
ExternType::Table(ty)
}
}
impl From<TagType> for ExternType {
fn from(ty: TagType) -> ExternType {
ExternType::Tag(ty)
}
}
#[derive(Clone, Hash)]
pub enum StorageType {
I8,
I16,
ValType(ValType),
}
impl fmt::Display for StorageType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StorageType::I8 => write!(f, "i8"),
StorageType::I16 => write!(f, "i16"),
StorageType::ValType(ty) => fmt::Display::fmt(ty, f),
}
}
}
impl From<ValType> for StorageType {
#[inline]
fn from(v: ValType) -> Self {
StorageType::ValType(v)
}
}
impl From<RefType> for StorageType {
#[inline]
fn from(r: RefType) -> Self {
StorageType::ValType(r.into())
}
}
impl StorageType {
#[inline]
pub fn is_i8(&self) -> bool {
matches!(self, Self::I8)
}
#[inline]
pub fn is_i16(&self) -> bool {
matches!(self, Self::I16)
}
#[inline]
pub fn is_val_type(&self) -> bool {
matches!(self, Self::I16)
}
#[inline]
pub fn as_val_type(&self) -> Option<&ValType> {
match self {
Self::ValType(v) => Some(v),
_ => None,
}
}
pub fn unwrap_val_type(&self) -> &ValType {
self.as_val_type().unwrap()
}
pub fn unpack(&self) -> &ValType {
match self {
StorageType::I8 | StorageType::I16 => &ValType::I32,
StorageType::ValType(ty) => ty,
}
}
pub fn matches(&self, other: &Self) -> bool {
match (self, other) {
(StorageType::I8, StorageType::I8) => true,
(StorageType::I8, _) => false,
(StorageType::I16, StorageType::I16) => true,
(StorageType::I16, _) => false,
(StorageType::ValType(a), StorageType::ValType(b)) => a.matches(b),
(StorageType::ValType(_), _) => false,
}
}
pub fn eq(a: &Self, b: &Self) -> bool {
match (a, b) {
(StorageType::I8, StorageType::I8) => true,
(StorageType::I8, _) => false,
(StorageType::I16, StorageType::I16) => true,
(StorageType::I16, _) => false,
(StorageType::ValType(a), StorageType::ValType(b)) => ValType::eq(a, b),
(StorageType::ValType(_), _) => false,
}
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
match self {
StorageType::I8 | StorageType::I16 => true,
StorageType::ValType(v) => v.comes_from_same_engine(engine),
}
}
pub(crate) fn from_wasm_storage_type(engine: &Engine, ty: &WasmStorageType) -> Self {
match ty {
WasmStorageType::I8 => Self::I8,
WasmStorageType::I16 => Self::I16,
WasmStorageType::Val(v) => ValType::from_wasm_type(engine, &v).into(),
}
}
pub(crate) fn to_wasm_storage_type(&self) -> WasmStorageType {
match self {
Self::I8 => WasmStorageType::I8,
Self::I16 => WasmStorageType::I16,
Self::ValType(v) => WasmStorageType::Val(v.to_wasm_type()),
}
}
#[cfg(feature = "gc")]
pub(crate) fn data_byte_size(&self) -> Option<u32> {
match self {
StorageType::I8 => Some(1),
StorageType::I16 => Some(2),
StorageType::ValType(ValType::I32 | ValType::F32) => Some(4),
StorageType::ValType(ValType::I64 | ValType::F64) => Some(8),
StorageType::ValType(ValType::V128) => Some(16),
StorageType::ValType(ValType::Ref(_)) => None,
}
}
}
#[derive(Clone, Hash)]
pub struct FieldType {
mutability: Mutability,
element_type: StorageType,
}
impl fmt::Display for FieldType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.mutability.is_var() {
write!(f, "(mut {})", self.element_type)
} else {
fmt::Display::fmt(&self.element_type, f)
}
}
}
impl FieldType {
#[inline]
pub fn new(mutability: Mutability, element_type: StorageType) -> Self {
Self {
mutability,
element_type,
}
}
#[inline]
pub fn mutability(&self) -> Mutability {
self.mutability
}
#[inline]
pub fn element_type(&self) -> &StorageType {
&self.element_type
}
pub fn matches(&self, other: &Self) -> bool {
use Mutability as M;
match (self.mutability, other.mutability) {
(M::Const, M::Const) => self.element_type.matches(&other.element_type),
(M::Var, M::Var) => StorageType::eq(&self.element_type, &other.element_type),
_ => false,
}
}
pub fn eq(a: &Self, b: &Self) -> bool {
a.matches(b) && b.matches(a)
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
self.element_type.comes_from_same_engine(engine)
}
pub(crate) fn from_wasm_field_type(engine: &Engine, ty: &WasmFieldType) -> Self {
Self {
mutability: if ty.mutable {
Mutability::Var
} else {
Mutability::Const
},
element_type: StorageType::from_wasm_storage_type(engine, &ty.element_type),
}
}
pub(crate) fn to_wasm_field_type(&self) -> WasmFieldType {
WasmFieldType {
element_type: self.element_type.to_wasm_storage_type(),
mutable: matches!(self.mutability, Mutability::Var),
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct StructType {
registered_type: RegisteredType,
}
impl fmt::Display for StructType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(struct")?;
for field in self.fields() {
write!(f, " (field {field})")?;
}
write!(f, ")")?;
Ok(())
}
}
impl StructType {
pub fn new(engine: &Engine, fields: impl IntoIterator<Item = FieldType>) -> Result<Self> {
Self::with_finality_and_supertype(engine, Finality::Final, None, fields)
}
pub fn with_finality_and_supertype(
engine: &Engine,
finality: Finality,
supertype: Option<&Self>,
fields: impl IntoIterator<Item = FieldType>,
) -> Result<Self> {
let fields = fields.into_iter();
let mut wasmtime_fields = Vec::with_capacity({
let size_hint = fields.size_hint();
let cap = size_hint.1.unwrap_or(size_hint.0);
supertype.is_some() as usize * cap
});
let mut registrations = smallvec::SmallVec::<[_; 4]>::new();
let fields = fields
.map(|ty: FieldType| {
assert!(ty.comes_from_same_engine(engine));
if supertype.is_some() {
wasmtime_fields.push(ty.clone());
}
if let Some(r) = ty.element_type.as_val_type().and_then(|v| v.as_ref()) {
if let Some(r) = r.heap_type().as_registered_type() {
registrations.push(r.clone());
}
}
ty.to_wasm_field_type()
})
.collect();
if let Some(supertype) = supertype {
ensure!(
supertype.finality().is_non_final(),
"cannot create a subtype of a final supertype"
);
ensure!(
Self::fields_match(wasmtime_fields.into_iter(), supertype.fields()),
"struct fields must match their supertype's fields"
);
}
Self::from_wasm_struct_type(
engine,
finality.is_final(),
false,
supertype.map(|ty| ty.type_index().into()),
WasmStructType { fields },
)
}
pub fn engine(&self) -> &Engine {
self.registered_type.engine()
}
pub fn finality(&self) -> Finality {
match self.registered_type.is_final {
true => Finality::Final,
false => Finality::NonFinal,
}
}
pub fn supertype(&self) -> Option<Self> {
self.registered_type
.supertype
.map(|ty| Self::from_shared_type_index(self.engine(), ty.unwrap_engine_type_index()))
}
pub fn field(&self, i: usize) -> Option<FieldType> {
let engine = self.engine();
self.as_wasm_struct_type()
.fields
.get(i)
.map(|ty| FieldType::from_wasm_field_type(engine, ty))
}
#[inline]
pub fn fields(&self) -> impl ExactSizeIterator<Item = FieldType> + '_ {
let engine = self.engine();
self.as_wasm_struct_type()
.fields
.iter()
.map(|ty| FieldType::from_wasm_field_type(engine, ty))
}
pub fn matches(&self, other: &StructType) -> bool {
assert!(self.comes_from_same_engine(other.engine()));
self.engine()
.signatures()
.is_subtype(self.type_index(), other.type_index())
}
fn fields_match(
a: impl ExactSizeIterator<Item = FieldType>,
b: impl ExactSizeIterator<Item = FieldType>,
) -> bool {
a.len() >= b.len() && a.zip(b).all(|(a, b)| a.matches(&b))
}
pub fn eq(a: &StructType, b: &StructType) -> bool {
assert!(a.comes_from_same_engine(b.engine()));
a.type_index() == b.type_index()
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
Engine::same(self.registered_type().engine(), engine)
}
pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
self.registered_type().index()
}
pub(crate) fn as_wasm_struct_type(&self) -> &WasmStructType {
self.registered_type().unwrap_struct()
}
pub(crate) fn registered_type(&self) -> &RegisteredType {
&self.registered_type
}
pub(crate) fn from_wasm_struct_type(
engine: &Engine,
is_final: bool,
is_shared: bool,
supertype: Option<EngineOrModuleTypeIndex>,
ty: WasmStructType,
) -> Result<StructType> {
const MAX_FIELDS: usize = 10_000;
let fields_len = ty.fields.len();
ensure!(
fields_len <= MAX_FIELDS,
"attempted to define a struct type with {fields_len} fields, but \
that is more than the maximum supported number of fields \
({MAX_FIELDS})",
);
let ty = RegisteredType::new(
engine,
WasmSubType {
is_final,
supertype,
composite_type: WasmCompositeType {
shared: is_shared,
inner: WasmCompositeInnerType::Struct(ty),
},
},
)?;
Ok(Self {
registered_type: ty,
})
}
pub(crate) fn from_shared_type_index(engine: &Engine, index: VMSharedTypeIndex) -> StructType {
let ty = RegisteredType::root(engine, index);
Self::from_registered_type(ty)
}
pub(crate) fn from_registered_type(registered_type: RegisteredType) -> Self {
debug_assert!(registered_type.is_struct());
Self { registered_type }
}
}
#[derive(Debug, Clone, Hash)]
pub struct ArrayType {
registered_type: RegisteredType,
}
impl fmt::Display for ArrayType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let field_ty = self.field_type();
write!(f, "(array (field {field_ty}))")?;
Ok(())
}
}
impl ArrayType {
pub fn new(engine: &Engine, field_type: FieldType) -> Self {
Self::with_finality_and_supertype(engine, Finality::Final, None, field_type)
.expect("cannot fail without a supertype")
}
pub fn with_finality_and_supertype(
engine: &Engine,
finality: Finality,
supertype: Option<&Self>,
field_type: FieldType,
) -> Result<Self> {
if let Some(supertype) = supertype {
assert!(supertype.comes_from_same_engine(engine));
ensure!(
supertype.finality().is_non_final(),
"cannot create a subtype of a final supertype"
);
ensure!(
field_type.matches(&supertype.field_type()),
"array field type must match its supertype's field type"
);
}
let _registration = field_type
.element_type
.as_val_type()
.and_then(|v| v.as_ref())
.and_then(|r| r.heap_type().as_registered_type());
assert!(field_type.comes_from_same_engine(engine));
let wasm_ty = WasmArrayType(field_type.to_wasm_field_type());
Ok(Self::from_wasm_array_type(
engine,
finality.is_final(),
supertype.map(|ty| ty.type_index().into()),
wasm_ty,
))
}
pub fn engine(&self) -> &Engine {
self.registered_type.engine()
}
pub fn finality(&self) -> Finality {
match self.registered_type.is_final {
true => Finality::Final,
false => Finality::NonFinal,
}
}
pub fn supertype(&self) -> Option<Self> {
self.registered_type
.supertype
.map(|ty| Self::from_shared_type_index(self.engine(), ty.unwrap_engine_type_index()))
}
pub fn field_type(&self) -> FieldType {
FieldType::from_wasm_field_type(self.engine(), &self.as_wasm_array_type().0)
}
pub fn mutability(&self) -> Mutability {
if self.as_wasm_array_type().0.mutable {
Mutability::Var
} else {
Mutability::Const
}
}
pub fn element_type(&self) -> StorageType {
StorageType::from_wasm_storage_type(
self.engine(),
&self.registered_type.unwrap_array().0.element_type,
)
}
pub fn matches(&self, other: &ArrayType) -> bool {
assert!(self.comes_from_same_engine(other.engine()));
self.engine()
.signatures()
.is_subtype(self.type_index(), other.type_index())
}
pub fn eq(a: &ArrayType, b: &ArrayType) -> bool {
assert!(a.comes_from_same_engine(b.engine()));
a.type_index() == b.type_index()
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
Engine::same(self.registered_type.engine(), engine)
}
#[cfg(feature = "gc")]
pub(crate) fn registered_type(&self) -> &RegisteredType {
&self.registered_type
}
pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
self.registered_type.index()
}
pub(crate) fn as_wasm_array_type(&self) -> &WasmArrayType {
self.registered_type.unwrap_array()
}
pub(crate) fn from_wasm_array_type(
engine: &Engine,
is_final: bool,
supertype: Option<EngineOrModuleTypeIndex>,
ty: WasmArrayType,
) -> ArrayType {
let ty = RegisteredType::new(
engine,
WasmSubType {
is_final,
supertype,
composite_type: WasmCompositeType {
shared: false,
inner: WasmCompositeInnerType::Array(ty),
},
},
)
.panic_on_oom();
Self {
registered_type: ty,
}
}
pub(crate) fn from_shared_type_index(engine: &Engine, index: VMSharedTypeIndex) -> ArrayType {
let ty = RegisteredType::root(engine, index);
Self::from_registered_type(ty)
}
pub(crate) fn from_registered_type(registered_type: RegisteredType) -> Self {
debug_assert!(registered_type.is_array());
Self { registered_type }
}
}
#[derive(Debug, Clone, Hash)]
pub struct FuncType {
registered_type: RegisteredType,
}
impl Display for FuncType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(type (func")?;
if self.params().len() > 0 {
write!(f, " (param")?;
for p in self.params() {
write!(f, " {p}")?;
}
write!(f, ")")?;
}
if self.results().len() > 0 {
write!(f, " (result")?;
for r in self.results() {
write!(f, " {r}")?;
}
write!(f, ")")?;
}
write!(f, "))")
}
}
impl FuncType {
pub fn new(
engine: &Engine,
params: impl IntoIterator<Item = ValType>,
results: impl IntoIterator<Item = ValType>,
) -> FuncType {
Self::with_finality_and_supertype(engine, Finality::Final, None, params, results)
.expect("cannot fail without a supertype")
}
pub fn with_finality_and_supertype(
engine: &Engine,
finality: Finality,
supertype: Option<&Self>,
params: impl IntoIterator<Item = ValType>,
results: impl IntoIterator<Item = ValType>,
) -> Result<Self> {
let params = params.into_iter();
let results = results.into_iter();
let mut wasmtime_params = Vec::with_capacity({
let size_hint = params.size_hint();
let cap = size_hint.1.unwrap_or(size_hint.0);
supertype.is_some() as usize * cap
});
let mut wasmtime_results = Vec::with_capacity({
let size_hint = results.size_hint();
let cap = size_hint.1.unwrap_or(size_hint.0);
supertype.is_some() as usize * cap
});
let mut registrations = smallvec::SmallVec::<[_; 4]>::new();
let mut to_wasm_type = |ty: ValType, vec: &mut Vec<_>| {
assert!(ty.comes_from_same_engine(engine));
if supertype.is_some() {
vec.push(ty.clone());
}
if let Some(r) = ty.as_ref() {
if let Some(r) = r.heap_type().as_registered_type() {
registrations.push(r.clone());
}
}
ty.to_wasm_type()
};
let wasm_func_ty = WasmFuncType::new(
params
.map(|p| to_wasm_type(p, &mut wasmtime_params))
.collect(),
results
.map(|r| to_wasm_type(r, &mut wasmtime_results))
.collect(),
);
if let Some(supertype) = supertype {
assert!(supertype.comes_from_same_engine(engine));
ensure!(
supertype.finality().is_non_final(),
"cannot create a subtype of a final supertype"
);
ensure!(
Self::matches_impl(
wasmtime_params.iter().cloned(),
supertype.params(),
wasmtime_results.iter().cloned(),
supertype.results()
),
"function type must match its supertype: found (func{params}{results}), expected \
{supertype}",
params = if wasmtime_params.is_empty() {
String::new()
} else {
let mut s = format!(" (params");
for p in &wasmtime_params {
write!(&mut s, " {p}").unwrap();
}
s.push(')');
s
},
results = if wasmtime_results.is_empty() {
String::new()
} else {
let mut s = format!(" (results");
for r in &wasmtime_results {
write!(&mut s, " {r}").unwrap();
}
s.push(')');
s
},
);
}
Ok(Self::from_wasm_func_type(
engine,
finality.is_final(),
supertype.map(|ty| ty.type_index().into()),
wasm_func_ty,
))
}
pub fn engine(&self) -> &Engine {
self.registered_type.engine()
}
pub fn finality(&self) -> Finality {
match self.registered_type.is_final {
true => Finality::Final,
false => Finality::NonFinal,
}
}
pub fn supertype(&self) -> Option<Self> {
self.registered_type
.supertype
.map(|ty| Self::from_shared_type_index(self.engine(), ty.unwrap_engine_type_index()))
}
pub fn param(&self, i: usize) -> Option<ValType> {
let engine = self.engine();
self.registered_type
.unwrap_func()
.params()
.get(i)
.map(|ty| ValType::from_wasm_type(engine, ty))
}
#[inline]
pub fn params(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
let engine = self.engine();
self.registered_type
.unwrap_func()
.params()
.iter()
.map(|ty| ValType::from_wasm_type(engine, ty))
}
pub fn result(&self, i: usize) -> Option<ValType> {
let engine = self.engine();
self.registered_type
.unwrap_func()
.returns()
.get(i)
.map(|ty| ValType::from_wasm_type(engine, ty))
}
#[inline]
pub fn results(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
let engine = self.engine();
self.registered_type
.unwrap_func()
.returns()
.iter()
.map(|ty| ValType::from_wasm_type(engine, ty))
}
pub fn matches(&self, other: &FuncType) -> bool {
assert!(self.comes_from_same_engine(other.engine()));
if self.type_index() == other.type_index() {
return true;
}
Self::matches_impl(
self.params(),
other.params(),
self.results(),
other.results(),
)
}
fn matches_impl(
a_params: impl ExactSizeIterator<Item = ValType>,
b_params: impl ExactSizeIterator<Item = ValType>,
a_results: impl ExactSizeIterator<Item = ValType>,
b_results: impl ExactSizeIterator<Item = ValType>,
) -> bool {
a_params.len() == b_params.len()
&& a_results.len() == b_results.len()
&& a_params
.zip(b_params)
.all(|(a, b)| b.matches(&a))
&& a_results
.zip(b_results)
.all(|(a, b)| a.matches(&b))
}
pub fn eq(a: &FuncType, b: &FuncType) -> bool {
assert!(a.comes_from_same_engine(b.engine()));
a.type_index() == b.type_index()
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
Engine::same(self.registered_type.engine(), engine)
}
pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
self.registered_type.index()
}
pub(crate) fn into_registered_type(self) -> RegisteredType {
self.registered_type
}
pub(crate) fn from_wasm_func_type(
engine: &Engine,
is_final: bool,
supertype: Option<EngineOrModuleTypeIndex>,
ty: WasmFuncType,
) -> FuncType {
let ty = RegisteredType::new(
engine,
WasmSubType {
is_final,
supertype,
composite_type: WasmCompositeType {
shared: false,
inner: WasmCompositeInnerType::Func(ty),
},
},
)
.panic_on_oom();
Self {
registered_type: ty,
}
}
pub(crate) fn from_shared_type_index(engine: &Engine, index: VMSharedTypeIndex) -> FuncType {
let ty = RegisteredType::root(engine, index);
Self::from_registered_type(ty)
}
pub(crate) fn from_registered_type(registered_type: RegisteredType) -> Self {
debug_assert!(registered_type.is_func());
Self { registered_type }
}
pub fn default_value(&self, mut store: impl AsContextMut) -> Result<Func> {
let dummy_results = self
.results()
.map(|ty| ty.default_value())
.collect::<Option<Vec<_>>>()
.ok_or_else(|| format_err!("function results do not have a default value"))?;
Ok(Func::new(&mut store, self.clone(), move |_, _, results| {
for (slot, dummy) in results.iter_mut().zip(dummy_results.iter()) {
*slot = *dummy;
}
Ok(())
}))
}
}
#[derive(Debug, Clone, Hash)]
pub struct ContType {
registered_type: RegisteredType,
}
impl ContType {
pub fn engine(&self) -> &Engine {
self.registered_type.engine()
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
Engine::same(self.registered_type.engine(), engine)
}
pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
self.registered_type.index()
}
pub fn matches(&self, other: &ContType) -> bool {
assert!(self.comes_from_same_engine(other.engine()));
self.type_index() == other.type_index()
}
pub(crate) fn from_shared_type_index(engine: &Engine, index: VMSharedTypeIndex) -> ContType {
let ty = RegisteredType::root(engine, index);
assert!(ty.is_cont());
Self {
registered_type: ty,
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct ExnType {
func_ty: FuncType,
registered_type: RegisteredType,
}
impl fmt::Display for ExnType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(exn {}", self.func_ty)?;
for field in self.fields() {
write!(f, " (field {field})")?;
}
write!(f, ")")?;
Ok(())
}
}
impl ExnType {
pub fn new(engine: &Engine, fields: impl IntoIterator<Item = ValType>) -> Result<ExnType> {
let fields = fields.into_iter().collect::<Vec<_>>();
let func_ty = FuncType::new(engine, fields.clone(), []);
Ok(Self::_new(engine, fields, func_ty))
}
pub fn from_tag_type(tag: &TagType) -> Result<ExnType> {
let func_ty = tag.ty();
ensure!(
func_ty.results().len() == 0,
"Cannot create an exception type from a tag type with results in the signature"
);
Ok(Self::_new(
tag.ty.engine(),
func_ty.params(),
func_ty.clone(),
))
}
fn _new(
engine: &Engine,
fields: impl IntoIterator<Item = ValType>,
func_ty: FuncType,
) -> ExnType {
let fields = fields
.into_iter()
.map(|ty| {
assert!(ty.comes_from_same_engine(engine));
WasmFieldType {
element_type: WasmStorageType::Val(ty.to_wasm_type()),
mutable: false,
}
})
.collect();
let ty = RegisteredType::new(
engine,
WasmSubType {
is_final: true,
supertype: None,
composite_type: WasmCompositeType {
shared: false,
inner: WasmCompositeInnerType::Exn(WasmExnType {
func_ty: EngineOrModuleTypeIndex::Engine(func_ty.type_index()),
fields,
}),
},
},
)
.panic_on_oom();
Self {
func_ty,
registered_type: ty,
}
}
pub fn tag_type(&self) -> TagType {
TagType {
ty: self.func_ty.clone(),
}
}
pub fn field(&self, i: usize) -> Option<FieldType> {
let engine = self.engine();
self.as_wasm_exn_type()
.fields
.get(i)
.map(|ty| FieldType::from_wasm_field_type(engine, ty))
}
#[inline]
pub fn fields(&self) -> impl ExactSizeIterator<Item = FieldType> + '_ {
let engine = self.engine();
self.as_wasm_exn_type()
.fields
.iter()
.map(|ty| FieldType::from_wasm_field_type(engine, ty))
}
pub fn engine(&self) -> &Engine {
self.registered_type.engine()
}
pub(crate) fn comes_from_same_engine(&self, engine: &Engine) -> bool {
Engine::same(self.registered_type.engine(), engine)
}
pub(crate) fn as_wasm_exn_type(&self) -> &WasmExnType {
self.registered_type().unwrap_exn()
}
pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
self.registered_type.index()
}
pub fn matches(&self, other: &ExnType) -> bool {
assert!(self.comes_from_same_engine(other.engine()));
self.type_index() == other.type_index()
}
pub(crate) fn registered_type(&self) -> &RegisteredType {
&self.registered_type
}
pub(crate) fn from_shared_type_index(engine: &Engine, index: VMSharedTypeIndex) -> ExnType {
let ty = RegisteredType::root(engine, index);
assert!(ty.is_exn());
let func_ty = FuncType::from_shared_type_index(
engine,
ty.unwrap_exn().func_ty.unwrap_engine_type_index(),
);
Self {
func_ty,
registered_type: ty,
}
}
}
#[derive(Debug, Clone, Hash)]
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_wasmtime_global(engine: &Engine, global: &Global) -> GlobalType {
let ty = ValType::from_wasm_type(engine, &global.wasm_ty);
let mutability = if global.mutability {
Mutability::Var
} else {
Mutability::Const
};
GlobalType::new(ty, mutability)
}
pub fn default_value(&self, store: impl AsContextMut) -> Result<RuntimeGlobal> {
let val = self
.content()
.default_value()
.ok_or_else(|| format_err!("global type has no default value"))?;
RuntimeGlobal::new(store, self.clone(), val)
}
pub(crate) fn into_registered_type(self) -> Option<RegisteredType> {
self.content.into_registered_type()
}
}
#[derive(Debug, Clone, Hash)]
pub struct TagType {
ty: FuncType,
}
impl TagType {
pub fn new(ty: FuncType) -> TagType {
TagType { ty }
}
pub fn ty(&self) -> &FuncType {
&self.ty
}
pub(crate) fn from_wasmtime_tag(engine: &Engine, tag: &Tag) -> TagType {
let ty = FuncType::from_shared_type_index(engine, tag.signature.unwrap_engine_type_index());
TagType { ty }
}
pub fn default_value(&self, store: impl AsContextMut) -> Result<RuntimeTag> {
RuntimeTag::new(store, self)
}
}
#[derive(Debug, Clone, Hash)]
pub struct TableType {
element: RefType,
ty: Table,
}
impl TableType {
pub fn new(element: RefType, min: u32, max: Option<u32>) -> TableType {
let ref_type = element.to_wasm_type();
debug_assert!(
ref_type.is_canonicalized_for_runtime_usage(),
"should be canonicalized for runtime usage: {ref_type:?}"
);
let limits = Limits {
min: u64::from(min),
max: max.map(|x| u64::from(x)),
};
TableType {
element,
ty: Table {
idx_type: IndexType::I32,
limits,
ref_type,
},
}
}
pub fn new64(element: RefType, min: u64, max: Option<u64>) -> TableType {
let ref_type = element.to_wasm_type();
debug_assert!(
ref_type.is_canonicalized_for_runtime_usage(),
"should be canonicalized for runtime usage: {ref_type:?}"
);
TableType {
element,
ty: Table {
ref_type,
idx_type: IndexType::I64,
limits: Limits { min, max },
},
}
}
pub fn is_64(&self) -> bool {
matches!(self.ty.idx_type, IndexType::I64)
}
pub fn element(&self) -> &RefType {
&self.element
}
pub fn minimum(&self) -> u64 {
self.ty.limits.min
}
pub fn maximum(&self) -> Option<u64> {
self.ty.limits.max
}
pub(crate) fn from_wasmtime_table(engine: &Engine, table: &Table) -> TableType {
let element = RefType::from_wasm_type(engine, &table.ref_type);
TableType {
element,
ty: *table,
}
}
pub(crate) fn wasmtime_table(&self) -> &Table {
&self.ty
}
pub fn default_value(&self, store: impl AsContextMut) -> Result<RuntimeTable> {
let val: ValType = self.element().clone().into();
let init_val = val
.default_value()
.context("table element type does not have a default value")?
.ref_()
.unwrap();
RuntimeTable::new(store, self.clone(), init_val)
}
}
pub struct MemoryTypeBuilder {
ty: Memory,
}
impl Default for MemoryTypeBuilder {
fn default() -> Self {
MemoryTypeBuilder {
ty: Memory {
idx_type: IndexType::I32,
limits: Limits { min: 0, max: None },
shared: false,
page_size_log2: Memory::DEFAULT_PAGE_SIZE_LOG2,
},
}
}
}
impl MemoryTypeBuilder {
pub fn new() -> MemoryTypeBuilder {
MemoryTypeBuilder::default()
}
fn validate(&self) -> Result<()> {
if self
.ty
.limits
.max
.map_or(false, |max| max < self.ty.limits.min)
{
bail!("maximum page size cannot be smaller than the minimum page size");
}
match self.ty.page_size_log2 {
0 | Memory::DEFAULT_PAGE_SIZE_LOG2 => {}
x => bail!(
"page size must be 2**16 or 2**0, but was given 2**{x}; note \
that future Wasm extensions might allow any power of two page \
size, but only 2**16 and 2**0 are currently valid",
),
}
if self.ty.shared && self.ty.limits.max.is_none() {
bail!("shared memories must have a maximum size");
}
let absolute_max = self.ty.max_size_based_on_index_type();
let min = self
.ty
.minimum_byte_size()
.context("memory's minimum byte size must fit in a u64")?;
if min > absolute_max {
bail!("minimum size is too large for this memory type's index type");
}
if self
.ty
.maximum_byte_size()
.map_or(false, |max| max > absolute_max)
{
bail!("maximum size is too large for this memory type's index type");
}
Ok(())
}
pub fn min(&mut self, minimum: u64) -> &mut Self {
self.ty.limits.min = minimum;
self
}
pub fn max(&mut self, maximum: Option<u64>) -> &mut Self {
self.ty.limits.max = maximum;
self
}
pub fn memory64(&mut self, memory64: bool) -> &mut Self {
self.ty.idx_type = match memory64 {
true => IndexType::I64,
false => IndexType::I32,
};
self
}
pub fn shared(&mut self, shared: bool) -> &mut Self {
self.ty.shared = shared;
self
}
pub fn page_size_log2(&mut self, page_size_log2: u8) -> &mut Self {
self.ty.page_size_log2 = page_size_log2;
self
}
pub fn build(&self) -> Result<MemoryType> {
self.validate()?;
Ok(MemoryType { ty: self.ty })
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MemoryType {
ty: Memory,
}
impl MemoryType {
pub fn new(minimum: u32, maximum: Option<u32>) -> MemoryType {
MemoryTypeBuilder::default()
.min(minimum.into())
.max(maximum.map(Into::into))
.build()
.unwrap()
}
pub fn new64(minimum: u64, maximum: Option<u64>) -> MemoryType {
MemoryTypeBuilder::default()
.memory64(true)
.min(minimum)
.max(maximum)
.build()
.unwrap()
}
pub fn shared(minimum: u32, maximum: u32) -> MemoryType {
MemoryTypeBuilder::default()
.shared(true)
.min(minimum.into())
.max(Some(maximum.into()))
.build()
.unwrap()
}
pub fn builder() -> MemoryTypeBuilder {
MemoryTypeBuilder::new()
}
pub fn is_64(&self) -> bool {
matches!(self.ty.idx_type, IndexType::I64)
}
pub fn is_shared(&self) -> bool {
self.ty.shared
}
pub fn minimum(&self) -> u64 {
self.ty.limits.min
}
pub fn maximum(&self) -> Option<u64> {
self.ty.limits.max
}
pub fn page_size(&self) -> u64 {
self.ty.page_size()
}
pub fn page_size_log2(&self) -> u8 {
self.ty.page_size_log2
}
pub(crate) fn from_wasmtime_memory(memory: &Memory) -> MemoryType {
MemoryType { ty: *memory }
}
pub(crate) fn wasmtime_memory(&self) -> &Memory {
&self.ty
}
pub fn default_value(&self, store: impl AsContextMut) -> Result<Extern> {
Ok(if self.is_shared() {
#[cfg(feature = "threads")]
{
let store = store.as_context();
Extern::SharedMemory(crate::SharedMemory::new(store.engine(), self.clone())?)
}
#[cfg(not(feature = "threads"))]
{
bail!("creation of shared memories disabled at compile time")
}
} else {
Extern::Memory(crate::Memory::new(store, self.clone())?)
})
}
}
#[derive(Clone)]
pub struct ImportType<'module> {
module: &'module str,
name: &'module str,
ty: EntityType,
types: &'module ModuleTypes,
engine: &'module Engine,
}
impl<'module> ImportType<'module> {
pub(crate) fn new(
module: &'module str,
name: &'module str,
ty: EntityType,
types: &'module ModuleTypes,
engine: &'module Engine,
) -> ImportType<'module> {
assert!(ty.is_canonicalized_for_runtime_usage());
ImportType {
module,
name,
ty,
types,
engine,
}
}
pub fn module(&self) -> &'module str {
self.module
}
pub fn name(&self) -> &'module str {
self.name
}
pub fn ty(&self) -> ExternType {
ExternType::from_wasmtime(self.engine, self.types, &self.ty)
}
}
impl<'module> fmt::Debug for ImportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImportType")
.field("module", &self.module())
.field("name", &self.name())
.field("ty", &self.ty())
.finish()
}
}
#[derive(Clone)]
pub struct ExportType<'module> {
name: &'module str,
ty: EntityType,
types: &'module ModuleTypes,
engine: &'module Engine,
}
impl<'module> ExportType<'module> {
pub(crate) fn new(
name: &'module str,
ty: EntityType,
types: &'module ModuleTypes,
engine: &'module Engine,
) -> ExportType<'module> {
ExportType {
name,
ty,
types,
engine,
}
}
pub fn name(&self) -> &'module str {
self.name
}
pub fn ty(&self) -> ExternType {
ExternType::from_wasmtime(self.engine, self.types, &self.ty)
}
}
impl<'module> fmt::Debug for ExportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExportType")
.field("name", &self.name().to_owned())
.field("ty", &self.ty())
.finish()
}
}