#[cfg(feature = "byte-code")]
mod byte_code;
mod storage;
use core::fmt;
use ::rust_alloc::sync::Arc;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate as rune;
use crate::alloc::prelude::*;
use crate::alloc::{self, Box, String, Vec};
use crate::hash;
use crate::runtime::{Call, ConstValue, DebugInfo, Inst, InstAddress, Rtti, StaticString};
use crate::Hash;
pub use self::storage::{ArrayUnit, EncodeError, UnitEncoder, UnitStorage};
pub(crate) use self::storage::{BadInstruction, BadJump};
#[cfg(feature = "byte-code")]
pub use self::byte_code::ByteCodeUnit;
#[cfg(not(rune_byte_code))]
pub type DefaultStorage = ArrayUnit;
#[cfg(rune_byte_code)]
pub type DefaultStorage = ByteCodeUnit;
#[derive(Debug, TryClone, Default, Serialize, Deserialize)]
#[serde(bound = "S: Serialize + DeserializeOwned")]
#[try_clone(bound = {S: TryClone})]
pub struct Unit<S = DefaultStorage> {
#[serde(flatten)]
logic: Logic<S>,
debug: Option<Box<DebugInfo>>,
}
assert_impl!(Unit<DefaultStorage>: Send + Sync);
#[derive(Debug, TryClone, Default, Serialize, Deserialize)]
#[serde(rename = "Unit")]
#[try_clone(bound = {S: TryClone})]
pub struct Logic<S = DefaultStorage> {
storage: S,
functions: hash::Map<UnitFn>,
static_strings: Vec<Arc<StaticString>>,
static_bytes: Vec<Vec<u8>>,
static_object_keys: Vec<Box<[String]>>,
drop_sets: Vec<Arc<[InstAddress]>>,
rtti: hash::Map<Arc<Rtti>>,
constants: hash::Map<ConstValue>,
}
impl<S> Unit<S> {
#[inline]
pub fn from_parts(data: Logic<S>, debug: Option<DebugInfo>) -> alloc::Result<Self> {
Ok(Self {
logic: data,
debug: debug.map(Box::try_new).transpose()?,
})
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub(crate) fn new(
storage: S,
functions: hash::Map<UnitFn>,
static_strings: Vec<Arc<StaticString>>,
static_bytes: Vec<Vec<u8>>,
static_object_keys: Vec<Box<[String]>>,
drop_sets: Vec<Arc<[InstAddress]>>,
rtti: hash::Map<Arc<Rtti>>,
debug: Option<Box<DebugInfo>>,
constants: hash::Map<ConstValue>,
) -> Self {
Self {
logic: Logic {
storage,
functions,
static_strings,
static_bytes,
static_object_keys,
drop_sets,
rtti,
constants,
},
debug,
}
}
#[inline]
pub fn logic(&self) -> &Logic<S> {
&self.logic
}
#[inline]
pub fn debug_info(&self) -> Option<&DebugInfo> {
Some(&**self.debug.as_ref()?)
}
#[inline]
pub(crate) fn instructions(&self) -> &S {
&self.logic.storage
}
#[cfg(feature = "cli")]
#[inline]
pub(crate) fn iter_static_strings(&self) -> impl Iterator<Item = &Arc<StaticString>> + '_ {
self.logic.static_strings.iter()
}
#[cfg(feature = "cli")]
#[inline]
pub(crate) fn iter_static_bytes(&self) -> impl Iterator<Item = &[u8]> + '_ {
self.logic.static_bytes.iter().map(|v| &**v)
}
#[cfg(feature = "cli")]
#[inline]
pub(crate) fn iter_static_drop_sets(&self) -> impl Iterator<Item = &[InstAddress]> + '_ {
self.logic.drop_sets.iter().map(|v| &**v)
}
#[cfg(feature = "cli")]
#[inline]
pub(crate) fn iter_constants(&self) -> impl Iterator<Item = (&Hash, &ConstValue)> + '_ {
self.logic.constants.iter()
}
#[cfg(feature = "cli")]
#[inline]
pub(crate) fn iter_static_object_keys(&self) -> impl Iterator<Item = (usize, &[String])> + '_ {
use core::iter;
let mut it = self.logic.static_object_keys.iter().enumerate();
iter::from_fn(move || {
let (n, s) = it.next()?;
Some((n, &s[..]))
})
}
#[cfg(feature = "cli")]
#[inline]
pub(crate) fn iter_functions(&self) -> impl Iterator<Item = (Hash, &UnitFn)> + '_ {
self.logic.functions.iter().map(|(h, f)| (*h, f))
}
#[inline]
pub(crate) fn lookup_string(&self, slot: usize) -> Option<&Arc<StaticString>> {
self.logic.static_strings.get(slot)
}
#[inline]
pub(crate) fn lookup_bytes(&self, slot: usize) -> Option<&[u8]> {
Some(self.logic.static_bytes.get(slot)?)
}
#[inline]
pub(crate) fn lookup_object_keys(&self, slot: usize) -> Option<&[String]> {
Some(self.logic.static_object_keys.get(slot)?)
}
#[inline]
pub(crate) fn lookup_drop_set(&self, set: usize) -> Option<&[InstAddress]> {
Some(self.logic.drop_sets.get(set)?)
}
#[inline]
pub(crate) fn lookup_rtti(&self, hash: &Hash) -> Option<&Arc<Rtti>> {
self.logic.rtti.get(hash)
}
#[inline]
pub(crate) fn function(&self, hash: &Hash) -> Option<&UnitFn> {
self.logic.functions.get(hash)
}
#[inline]
pub(crate) fn constant(&self, hash: &Hash) -> Option<&ConstValue> {
self.logic.constants.get(hash)
}
}
impl<S> Unit<S>
where
S: UnitStorage,
{
#[inline]
pub(crate) fn translate(&self, jump: usize) -> Result<usize, BadJump> {
self.logic.storage.translate(jump)
}
#[inline]
pub(crate) fn instruction_at(
&self,
ip: usize,
) -> Result<Option<(Inst, usize)>, BadInstruction> {
self.logic.storage.get(ip)
}
#[cfg(feature = "emit")]
#[inline]
pub(crate) fn iter_instructions(&self) -> impl Iterator<Item = (usize, Inst)> + '_ {
self.logic.storage.iter()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[non_exhaustive]
pub(crate) enum UnitFn {
Offset {
offset: usize,
call: Call,
args: usize,
captures: Option<usize>,
},
EmptyStruct {
hash: Hash,
},
TupleStruct {
hash: Hash,
args: usize,
},
}
impl TryClone for UnitFn {
#[inline]
fn try_clone(&self) -> alloc::Result<Self> {
Ok(*self)
}
}
impl fmt::Display for UnitFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Offset {
offset,
call,
args,
captures,
} => {
write!(
f,
"offset offset={offset}, call={call}, args={args}, captures={captures:?}"
)?;
}
Self::EmptyStruct { hash } => {
write!(f, "unit hash={hash}")?;
}
Self::TupleStruct { hash, args } => {
write!(f, "tuple hash={hash}, args={args}")?;
}
}
Ok(())
}
}
#[cfg(test)]
static_assertions::assert_impl_all!(Unit: Send, Sync);