use std::fmt;
use wasmtime_environ::wasm::{EntityType, WasmFuncType};
use wasmtime_environ::{ir, wasm};
use wasmtime_jit::TypeTables;
pub(crate) mod matching;
#[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,
wasm::WasmType::ExnRef => unimplemented!(),
}
}
}
#[derive(Debug, Clone)]
pub enum ExternType {
Func(FuncType),
Global(GlobalType),
Table(TableType),
Memory(MemoryType),
Instance(InstanceType),
Module(ModuleType),
}
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)
(Module(ModuleType) module unwrap_module)
(Instance(InstanceType) instance unwrap_instance)
}
pub(crate) fn from_wasmtime(
types: &TypeTables,
ty: &wasmtime_environ::wasm::EntityType,
) -> ExternType {
match ty {
EntityType::Function(idx) => {
let sig = &types.wasm_signatures[*idx];
FuncType::from_wasm_func_type(sig).into()
}
EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(),
EntityType::Module(ty) => {
let ty = &types.module_signatures[*ty];
ModuleType::from_wasmtime(types, ty).into()
}
EntityType::Instance(ty) => {
let ty = &types.instance_signatures[*ty];
InstanceType::from_wasmtime(types, ty).into()
}
EntityType::Event(_) => unimplemented!("wasm event support"),
}
}
}
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<ModuleType> for ExternType {
fn from(ty: ModuleType) -> ExternType {
ExternType::Module(ty)
}
}
impl From<InstanceType> for ExternType {
fn from(ty: InstanceType) -> ExternType {
ExternType::Instance(ty)
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FuncType {
sig: WasmFuncType,
}
impl FuncType {
pub fn new(
params: impl IntoIterator<Item = ValType>,
results: impl IntoIterator<Item = ValType>,
) -> FuncType {
FuncType {
sig: WasmFuncType {
params: params.into_iter().map(|t| t.to_wasm_type()).collect(),
returns: results.into_iter().map(|t| t.to_wasm_type()).collect(),
},
}
}
pub fn params(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
self.sig.params.iter().map(ValType::from_wasm_type)
}
pub fn results(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
self.sig.returns.iter().map(ValType::from_wasm_type)
}
pub(crate) fn as_wasm_func_type(&self) -> &wasm::WasmFuncType {
&self.sig
}
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 = vec![
AbiParam::special(pointer_type, ArgumentPurpose::VMContext),
AbiParam::new(pointer_type),
];
params.extend(self.params().map(|p| AbiParam::new(p.get_wasmtime_type())));
let returns = self
.results()
.map(|p| AbiParam::new(p.get_wasmtime_type()))
.collect::<Vec<_>>();
Signature {
params,
returns,
call_conv,
}
}
pub(crate) fn from_wasm_func_type(sig: &wasm::WasmFuncType) -> FuncType {
FuncType { sig: sig.clone() }
}
}
#[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(Debug, Clone)]
pub struct ModuleType {
imports: Vec<(String, Option<String>, ExternType)>,
exports: Vec<(String, ExternType)>,
}
impl ModuleType {
pub fn new() -> ModuleType {
ModuleType {
imports: Vec::new(),
exports: Vec::new(),
}
}
pub fn add_named_export(&mut self, name: &str, ty: ExternType) {
self.exports.push((name.to_string(), ty));
}
pub fn add_named_import(&mut self, module: &str, field: Option<&str>, ty: ExternType) {
self.imports
.push((module.to_string(), field.map(|f| f.to_string()), ty));
}
pub fn imports(&self) -> impl ExactSizeIterator<Item = ImportType<'_>> {
self.imports.iter().map(|(module, name, ty)| ImportType {
module,
name: name.as_deref(),
ty: EntityOrExtern::Extern(ty),
})
}
pub fn exports(&self) -> impl ExactSizeIterator<Item = ExportType<'_>> {
self.exports.iter().map(|(name, ty)| ExportType {
name,
ty: EntityOrExtern::Extern(ty),
})
}
pub(crate) fn from_wasmtime(
types: &TypeTables,
ty: &wasmtime_environ::ModuleSignature,
) -> ModuleType {
let exports = &types.instance_signatures[ty.exports].exports;
ModuleType {
exports: exports
.iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
.collect(),
imports: ty
.imports
.iter()
.map(|(m, name, ty)| {
(
m.to_string(),
name.as_ref().map(|n| n.to_string()),
ExternType::from_wasmtime(types, ty),
)
})
.collect(),
}
}
}
#[derive(Debug, Clone)]
pub struct InstanceType {
exports: Vec<(String, ExternType)>,
}
impl InstanceType {
pub fn new() -> InstanceType {
InstanceType {
exports: Vec::new(),
}
}
pub fn add_named_export(&mut self, name: &str, ty: ExternType) {
self.exports.push((name.to_string(), ty));
}
pub fn exports(&self) -> impl ExactSizeIterator<Item = ExportType<'_>> {
self.exports.iter().map(|(name, ty)| ExportType {
name,
ty: EntityOrExtern::Extern(ty),
})
}
pub(crate) fn from_wasmtime(
types: &TypeTables,
ty: &wasmtime_environ::InstanceSignature,
) -> InstanceType {
InstanceType {
exports: ty
.exports
.iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
.collect(),
}
}
}
#[derive(Clone)]
pub struct ImportType<'module> {
module: &'module str,
name: Option<&'module str>,
ty: EntityOrExtern<'module>,
}
#[derive(Clone)]
enum EntityOrExtern<'a> {
Entity(EntityType, &'a TypeTables),
Extern(&'a ExternType),
}
impl<'module> ImportType<'module> {
pub(crate) fn new(
module: &'module str,
name: Option<&'module str>,
ty: EntityType,
types: &'module TypeTables,
) -> ImportType<'module> {
ImportType {
module,
name,
ty: EntityOrExtern::Entity(ty, types),
}
}
pub fn module(&self) -> &'module str {
self.module
}
pub fn name(&self) -> Option<&'module str> {
self.name
}
pub fn ty(&self) -> ExternType {
match &self.ty {
EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
EntityOrExtern::Extern(e) => (*e).clone(),
}
}
}
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: EntityOrExtern<'module>,
}
impl<'module> ExportType<'module> {
pub(crate) fn new(
name: &'module str,
ty: EntityType,
types: &'module TypeTables,
) -> ExportType<'module> {
ExportType {
name,
ty: EntityOrExtern::Entity(ty, types),
}
}
pub fn name(&self) -> &'module str {
self.name
}
pub fn ty(&self) -> ExternType {
match &self.ty {
EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
EntityOrExtern::Extern(e) => (*e).clone(),
}
}
}
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()
}
}