use std::fmt::{Debug, Formatter};
use anyhow::{anyhow, bail, ensure, Result};
use crate::binary_grammar::{
Function, FunctionType, GlobalType, ImportDescription, Instruction, MemoryType, RefType,
TableType, ValueType,
};
#[derive(Debug, Copy, Clone)]
pub enum Ref {
Null,
FunctionAddr(usize),
RefExtern(usize),
}
#[derive(Debug, Clone)]
pub enum Value {
I32(i32),
I64(i64),
F32(f32),
F64(f64),
V128(i128),
Ref(Ref),
}
impl Value {
pub fn default(value_type: &ValueType) -> Self {
match value_type {
ValueType::I32 => Value::I32(0),
ValueType::I64 => Value::I64(0),
ValueType::F32 => Value::F32(0.0),
ValueType::F64 => Value::F64(0.0),
ValueType::V128 => Value::V128(0),
ValueType::Ref(_) => Value::Ref(Ref::Null),
}
}
}
#[macro_export]
macro_rules! try_from_value {
($value_variant: ident, $ty:ty) => {
impl TryFrom<Value> for $ty {
type Error = anyhow::Error;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
match value {
Value::$value_variant(v) => Ok(v),
_ => bail!(""),
}
}
}
impl TryFrom<$ty> for Value {
type Error = anyhow::Error;
fn try_from(value: $ty) -> std::result::Result<Self, Self::Error> {
Ok(Value::$value_variant(value))
}
}
};
}
try_from_value!(I32, i32);
try_from_value!(I64, i64);
try_from_value!(F32, f32);
try_from_value!(F64, f64);
try_from_value!(V128, i128);
#[derive(Debug)]
pub enum ResultKind {
Values(Vec<Value>),
Trap,
}
#[derive(Debug, Clone)]
pub struct ModuleInstance<'a> {
pub types: Vec<FunctionType>,
pub function_addrs: Vec<usize>,
pub table_addrs: Vec<usize>,
pub mem_addrs: Vec<usize>,
pub global_addrs: Vec<usize>,
pub tag_addrs: Vec<usize>,
pub elem_addrs: Vec<usize>,
pub data_addrs: Vec<usize>,
pub exports: Vec<ExportInstance<'a>>,
}
impl<'a> ModuleInstance<'a> {
pub const fn new(types: Vec<FunctionType>) -> Self {
Self {
types,
function_addrs: vec![],
table_addrs: vec![],
mem_addrs: vec![],
global_addrs: vec![],
tag_addrs: vec![],
elem_addrs: vec![],
data_addrs: vec![],
exports: vec![],
}
}
}
impl<'a> Default for ModuleInstance<'a> {
fn default() -> Self {
Self::new(vec![])
}
}
pub enum FunctionInstance<'a> {
Local {
function_type: FunctionType,
module: ModuleInstance<'a>,
code: Function,
},
Host {
function_type: FunctionType,
code: Box<dyn Fn()>,
},
}
impl<'a> Debug for FunctionInstance<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Local {
function_type,
module,
code,
} => f
.debug_struct("FunctionInstance Local")
.field("function_type: {:?}", function_type)
.field("module: {:?}", module)
.field("code: {:?}", code)
.finish(),
Self::Host { function_type, .. } => f
.debug_struct("FunctionInstance Host")
.field("function_type {:?}", function_type)
.finish(),
}
}
}
#[derive(Debug)]
pub struct TableInstance {
pub table_type: TableType,
pub elem: Vec<Ref>,
}
#[derive(Debug)]
pub struct MemoryInstance {
pub memory_type: MemoryType,
pub data: Vec<u8>,
}
#[derive(Debug)]
pub struct GlobalInstance {
pub global_type: GlobalType,
pub value: Value,
}
#[derive(Debug)]
pub struct ElementInstance {
pub ref_type: RefType,
pub elem: Vec<Ref>,
}
#[derive(Debug)]
pub struct TagInstance {
pub tag_type: FunctionType,
}
#[derive(Debug)]
pub struct DataInstance<'a> {
pub data: &'a [u8],
}
pub enum ImportValue {
Func(Box<dyn Fn()>),
Table(Vec<Ref>),
Memory(Vec<u8>),
Global(Value),
}
impl Debug for ImportValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Func(_) => f.debug_tuple("ImportValue Function").finish(),
Self::Table(t) => f.debug_tuple("Import Value Table").field(t).finish(),
Self::Memory(m) => f.debug_tuple("Import Value Memory").field(m).finish(),
Self::Global(g) => f.debug_tuple("Import Value Global").field(g).finish(),
}
}
}
#[derive(Debug)]
pub struct ExternalImport<'a> {
pub module: &'a str,
pub name: &'a str,
pub value: ImportValue,
}
#[derive(Debug, Clone)]
pub enum ExternalValue {
Function { addr: usize },
Table { addr: usize },
Memory { addr: usize },
Global { addr: usize },
Tag { addr: usize },
}
#[derive(Debug, Clone)]
pub struct ExportInstance<'a> {
pub name: &'a str,
pub value: ExternalValue,
}
#[derive(Debug)]
pub struct Label {
pub arity: u32,
pub continuation: Vec<Instruction>,
}
#[derive(Debug, Default, Clone)]
pub struct Frame<'a> {
pub arity: usize,
pub locals: Vec<Value>,
pub module: ModuleInstance<'a>,
}
#[derive(Debug)]
pub enum Entry<'a> {
Value(Value),
Label(Label),
Activation(Frame<'a>),
}
#[derive(Debug)]
pub struct Stack<'a>(Vec<Entry<'a>>);
impl<'a> Stack<'a> {
pub fn new() -> Self {
Self(vec![])
}
pub fn push(&mut self, entry: Entry<'a>) {
self.0.push(entry);
}
pub fn push_value<T: TryInto<Value, Error = anyhow::Error>>(&mut self, value: T) -> Result<()> {
self.push(Entry::Value(value.try_into()?));
Ok(())
}
pub fn pop(&mut self) -> Result<Entry<'a>> {
self.0.pop().ok_or(anyhow!("Empty stack."))
}
pub fn pop_as_value<T: TryFrom<Value, Error = anyhow::Error>>(&mut self) -> Result<T> {
if let Entry::Value(v) = self.pop()? {
v.try_into()
} else {
bail!("Failed to pop entry as value.")
}
}
pub fn pop_n(&mut self, n: usize) -> Result<Vec<Entry<'a>>> {
ensure!(self.len() >= n, "Stack must have at least {} entries", n);
Ok(self.0.split_off(self.len() - n))
}
pub fn len(&self) -> usize {
self.0.len()
}
}