use crate::{wasm_unsupported, WasmResult};
use alloc::borrow::Cow;
use alloc::boxed::Box;
use core::{fmt, ops::Range};
use cranelift_entity::entity_impl;
use serde_derive::{Deserialize, Serialize};
use smallvec::SmallVec;
pub trait TypeTrace {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>;
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>;
fn trace_engine_indices<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(VMSharedTypeIndex) -> Result<(), E>,
{
self.trace(&mut |idx| match idx {
EngineOrModuleTypeIndex::Engine(idx) => func(idx),
EngineOrModuleTypeIndex::Module(_) | EngineOrModuleTypeIndex::RecGroup(_) => Ok(()),
})
}
fn canonicalize_for_runtime_usage<F>(&mut self, module_to_engine: &mut F)
where
F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,
{
self.trace_mut::<_, ()>(&mut |idx| match idx {
EngineOrModuleTypeIndex::Engine(_) => Ok(()),
EngineOrModuleTypeIndex::Module(module_index) => {
let engine_index = module_to_engine(*module_index);
*idx = EngineOrModuleTypeIndex::Engine(engine_index);
Ok(())
}
EngineOrModuleTypeIndex::RecGroup(_) => {
panic!("should not already be canonicalized for hash consing")
}
})
.unwrap()
}
fn is_canonicalized_for_runtime_usage(&self) -> bool {
self.trace(&mut |idx| match idx {
EngineOrModuleTypeIndex::Engine(_) => Ok(()),
EngineOrModuleTypeIndex::Module(_) | EngineOrModuleTypeIndex::RecGroup(_) => Err(()),
})
.is_ok()
}
fn canonicalize_for_hash_consing<F>(
&mut self,
rec_group_range: Range<ModuleInternedTypeIndex>,
module_to_engine: &mut F,
) where
F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,
{
self.trace_mut::<_, ()>(&mut |idx| match *idx {
EngineOrModuleTypeIndex::Engine(_) => Ok(()),
EngineOrModuleTypeIndex::Module(module_index) => {
*idx = if rec_group_range.start <= module_index {
debug_assert!(module_index < rec_group_range.end);
let relative = module_index.as_u32() - rec_group_range.start.as_u32();
let relative = RecGroupRelativeTypeIndex::from_u32(relative);
EngineOrModuleTypeIndex::RecGroup(relative)
} else {
debug_assert!(module_index < rec_group_range.start);
EngineOrModuleTypeIndex::Engine(module_to_engine(module_index))
};
Ok(())
}
EngineOrModuleTypeIndex::RecGroup(_) => {
panic!("should not already be canonicalized for hash consing")
}
})
.unwrap()
}
fn is_canonicalized_for_hash_consing(&self) -> bool {
self.trace(&mut |idx| match idx {
EngineOrModuleTypeIndex::Engine(_) | EngineOrModuleTypeIndex::RecGroup(_) => Ok(()),
EngineOrModuleTypeIndex::Module(_) => Err(()),
})
.is_ok()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WasmValType {
I32,
I64,
F32,
F64,
V128,
Ref(WasmRefType),
}
impl fmt::Display for WasmValType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WasmValType::I32 => write!(f, "i32"),
WasmValType::I64 => write!(f, "i64"),
WasmValType::F32 => write!(f, "f32"),
WasmValType::F64 => write!(f, "f64"),
WasmValType::V128 => write!(f, "v128"),
WasmValType::Ref(rt) => write!(f, "{rt}"),
}
}
}
impl TypeTrace for WasmValType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
WasmValType::Ref(r) => r.trace(func),
WasmValType::I32
| WasmValType::I64
| WasmValType::F32
| WasmValType::F64
| WasmValType::V128 => Ok(()),
}
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
WasmValType::Ref(r) => r.trace_mut(func),
WasmValType::I32
| WasmValType::I64
| WasmValType::F32
| WasmValType::F64
| WasmValType::V128 => Ok(()),
}
}
}
impl WasmValType {
#[inline]
pub fn is_vmgcref_type(&self) -> bool {
match self {
WasmValType::Ref(r) => r.is_vmgcref_type(),
_ => false,
}
}
#[inline]
pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
match self {
WasmValType::Ref(r) => r.is_vmgcref_type_and_not_i31(),
_ => false,
}
}
fn trampoline_type(&self) -> Self {
match self {
WasmValType::Ref(r) => WasmValType::Ref(WasmRefType {
nullable: true,
heap_type: r.heap_type.top().into(),
}),
WasmValType::I32
| WasmValType::I64
| WasmValType::F32
| WasmValType::F64
| WasmValType::V128 => *self,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct WasmRefType {
pub nullable: bool,
pub heap_type: WasmHeapType,
}
impl TypeTrace for WasmRefType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
self.heap_type.trace(func)
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
self.heap_type.trace_mut(func)
}
}
impl WasmRefType {
pub const EXTERNREF: WasmRefType = WasmRefType {
nullable: true,
heap_type: WasmHeapType::Extern,
};
pub const FUNCREF: WasmRefType = WasmRefType {
nullable: true,
heap_type: WasmHeapType::Func,
};
#[inline]
pub fn is_vmgcref_type(&self) -> bool {
self.heap_type.is_vmgcref_type()
}
#[inline]
pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
self.heap_type.is_vmgcref_type_and_not_i31()
}
}
impl fmt::Display for WasmRefType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::FUNCREF => write!(f, "funcref"),
Self::EXTERNREF => write!(f, "externref"),
_ => {
if self.nullable {
write!(f, "(ref null {})", self.heap_type)
} else {
write!(f, "(ref {})", self.heap_type)
}
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EngineOrModuleTypeIndex {
Engine(VMSharedTypeIndex),
Module(ModuleInternedTypeIndex),
RecGroup(RecGroupRelativeTypeIndex),
}
impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex {
#[inline]
fn from(i: ModuleInternedTypeIndex) -> Self {
Self::Module(i)
}
}
impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex {
#[inline]
fn from(i: VMSharedTypeIndex) -> Self {
Self::Engine(i)
}
}
impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex {
#[inline]
fn from(i: RecGroupRelativeTypeIndex) -> Self {
Self::RecGroup(i)
}
}
impl fmt::Display for EngineOrModuleTypeIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Engine(i) => write!(f, "(engine {})", i.bits()),
Self::Module(i) => write!(f, "(module {})", i.as_u32()),
Self::RecGroup(i) => write!(f, "(recgroup {})", i.as_u32()),
}
}
}
impl EngineOrModuleTypeIndex {
pub fn is_engine_type_index(self) -> bool {
matches!(self, Self::Engine(_))
}
pub fn as_engine_type_index(self) -> Option<VMSharedTypeIndex> {
match self {
Self::Engine(e) => Some(e),
Self::RecGroup(_) | Self::Module(_) => None,
}
}
pub fn unwrap_engine_type_index(self) -> VMSharedTypeIndex {
self.as_engine_type_index()
.unwrap_or_else(|| panic!("`unwrap_engine_type_index` on {self:?}"))
}
pub fn is_module_type_index(self) -> bool {
matches!(self, Self::Module(_))
}
pub fn as_module_type_index(self) -> Option<ModuleInternedTypeIndex> {
match self {
Self::Module(e) => Some(e),
Self::RecGroup(_) | Self::Engine(_) => None,
}
}
pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex {
self.as_module_type_index()
.unwrap_or_else(|| panic!("`unwrap_module_type_index` on {self:?}"))
}
pub fn is_rec_group_type_index(self) -> bool {
matches!(self, Self::RecGroup(_))
}
pub fn as_rec_group_type_index(self) -> Option<RecGroupRelativeTypeIndex> {
match self {
Self::RecGroup(r) => Some(r),
Self::Module(_) | Self::Engine(_) => None,
}
}
pub fn unwrap_rec_group_type_index(self) -> RecGroupRelativeTypeIndex {
self.as_rec_group_type_index()
.unwrap_or_else(|| panic!("`unwrap_rec_group_type_index` on {self:?}"))
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum WasmHeapType {
Extern,
NoExtern,
Func,
ConcreteFunc(EngineOrModuleTypeIndex),
NoFunc,
Any,
Eq,
I31,
Array,
ConcreteArray(EngineOrModuleTypeIndex),
Struct,
ConcreteStruct(EngineOrModuleTypeIndex),
None,
}
impl From<WasmHeapTopType> for WasmHeapType {
#[inline]
fn from(value: WasmHeapTopType) -> Self {
match value {
WasmHeapTopType::Extern => Self::Extern,
WasmHeapTopType::Any => Self::Any,
WasmHeapTopType::Func => Self::Func,
}
}
}
impl fmt::Display for WasmHeapType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Extern => write!(f, "extern"),
Self::NoExtern => write!(f, "noextern"),
Self::Func => write!(f, "func"),
Self::ConcreteFunc(i) => write!(f, "func {i}"),
Self::NoFunc => write!(f, "nofunc"),
Self::Any => write!(f, "any"),
Self::Eq => write!(f, "eq"),
Self::I31 => write!(f, "i31"),
Self::Array => write!(f, "array"),
Self::ConcreteArray(i) => write!(f, "array {i}"),
Self::Struct => write!(f, "struct"),
Self::ConcreteStruct(i) => write!(f, "struct {i}"),
Self::None => write!(f, "none"),
}
}
}
impl TypeTrace for WasmHeapType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
match *self {
Self::ConcreteArray(i) => func(i),
Self::ConcreteFunc(i) => func(i),
Self::ConcreteStruct(i) => func(i),
_ => Ok(()),
}
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
Self::ConcreteArray(i) => func(i),
Self::ConcreteFunc(i) => func(i),
Self::ConcreteStruct(i) => func(i),
_ => Ok(()),
}
}
}
impl WasmHeapType {
#[inline]
pub fn is_vmgcref_type(&self) -> bool {
match self.top() {
WasmHeapTopType::Any | WasmHeapTopType::Extern => true,
WasmHeapTopType::Func => false,
}
}
#[inline]
pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
self.is_vmgcref_type() && *self != Self::I31
}
#[inline]
pub fn top(&self) -> WasmHeapTopType {
match self {
WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapTopType::Extern,
WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
WasmHeapTopType::Func
}
WasmHeapType::Any
| WasmHeapType::Eq
| WasmHeapType::I31
| WasmHeapType::Array
| WasmHeapType::ConcreteArray(_)
| WasmHeapType::Struct
| WasmHeapType::ConcreteStruct(_)
| WasmHeapType::None => WasmHeapTopType::Any,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum WasmHeapTopType {
Extern,
Any,
Func,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmFuncType {
params: Box<[WasmValType]>,
non_i31_gc_ref_params_count: usize,
returns: Box<[WasmValType]>,
non_i31_gc_ref_returns_count: usize,
}
impl fmt::Display for WasmFuncType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(func")?;
if !self.params.is_empty() {
write!(f, " (param")?;
for p in self.params.iter() {
write!(f, " {p}")?;
}
write!(f, ")")?;
}
if !self.returns.is_empty() {
write!(f, " (result")?;
for r in self.returns.iter() {
write!(f, " {r}")?;
}
write!(f, ")")?;
}
write!(f, ")")
}
}
impl TypeTrace for WasmFuncType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
for p in self.params.iter() {
p.trace(func)?;
}
for r in self.returns.iter() {
r.trace(func)?;
}
Ok(())
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
for p in self.params.iter_mut() {
p.trace_mut(func)?;
}
for r in self.returns.iter_mut() {
r.trace_mut(func)?;
}
Ok(())
}
}
impl WasmFuncType {
#[inline]
pub fn new(params: Box<[WasmValType]>, returns: Box<[WasmValType]>) -> Self {
let non_i31_gc_ref_params_count = params
.iter()
.filter(|p| p.is_vmgcref_type_and_not_i31())
.count();
let non_i31_gc_ref_returns_count = returns
.iter()
.filter(|r| r.is_vmgcref_type_and_not_i31())
.count();
WasmFuncType {
params,
non_i31_gc_ref_params_count,
returns,
non_i31_gc_ref_returns_count,
}
}
#[inline]
pub fn params(&self) -> &[WasmValType] {
&self.params
}
#[inline]
pub fn non_i31_gc_ref_params_count(&self) -> usize {
self.non_i31_gc_ref_params_count
}
#[inline]
pub fn returns(&self) -> &[WasmValType] {
&self.returns
}
#[inline]
pub fn non_i31_gc_ref_returns_count(&self) -> usize {
self.non_i31_gc_ref_returns_count
}
pub fn is_trampoline_type(&self) -> bool {
self.params().iter().all(|p| *p == p.trampoline_type())
&& self.returns().iter().all(|r| *r == r.trampoline_type())
}
pub fn trampoline_type(&self) -> Cow<'_, Self> {
if self.is_trampoline_type() {
return Cow::Borrowed(self);
}
Cow::Owned(Self::new(
self.params().iter().map(|p| p.trampoline_type()).collect(),
self.returns().iter().map(|r| r.trampoline_type()).collect(),
))
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum WasmStorageType {
I8,
I16,
Val(WasmValType),
}
impl fmt::Display for WasmStorageType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WasmStorageType::I8 => write!(f, "i8"),
WasmStorageType::I16 => write!(f, "i16"),
WasmStorageType::Val(v) => fmt::Display::fmt(v, f),
}
}
}
impl TypeTrace for WasmStorageType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
WasmStorageType::Val(v) => v.trace(func),
}
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
WasmStorageType::Val(v) => v.trace_mut(func),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmFieldType {
pub element_type: WasmStorageType,
pub mutable: bool,
}
impl fmt::Display for WasmFieldType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.mutable {
write!(f, "(mut {})", self.element_type)
} else {
fmt::Display::fmt(&self.element_type, f)
}
}
}
impl TypeTrace for WasmFieldType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
self.element_type.trace(func)
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
self.element_type.trace_mut(func)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmArrayType(pub WasmFieldType);
impl fmt::Display for WasmArrayType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(array {})", self.0)
}
}
impl TypeTrace for WasmArrayType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
self.0.trace(func)
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
self.0.trace_mut(func)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmStructType {
pub fields: Box<[WasmFieldType]>,
}
impl fmt::Display for WasmStructType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(struct")?;
for ty in self.fields.iter() {
write!(f, " {ty}")?;
}
write!(f, ")")
}
}
impl TypeTrace for WasmStructType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
for f in self.fields.iter() {
f.trace(func)?;
}
Ok(())
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
for f in self.fields.iter_mut() {
f.trace_mut(func)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum WasmCompositeType {
Array(WasmArrayType),
Func(WasmFuncType),
Struct(WasmStructType),
}
impl fmt::Display for WasmCompositeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WasmCompositeType::Array(ty) => fmt::Display::fmt(ty, f),
WasmCompositeType::Func(ty) => fmt::Display::fmt(ty, f),
WasmCompositeType::Struct(ty) => fmt::Display::fmt(ty, f),
}
}
}
#[allow(missing_docs)]
impl WasmCompositeType {
#[inline]
pub fn is_array(&self) -> bool {
matches!(self, Self::Array(_))
}
#[inline]
pub fn as_array(&self) -> Option<&WasmArrayType> {
match self {
WasmCompositeType::Array(f) => Some(f),
_ => None,
}
}
#[inline]
pub fn unwrap_array(&self) -> &WasmArrayType {
self.as_array().unwrap()
}
#[inline]
pub fn is_func(&self) -> bool {
matches!(self, Self::Func(_))
}
#[inline]
pub fn as_func(&self) -> Option<&WasmFuncType> {
match self {
WasmCompositeType::Func(f) => Some(f),
_ => None,
}
}
#[inline]
pub fn unwrap_func(&self) -> &WasmFuncType {
self.as_func().unwrap()
}
#[inline]
pub fn is_struct(&self) -> bool {
matches!(self, Self::Struct(_))
}
#[inline]
pub fn as_struct(&self) -> Option<&WasmStructType> {
match self {
WasmCompositeType::Struct(f) => Some(f),
_ => None,
}
}
#[inline]
pub fn unwrap_struct(&self) -> &WasmStructType {
self.as_struct().unwrap()
}
}
impl TypeTrace for WasmCompositeType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
WasmCompositeType::Array(a) => a.trace(func),
WasmCompositeType::Func(f) => f.trace(func),
WasmCompositeType::Struct(a) => a.trace(func),
}
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
WasmCompositeType::Array(a) => a.trace_mut(func),
WasmCompositeType::Func(f) => f.trace_mut(func),
WasmCompositeType::Struct(a) => a.trace_mut(func),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmSubType {
pub is_final: bool,
pub supertype: Option<EngineOrModuleTypeIndex>,
pub composite_type: WasmCompositeType,
}
impl fmt::Display for WasmSubType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_final && self.supertype.is_none() {
fmt::Display::fmt(&self.composite_type, f)
} else {
write!(f, "(sub")?;
if self.is_final {
write!(f, " final")?;
}
if let Some(sup) = self.supertype {
write!(f, " {sup}")?;
}
write!(f, " {})", self.composite_type)
}
}
}
#[allow(missing_docs)]
impl WasmSubType {
#[inline]
pub fn is_func(&self) -> bool {
self.composite_type.is_func()
}
#[inline]
pub fn as_func(&self) -> Option<&WasmFuncType> {
self.composite_type.as_func()
}
#[inline]
pub fn unwrap_func(&self) -> &WasmFuncType {
self.composite_type.unwrap_func()
}
#[inline]
pub fn is_array(&self) -> bool {
self.composite_type.is_array()
}
#[inline]
pub fn as_array(&self) -> Option<&WasmArrayType> {
self.composite_type.as_array()
}
#[inline]
pub fn unwrap_array(&self) -> &WasmArrayType {
self.composite_type.unwrap_array()
}
#[inline]
pub fn is_struct(&self) -> bool {
self.composite_type.is_struct()
}
#[inline]
pub fn as_struct(&self) -> Option<&WasmStructType> {
self.composite_type.as_struct()
}
#[inline]
pub fn unwrap_struct(&self) -> &WasmStructType {
self.composite_type.unwrap_struct()
}
}
impl TypeTrace for WasmSubType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
if let Some(sup) = self.supertype {
func(sup)?;
}
self.composite_type.trace(func)
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
if let Some(sup) = self.supertype.as_mut() {
func(sup)?;
}
self.composite_type.trace_mut(func)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmRecGroup {
pub types: Box<[WasmSubType]>,
}
impl TypeTrace for WasmRecGroup {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
for ty in self.types.iter() {
ty.trace(func)?;
}
Ok(())
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
for ty in self.types.iter_mut() {
ty.trace_mut(func)?;
}
Ok(())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct FuncIndex(u32);
entity_impl!(FuncIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedFuncIndex(u32);
entity_impl!(DefinedFuncIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedTableIndex(u32);
entity_impl!(DefinedTableIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedMemoryIndex(u32);
entity_impl!(DefinedMemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct OwnedMemoryIndex(u32);
entity_impl!(OwnedMemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedGlobalIndex(u32);
entity_impl!(DefinedGlobalIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TableIndex(u32);
entity_impl!(TableIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct GlobalIndex(u32);
entity_impl!(GlobalIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct MemoryIndex(u32);
entity_impl!(MemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct ModuleInternedRecGroupIndex(u32);
entity_impl!(ModuleInternedRecGroupIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct EngineInternedRecGroupIndex(u32);
entity_impl!(EngineInternedRecGroupIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TypeIndex(u32);
entity_impl!(TypeIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct RecGroupRelativeTypeIndex(u32);
entity_impl!(RecGroupRelativeTypeIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct ModuleInternedTypeIndex(u32);
entity_impl!(ModuleInternedTypeIndex);
#[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct VMSharedTypeIndex(u32);
entity_impl!(VMSharedTypeIndex);
impl VMSharedTypeIndex {
#[inline]
pub fn new(value: u32) -> Self {
assert_ne!(
value,
u32::MAX,
"u32::MAX is reserved for the default value"
);
Self(value)
}
#[inline]
pub fn bits(&self) -> u32 {
self.0
}
}
impl Default for VMSharedTypeIndex {
#[inline]
fn default() -> Self {
Self(u32::MAX)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DataIndex(u32);
entity_impl!(DataIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct ElemIndex(u32);
entity_impl!(ElemIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TagIndex(u32);
entity_impl!(TagIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct StaticModuleIndex(u32);
entity_impl!(StaticModuleIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum EntityIndex {
Function(FuncIndex),
Table(TableIndex),
Memory(MemoryIndex),
Global(GlobalIndex),
}
impl From<FuncIndex> for EntityIndex {
fn from(idx: FuncIndex) -> EntityIndex {
EntityIndex::Function(idx)
}
}
impl From<TableIndex> for EntityIndex {
fn from(idx: TableIndex) -> EntityIndex {
EntityIndex::Table(idx)
}
}
impl From<MemoryIndex> for EntityIndex {
fn from(idx: MemoryIndex) -> EntityIndex {
EntityIndex::Memory(idx)
}
}
impl From<GlobalIndex> for EntityIndex {
fn from(idx: GlobalIndex) -> EntityIndex {
EntityIndex::Global(idx)
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EntityType {
Global(Global),
Memory(Memory),
Tag(Tag),
Table(Table),
Function(EngineOrModuleTypeIndex),
}
impl TypeTrace for EntityType {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
Self::Global(g) => g.trace(func),
Self::Table(t) => t.trace(func),
Self::Function(idx) => func(*idx),
Self::Memory(_) | Self::Tag(_) => Ok(()),
}
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
match self {
Self::Global(g) => g.trace_mut(func),
Self::Table(t) => t.trace_mut(func),
Self::Function(idx) => func(idx),
Self::Memory(_) | Self::Tag(_) => Ok(()),
}
}
}
impl EntityType {
pub fn unwrap_global(&self) -> &Global {
match self {
EntityType::Global(g) => g,
_ => panic!("not a global"),
}
}
pub fn unwrap_memory(&self) -> &Memory {
match self {
EntityType::Memory(g) => g,
_ => panic!("not a memory"),
}
}
pub fn unwrap_tag(&self) -> &Tag {
match self {
EntityType::Tag(g) => g,
_ => panic!("not a tag"),
}
}
pub fn unwrap_table(&self) -> &Table {
match self {
EntityType::Table(g) => g,
_ => panic!("not a table"),
}
}
pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex {
match self {
EntityType::Function(g) => *g,
_ => panic!("not a func"),
}
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Global {
pub wasm_ty: crate::WasmValType,
pub mutability: bool,
}
impl TypeTrace for Global {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
let Global {
wasm_ty,
mutability: _,
} = self;
wasm_ty.trace(func)
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
let Global {
wasm_ty,
mutability: _,
} = self;
wasm_ty.trace_mut(func)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ConstExpr {
ops: SmallVec<[ConstOp; 2]>,
}
impl ConstExpr {
pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self {
let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>();
assert!(!ops.is_empty());
ConstExpr { ops }
}
pub fn from_wasmparser(
expr: wasmparser::ConstExpr<'_>,
) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> {
let mut iter = expr
.get_operators_reader()
.into_iter_with_offsets()
.peekable();
let mut ops = SmallVec::<[ConstOp; 2]>::new();
let mut escaped = SmallVec::<[FuncIndex; 1]>::new();
while let Some(res) = iter.next() {
let (op, offset) = res?;
if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() {
break;
}
if let wasmparser::Operator::RefFunc { function_index } = &op {
escaped.push(FuncIndex::from_u32(*function_index));
}
ops.push(ConstOp::from_wasmparser(op, offset)?);
}
Ok((Self { ops }, escaped))
}
pub fn ops(&self) -> &[ConstOp] {
&self.ops
}
pub fn provably_nonzero_i32(&self) -> bool {
assert!(self.ops.len() > 0);
if self.ops.len() > 1 {
return false;
}
match self.ops[0] {
ConstOp::I32Const(0) => false,
ConstOp::I32Const(_) => true,
_ => false,
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum ConstOp {
I32Const(i32),
I64Const(i64),
F32Const(u32),
F64Const(u64),
V128Const(u128),
GlobalGet(GlobalIndex),
RefI31,
RefNull,
RefFunc(FuncIndex),
I32Add,
I32Sub,
I32Mul,
I64Add,
I64Sub,
I64Mul,
StructNew {
struct_type_index: TypeIndex,
},
StructNewDefault {
struct_type_index: TypeIndex,
},
ArrayNew {
array_type_index: TypeIndex,
},
ArrayNewDefault {
array_type_index: TypeIndex,
},
ArrayNewFixed {
array_type_index: TypeIndex,
array_size: u32,
},
}
impl ConstOp {
pub fn from_wasmparser(op: wasmparser::Operator<'_>, offset: usize) -> WasmResult<Self> {
use wasmparser::Operator as O;
Ok(match op {
O::I32Const { value } => Self::I32Const(value),
O::I64Const { value } => Self::I64Const(value),
O::F32Const { value } => Self::F32Const(value.bits()),
O::F64Const { value } => Self::F64Const(value.bits()),
O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
O::RefNull { hty: _ } => Self::RefNull,
O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
O::RefI31 => Self::RefI31,
O::I32Add => Self::I32Add,
O::I32Sub => Self::I32Sub,
O::I32Mul => Self::I32Mul,
O::I64Add => Self::I64Add,
O::I64Sub => Self::I64Sub,
O::I64Mul => Self::I64Mul,
O::StructNew { struct_type_index } => Self::StructNew {
struct_type_index: TypeIndex::from_u32(struct_type_index),
},
O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
struct_type_index: TypeIndex::from_u32(struct_type_index),
},
O::ArrayNew { array_type_index } => Self::ArrayNew {
array_type_index: TypeIndex::from_u32(array_type_index),
},
O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
array_type_index: TypeIndex::from_u32(array_type_index),
},
O::ArrayNewFixed {
array_type_index,
array_size,
} => Self::ArrayNewFixed {
array_type_index: TypeIndex::from_u32(array_type_index),
array_size,
},
op => {
return Err(wasm_unsupported!(
"unsupported opcode in const expression at offset {offset:#x}: {op:?}",
));
}
})
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum IndexType {
I32,
I64,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct Limits {
pub min: u64,
pub max: Option<u64>,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Table {
pub idx_type: IndexType,
pub limits: Limits,
pub ref_type: WasmRefType,
}
impl TypeTrace for Table {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
let Table {
ref_type: wasm_ty,
idx_type: _,
limits: _,
} = self;
wasm_ty.trace(func)
}
fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
where
F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
{
let Table {
ref_type: wasm_ty,
idx_type: _,
limits: _,
} = self;
wasm_ty.trace_mut(func)
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Memory {
pub idx_type: IndexType,
pub limits: Limits,
pub shared: bool,
pub page_size_log2: u8,
}
pub const WASM32_MAX_SIZE: u64 = 1 << 32;
impl Memory {
pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
let log2 = 16;
assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
log2
};
pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
self.limits
.min
.checked_mul(self.page_size())
.ok_or(SizeOverflow)
}
pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
match self.limits.max {
Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
None => {
let min = self.minimum_byte_size()?;
Ok(min.max(self.max_size_based_on_index_type()))
}
}
}
pub fn page_size(&self) -> u64 {
debug_assert!(
self.page_size_log2 == 16 || self.page_size_log2 == 0,
"invalid page_size_log2: {}; must be 16 or 0",
self.page_size_log2
);
1 << self.page_size_log2
}
pub fn max_size_based_on_index_type(&self) -> u64 {
match self.idx_type {
IndexType::I64 =>
{
0_u64.wrapping_sub(self.page_size())
}
IndexType::I32 => WASM32_MAX_SIZE,
}
}
}
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
pub struct SizeOverflow;
impl fmt::Display for SizeOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("size overflow calculating memory size")
}
}
#[cfg(feature = "std")]
impl std::error::Error for SizeOverflow {}
impl From<wasmparser::MemoryType> for Memory {
fn from(ty: wasmparser::MemoryType) -> Memory {
let idx_type = match ty.memory64 {
false => IndexType::I32,
true => IndexType::I64,
};
let limits = Limits {
min: ty.initial,
max: ty.maximum,
};
let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
debug_assert!(
page_size_log2 == 16 || page_size_log2 == 0,
"invalid page_size_log2: {page_size_log2}; must be 16 or 0"
);
Memory {
idx_type,
limits,
shared: ty.shared,
page_size_log2,
}
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Tag {
pub ty: TypeIndex,
}
impl From<wasmparser::TagType> for Tag {
fn from(ty: wasmparser::TagType) -> Tag {
match ty.kind {
wasmparser::TagKind::Exception => Tag {
ty: TypeIndex::from_u32(ty.func_type_idx),
},
}
}
}
#[allow(missing_docs)]
pub trait TypeConvert {
fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> Global {
Global {
wasm_ty: self.convert_valtype(ty.content_type),
mutability: ty.mutable,
}
}
fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
let idx_type = match ty.table64 {
false => IndexType::I32,
true => IndexType::I64,
};
let limits = Limits {
min: ty.initial.try_into().unwrap(),
max: ty.maximum.map(|i| i.try_into().unwrap()),
};
Ok(Table {
idx_type,
limits,
ref_type: self.convert_ref_type(ty.element_type),
})
}
fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmSubType {
WasmSubType {
is_final: ty.is_final,
supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
composite_type: self.convert_composite_type(&ty.composite_type),
}
}
fn convert_composite_type(&self, ty: &wasmparser::CompositeType) -> WasmCompositeType {
assert!(!ty.shared);
match &ty.inner {
wasmparser::CompositeInnerType::Func(f) => {
WasmCompositeType::Func(self.convert_func_type(f))
}
wasmparser::CompositeInnerType::Array(a) => {
WasmCompositeType::Array(self.convert_array_type(a))
}
wasmparser::CompositeInnerType::Struct(s) => {
WasmCompositeType::Struct(self.convert_struct_type(s))
}
wasmparser::CompositeInnerType::Cont(_) => {
unimplemented!("continuation types")
}
}
}
fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmStructType {
WasmStructType {
fields: ty
.fields
.iter()
.map(|f| self.convert_field_type(f))
.collect(),
}
}
fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmArrayType {
WasmArrayType(self.convert_field_type(&ty.0))
}
fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmFieldType {
WasmFieldType {
element_type: self.convert_storage_type(&ty.element_type),
mutable: ty.mutable,
}
}
fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmStorageType {
match ty {
wasmparser::StorageType::I8 => WasmStorageType::I8,
wasmparser::StorageType::I16 => WasmStorageType::I16,
wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)),
}
}
fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmFuncType {
let params = ty
.params()
.iter()
.map(|t| self.convert_valtype(*t))
.collect();
let results = ty
.results()
.iter()
.map(|t| self.convert_valtype(*t))
.collect();
WasmFuncType::new(params, results)
}
fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmValType {
match ty {
wasmparser::ValType::I32 => WasmValType::I32,
wasmparser::ValType::I64 => WasmValType::I64,
wasmparser::ValType::F32 => WasmValType::F32,
wasmparser::ValType::F64 => WasmValType::F64,
wasmparser::ValType::V128 => WasmValType::V128,
wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)),
}
}
fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmRefType {
WasmRefType {
nullable: ty.is_nullable(),
heap_type: self.convert_heap_type(ty.heap_type()),
}
}
fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmHeapType {
match ty {
wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
wasmparser::AbstractHeapType::None => WasmHeapType::None,
wasmparser::AbstractHeapType::Exn
| wasmparser::AbstractHeapType::NoExn
| wasmparser::AbstractHeapType::Cont
| wasmparser::AbstractHeapType::NoCont => {
unimplemented!("unsupported heap type {ty:?}");
}
},
_ => unimplemented!("unsupported heap type {ty:?}"),
}
}
fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
}