pub use wasmparser;
use cranelift_entity::entity_impl;
use serde_derive::{Deserialize, Serialize};
use std::fmt;
mod error;
pub use error::*;
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 canonicalize<F>(&mut self, module_to_engine: &mut F)
where
F: FnMut(ModuleInternedTypeIndex) -> u32,
{
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(())
}
})
.unwrap()
}
}
#[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 {
pub fn is_vmgcref_type(&self) -> bool {
self.is_gc_heap_type()
|| matches!(
self,
WasmValType::Ref(WasmRefType {
heap_type: WasmHeapType::I31,
nullable: _,
})
)
}
pub fn is_gc_heap_type(&self) -> bool {
match self {
WasmValType::Ref(r) => r.is_gc_heap_type(),
_ => false,
}
}
}
#[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,
};
pub fn is_gc_heap_type(&self) -> bool {
self.heap_type.is_gc_heap_type()
}
}
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(u32),
Module(ModuleInternedTypeIndex),
}
impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex {
fn from(i: ModuleInternedTypeIndex) -> Self {
Self::Module(i)
}
}
impl fmt::Display for EngineOrModuleTypeIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Engine(i) => write!(f, "(engine {i})"),
Self::Module(i) => write!(f, "(module {})", 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<u32> {
match self {
Self::Engine(e) => Some(e),
Self::Module(_) => None,
}
}
pub fn unwrap_engine_type_index(self) -> u32 {
self.as_engine_type_index()
.expect("`unwrap_engine_type_index` on module type index")
}
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::Engine(_) => None,
}
}
pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex {
self.as_module_type_index()
.expect("`unwrap_module_type_index` on engine type index")
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WasmHeapType {
Extern,
Func,
Concrete(EngineOrModuleTypeIndex),
NoFunc,
Any,
I31,
None,
}
impl fmt::Display for WasmHeapType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Extern => write!(f, "extern"),
Self::Func => write!(f, "func"),
Self::Concrete(i) => write!(f, "{i}"),
Self::NoFunc => write!(f, "nofunc"),
Self::Any => write!(f, "any"),
Self::I31 => write!(f, "i31"),
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::Concrete(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::Concrete(i) => func(i),
_ => Ok(()),
}
}
}
impl WasmHeapType {
pub fn is_gc_heap_type(&self) -> bool {
match self {
Self::Extern | Self::Any => true,
Self::Concrete(_) => false,
Self::I31 => false,
Self::None => false,
Self::Func | Self::NoFunc => false,
}
}
}
#[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 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_gc_heap_type()).count();
let non_i31_gc_ref_returns_count = returns.iter().filter(|r| r.is_gc_heap_type()).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
}
}
#[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 TypeIndex(u32);
entity_impl!(TypeIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct ModuleInternedTypeIndex(u32);
entity_impl!(ModuleInternedTypeIndex);
#[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(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum GlobalInit {
I32Const(i32),
I64Const(i64),
F32Const(u32),
F64Const(u64),
V128Const(u128),
GetGlobal(GlobalIndex),
RefI31Const(i32),
RefNullConst,
RefFunc(FuncIndex),
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Table {
pub wasm_ty: WasmRefType,
pub minimum: u32,
pub maximum: Option<u32>,
}
impl TypeTrace for Table {
fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
where
F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
{
let Table {
wasm_ty,
minimum: _,
maximum: _,
} = 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 {
wasm_ty,
minimum: _,
maximum: _,
} = self;
wasm_ty.trace_mut(func)
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Memory {
pub minimum: u64,
pub maximum: Option<u64>,
pub shared: bool,
pub memory64: bool,
}
impl From<wasmparser::MemoryType> for Memory {
fn from(ty: wasmparser::MemoryType) -> Memory {
Memory {
minimum: ty.initial,
maximum: ty.maximum,
shared: ty.shared,
memory64: ty.memory64,
}
}
}
#[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),
},
}
}
}
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) -> Table {
Table {
wasm_ty: self.convert_ref_type(ty.element_type),
minimum: ty.initial,
maximum: ty.maximum,
}
}
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::Extern => WasmHeapType::Extern,
wasmparser::HeapType::Func => WasmHeapType::Func,
wasmparser::HeapType::NoFunc => WasmHeapType::NoFunc,
wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
wasmparser::HeapType::Any => WasmHeapType::Any,
wasmparser::HeapType::I31 => WasmHeapType::I31,
wasmparser::HeapType::None => WasmHeapType::None,
wasmparser::HeapType::Exn
| wasmparser::HeapType::NoExtern
| wasmparser::HeapType::Eq
| wasmparser::HeapType::Struct
| wasmparser::HeapType::Array => {
unimplemented!("unsupported heap type {ty:?}");
}
}
}
fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
}