use std::fmt;
use wasmtime_environ::{ir, wasm, EntityIndex};
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Mutability {
Const,
Var,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Limits {
min: u32,
max: Option<u32>,
}
impl Limits {
pub fn new(min: u32, max: Option<u32>) -> Limits {
Limits { min, max }
}
pub fn at_least(min: u32) -> Limits {
Limits::new(min, None)
}
pub fn min(&self) -> u32 {
self.min
}
pub fn max(&self) -> Option<u32> {
self.max
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum ValType {
I32,
I64,
F32,
F64,
V128,
ExternRef,
FuncRef,
}
impl fmt::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::ExternRef => write!(f, "externref"),
ValType::FuncRef => write!(f, "funcref"),
}
}
}
impl ValType {
pub fn is_num(&self) -> bool {
match self {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
_ => false,
}
}
pub fn is_ref(&self) -> bool {
match self {
ValType::ExternRef | ValType::FuncRef => true,
_ => false,
}
}
pub(crate) fn get_wasmtime_type(&self) -> ir::Type {
match self {
ValType::I32 => ir::types::I32,
ValType::I64 => ir::types::I64,
ValType::F32 => ir::types::F32,
ValType::F64 => ir::types::F64,
ValType::V128 => ir::types::I8X16,
ValType::ExternRef => wasmtime_runtime::ref_type(),
ValType::FuncRef => wasmtime_runtime::pointer_type(),
}
}
pub(crate) fn to_wasm_type(&self) -> wasm::WasmType {
match self {
Self::I32 => wasm::WasmType::I32,
Self::I64 => wasm::WasmType::I64,
Self::F32 => wasm::WasmType::F32,
Self::F64 => wasm::WasmType::F64,
Self::V128 => wasm::WasmType::V128,
Self::FuncRef => wasm::WasmType::FuncRef,
Self::ExternRef => wasm::WasmType::ExternRef,
}
}
pub(crate) fn from_wasm_type(ty: &wasm::WasmType) -> Self {
match ty {
wasm::WasmType::I32 => Self::I32,
wasm::WasmType::I64 => Self::I64,
wasm::WasmType::F32 => Self::F32,
wasm::WasmType::F64 => Self::F64,
wasm::WasmType::V128 => Self::V128,
wasm::WasmType::FuncRef => Self::FuncRef,
wasm::WasmType::ExternRef => Self::ExternRef,
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum ExternType {
Func(FuncType),
Global(GlobalType),
Table(TableType),
Memory(MemoryType),
}
macro_rules! accessors {
($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
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 {
accessors! {
(Func(FuncType) func unwrap_func)
(Global(GlobalType) global unwrap_global)
(Table(TableType) table unwrap_table)
(Memory(MemoryType) memory unwrap_memory)
}
}
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)
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FuncType {
params: Box<[ValType]>,
results: Box<[ValType]>,
}
impl FuncType {
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
FuncType { params, results }
}
pub fn params(&self) -> &[ValType] {
&self.params
}
pub fn results(&self) -> &[ValType] {
&self.results
}
pub(crate) fn to_wasm_func_type(&self) -> wasm::WasmFuncType {
wasm::WasmFuncType {
params: self.params.iter().map(|p| p.to_wasm_type()).collect(),
returns: self.results.iter().map(|r| r.to_wasm_type()).collect(),
}
}
pub(crate) fn get_wasmtime_signature(&self, pointer_type: ir::Type) -> ir::Signature {
use wasmtime_environ::ir::{AbiParam, ArgumentPurpose, Signature};
use wasmtime_jit::native;
let call_conv = native::call_conv();
let mut params = self
.params
.iter()
.map(|p| AbiParam::new(p.get_wasmtime_type()))
.collect::<Vec<_>>();
let returns = self
.results
.iter()
.map(|p| AbiParam::new(p.get_wasmtime_type()))
.collect::<Vec<_>>();
params.insert(
0,
AbiParam::special(pointer_type, ArgumentPurpose::VMContext),
);
params.insert(1, AbiParam::new(pointer_type));
Signature {
params,
returns,
call_conv,
}
}
pub(crate) fn from_wasm_func_type(signature: &wasm::WasmFuncType) -> FuncType {
let params = signature
.params
.iter()
.map(|p| ValType::from_wasm_type(p))
.collect::<Vec<_>>();
let results = signature
.returns
.iter()
.map(|r| ValType::from_wasm_type(r))
.collect::<Vec<_>>();
FuncType {
params: params.into_boxed_slice(),
results: results.into_boxed_slice(),
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
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(global: &wasm::Global) -> GlobalType {
let ty = ValType::from_wasm_type(&global.wasm_ty);
let mutability = if global.mutability {
Mutability::Var
} else {
Mutability::Const
};
GlobalType::new(ty, mutability)
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct TableType {
element: ValType,
limits: Limits,
}
impl TableType {
pub fn new(element: ValType, limits: Limits) -> TableType {
TableType { element, limits }
}
pub fn element(&self) -> &ValType {
&self.element
}
pub fn limits(&self) -> &Limits {
&self.limits
}
pub(crate) fn from_wasmtime_table(table: &wasm::Table) -> TableType {
let ty = match table.ty {
wasm::TableElementType::Func => ValType::FuncRef,
#[cfg(target_pointer_width = "64")]
wasm::TableElementType::Val(ir::types::R64) => ValType::ExternRef,
#[cfg(target_pointer_width = "32")]
wasm::TableElementType::Val(ir::types::R32) => ValType::ExternRef,
_ => panic!("only `funcref` and `externref` tables supported"),
};
let limits = Limits::new(table.minimum, table.maximum);
TableType::new(ty, limits)
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MemoryType {
limits: Limits,
}
impl MemoryType {
pub fn new(limits: Limits) -> MemoryType {
MemoryType { limits }
}
pub fn limits(&self) -> &Limits {
&self.limits
}
pub(crate) fn from_wasmtime_memory(memory: &wasm::Memory) -> MemoryType {
MemoryType::new(Limits::new(memory.minimum, memory.maximum))
}
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub(crate) enum EntityType<'module> {
Function(&'module wasm::WasmFuncType),
Table(&'module wasm::Table),
Memory(&'module wasm::Memory),
Global(&'module wasm::Global),
}
impl<'module> EntityType<'module> {
pub(crate) fn new(
entity_index: &EntityIndex,
module: &'module wasmtime_environ::Module,
) -> EntityType<'module> {
match entity_index {
EntityIndex::Function(func_index) => {
let sig = module.local.wasm_func_type(*func_index);
EntityType::Function(&sig)
}
EntityIndex::Table(table_index) => {
EntityType::Table(&module.local.table_plans[*table_index].table)
}
EntityIndex::Memory(memory_index) => {
EntityType::Memory(&module.local.memory_plans[*memory_index].memory)
}
EntityIndex::Global(global_index) => {
EntityType::Global(&module.local.globals[*global_index])
}
}
}
pub(crate) fn extern_type(&self) -> ExternType {
match self {
EntityType::Function(sig) => FuncType::from_wasm_func_type(sig).into(),
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
EntityType::Global(global) => GlobalType::from_wasmtime_global(global).into(),
}
}
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ImportType<'module> {
module: &'module str,
name: &'module str,
ty: EntityType<'module>,
}
impl<'module> ImportType<'module> {
pub(crate) fn new(
module: &'module str,
name: &'module str,
ty: EntityType<'module>,
) -> ImportType<'module> {
ImportType { module, name, ty }
}
pub fn module(&self) -> &'module str {
self.module
}
pub fn name(&self) -> &'module str {
self.name
}
pub fn ty(&self) -> ExternType {
self.ty.extern_type()
}
}
impl<'module> fmt::Debug for ImportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImportType")
.field("module", &self.module().to_owned())
.field("name", &self.name().to_owned())
.field("ty", &self.ty())
.finish()
}
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ExportType<'module> {
name: &'module str,
ty: EntityType<'module>,
}
impl<'module> ExportType<'module> {
pub(crate) fn new(name: &'module str, ty: EntityType<'module>) -> ExportType<'module> {
ExportType { name, ty }
}
pub fn name(&self) -> &'module str {
self.name
}
pub fn ty(&self) -> ExternType {
self.ty.extern_type()
}
}
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()
}
}