use crate::collections::HashMap;
use crate::{
Call, DebugInfo, Hash, Inst, Rtti, StaticString, Type, VariantRtti, VmError, VmErrorKind,
};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Unit {
instructions: Vec<Inst>,
functions: HashMap<Hash, UnitFn>,
types: HashMap<Hash, UnitTypeInfo>,
static_strings: Vec<Arc<StaticString>>,
static_bytes: Vec<Vec<u8>>,
static_object_keys: Vec<Box<[String]>>,
rtti: HashMap<Hash, Arc<Rtti>>,
variant_rtti: HashMap<Hash, Arc<VariantRtti>>,
debug: Option<Box<DebugInfo>>,
}
impl Unit {
#[allow(clippy::too_many_arguments)]
pub fn new(
instructions: Vec<Inst>,
functions: HashMap<Hash, UnitFn>,
types: HashMap<Hash, UnitTypeInfo>,
static_strings: Vec<Arc<StaticString>>,
static_bytes: Vec<Vec<u8>>,
static_object_keys: Vec<Box<[String]>>,
rtti: HashMap<Hash, Arc<Rtti>>,
variant_rtti: HashMap<Hash, Arc<VariantRtti>>,
debug: Option<Box<DebugInfo>>,
) -> Self {
Self {
instructions,
functions,
types,
static_strings,
static_bytes,
static_object_keys,
rtti,
variant_rtti,
debug,
}
}
pub fn lookup_type(&self, hash: Hash) -> Option<&UnitTypeInfo> {
self.types.get(&hash)
}
pub fn debug_info(&self) -> Option<&DebugInfo> {
let debug = self.debug.as_ref()?;
Some(&**debug)
}
pub fn instruction_at(&self, ip: usize) -> Option<&Inst> {
self.instructions.get(ip)
}
pub fn iter_static_strings(&self) -> impl Iterator<Item = &Arc<StaticString>> + '_ {
self.static_strings.iter()
}
pub fn iter_static_object_keys(&self) -> impl Iterator<Item = (usize, &[String])> + '_ {
let mut it = self.static_object_keys.iter().enumerate();
std::iter::from_fn(move || {
let (n, s) = it.next()?;
Some((n, &s[..]))
})
}
pub fn iter_instructions(&self) -> impl Iterator<Item = Inst> + '_ {
self.instructions.iter().copied()
}
pub fn iter_functions(&self) -> impl Iterator<Item = (Hash, &UnitFn)> + '_ {
self.functions.iter().map(|(h, f)| (*h, f))
}
pub fn iter_types(&self) -> impl Iterator<Item = (Hash, &UnitTypeInfo)> + '_ {
self.types.iter().map(|(h, v)| (*h, v))
}
pub fn lookup_string(&self, slot: usize) -> Result<&Arc<StaticString>, VmError> {
Ok(self
.static_strings
.get(slot)
.ok_or_else(|| VmErrorKind::MissingStaticString { slot })?)
}
pub fn lookup_bytes(&self, slot: usize) -> Result<&[u8], VmError> {
Ok(self
.static_bytes
.get(slot)
.ok_or_else(|| VmErrorKind::MissingStaticString { slot })?
.as_ref())
}
pub fn lookup_object_keys(&self, slot: usize) -> Option<&[String]> {
self.static_object_keys.get(slot).map(|keys| &keys[..])
}
pub fn lookup_rtti(&self, hash: Hash) -> Option<&Arc<Rtti>> {
self.rtti.get(&hash)
}
pub fn lookup_variant_rtti(&self, hash: Hash) -> Option<&Arc<VariantRtti>> {
self.variant_rtti.get(&hash)
}
pub fn lookup(&self, hash: Hash) -> Option<UnitFn> {
self.functions.get(&hash).copied()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum UnitFn {
Offset {
offset: usize,
call: Call,
args: usize,
},
UnitStruct {
hash: Hash,
},
TupleStruct {
hash: Hash,
args: usize,
},
UnitVariant {
hash: Hash,
},
TupleVariant {
hash: Hash,
args: usize,
},
}
impl fmt::Display for UnitFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Offset { offset, call, args } => {
write!(f, "offset {}, {}, {}", offset, call, args)?;
}
Self::UnitStruct { hash } => {
write!(f, "unit {}", hash)?;
}
Self::TupleStruct { hash, args } => {
write!(f, "tuple {}, {}", hash, args)?;
}
Self::UnitVariant { hash } => {
write!(f, "empty-variant {}", hash)?;
}
Self::TupleVariant { hash, args } => {
write!(f, "tuple-variant {}, {}", hash, args)?;
}
}
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UnitTypeInfo {
pub hash: Hash,
pub type_of: Type,
}
#[cfg(test)]
mod tests {
use super::Unit;
fn assert_send_sync<T>()
where
T: Send + Sync,
{
}
#[test]
fn assert_thread_safe_unit() {
assert_send_sync::<Unit>();
}
}