use std::{
cell::RefCell,
collections::VecDeque,
fmt::{Debug, Display},
ptr::addr_eq,
sync::Arc,
};
use arc_gc::{
arc::{GCArc, GCArcWeak},
gc::GC,
traceable::GCTraceable,
};
use base64::{engine::general_purpose, Engine as _};
use crate::lambda::runnable::RuntimeError;
use super::{
lambda::{
definition::OnionLambdaDefinition, vm_instructions::instruction_set::VMInstructionPackage,
},
lazy_set::OnionLazySet,
named::OnionNamed,
pair::OnionPair,
tuple::OnionTuple,
};
#[derive(Clone)]
pub struct OnionObjectCell(pub RefCell<OnionObject>);
impl OnionObjectCell {
#[inline(always)]
pub fn with_data<T, F>(&self, f: F) -> Result<T, RuntimeError>
where
F: FnOnce(&OnionObject) -> Result<T, RuntimeError>,
{
self.0
.try_borrow()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `with_data`".to_string(),
)
})?
.with_data(f)
}
#[inline(always)]
pub fn with_data_mut<T, F>(&self, f: F) -> Result<T, RuntimeError>
where
F: FnOnce(&mut OnionObject) -> Result<T, RuntimeError>,
{
self.0
.try_borrow_mut()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `with_data_mut`".to_string(),
)
})?
.with_data_mut(f)
}
#[inline(always)]
pub fn with_data_ref_mut<T, F>(&self, f: F) -> Result<T, RuntimeError>
where
F: FnOnce(&mut OnionObject) -> Result<T, RuntimeError>,
{
self.0
.try_borrow_mut()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `with_data_ref_mut`".to_string(),
)
})?
.with_data_ref_mut(f)
}
#[inline(always)]
pub fn with_attribute<T, F>(&self, key: &OnionObject, f: &F) -> Result<T, RuntimeError>
where
F: Fn(&OnionObject) -> Result<T, RuntimeError>,
{
self.0
.try_borrow()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `with_attribute`".to_string(),
)
})?
.with_attribute(key, f)
}
#[inline(always)]
pub fn with_attribute_mut<T, F>(&self, key: &OnionObject, f: &F) -> Result<T, RuntimeError>
where
F: Fn(&mut OnionObject) -> Result<T, RuntimeError>,
{
self.0
.try_borrow_mut()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `with_attribute_mut`".to_string(),
)
})?
.with_attribute_mut(key, f)
}
#[inline(always)]
pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
match self.0.try_borrow() {
Ok(obj) => obj.upgrade(collected),
Err(_) => {
}
}
}
#[inline(always)]
pub fn stabilize(self) -> OnionStaticObject {
OnionStaticObject::new(self.borrow().clone())
}
#[inline(always)]
pub fn equals(&self, other: &Self) -> Result<bool, RuntimeError> {
self.with_data(|obj| other.with_data(|other_obj| obj.equals(other_obj)))
}
#[inline(always)]
pub fn repr(&self, ptrs: &Vec<*const OnionObject>) -> Result<String, RuntimeError> {
self.with_data(|obj| obj.repr(ptrs))
}
#[inline(always)]
pub fn try_borrow(&self) -> Result<std::cell::Ref<OnionObject>, RuntimeError> {
self.0.try_borrow().map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `try_borrow`".to_string(),
)
})
}
#[inline(always)]
pub fn try_borrow_mut(&self) -> Result<std::cell::RefMut<OnionObject>, RuntimeError> {
self.0.try_borrow_mut().map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow OnionObjectCell at `try_borrow_mut`".to_string(),
)
})
}
}
impl std::ops::Deref for OnionObjectCell {
type Target = RefCell<OnionObject>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for OnionObjectCell {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<RefCell<OnionObject>> for OnionObjectCell {
fn from(cell: RefCell<OnionObject>) -> Self {
OnionObjectCell(cell)
}
}
impl From<OnionObject> for OnionObjectCell {
fn from(obj: OnionObject) -> Self {
OnionObjectCell(RefCell::new(obj))
}
}
impl GCTraceable<OnionObjectCell> for OnionObjectCell {
fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
self.0.borrow().collect(queue);
}
}
impl Debug for OnionObjectCell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0.borrow())
}
}
impl Display for OnionObjectCell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0.borrow())
}
}
#[derive(Clone)]
pub enum OnionObject {
Integer(i64),
Float(f64),
String(Arc<String>),
Bytes(Arc<Vec<u8>>),
Boolean(bool),
Range(i64, i64),
Null,
Undefined(Option<String>),
Tuple(OnionTuple),
Pair(OnionPair),
Named(OnionNamed),
LazySet(OnionLazySet),
InstructionPackage(Arc<VMInstructionPackage>),
Lambda(Box<OnionLambdaDefinition>),
Mut(GCArcWeak<OnionObjectCell>),
}
impl Debug for OnionObject {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.repr(&vec![])
.unwrap_or_else(|_| "BrokenReference".to_string())
)
}
}
impl GCTraceable<OnionObjectCell> for OnionObject {
fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
match self {
OnionObject::Mut(weak) => {
queue.push_back(weak.clone());
}
OnionObject::Tuple(tuple) => tuple.collect(queue),
OnionObject::Pair(pair) => pair.collect(queue),
OnionObject::Named(named) => named.collect(queue),
OnionObject::LazySet(lazy_set) => lazy_set.collect(queue),
OnionObject::Lambda(lambda) => lambda.collect(queue),
_ => {}
}
}
}
impl OnionObject {
pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
match self {
OnionObject::Mut(weak) => {
if let Some(strong) = weak.upgrade() {
collected.push(strong.clone());
}
}
OnionObject::Tuple(tuple) => tuple.upgrade(collected),
OnionObject::Pair(pair) => pair.upgrade(collected),
OnionObject::Named(named) => named.upgrade(collected),
OnionObject::LazySet(lazy_set) => lazy_set.upgrade(collected),
OnionObject::Lambda(lambda) => lambda.upgrade(collected),
_ => {}
}
}
#[inline(always)]
pub fn to_cell(self) -> OnionObjectCell {
OnionObjectCell(RefCell::new(self))
}
#[inline(always)]
pub fn stabilize(self) -> OnionStaticObject {
OnionStaticObject::new(self)
}
pub fn len(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Tuple(tuple) => tuple.len(),
OnionObject::String(s) => {
Ok(OnionStaticObject::new(OnionObject::Integer(s.len() as i64)))
}
OnionObject::Bytes(b) => {
Ok(OnionStaticObject::new(OnionObject::Integer(b.len() as i64)))
}
OnionObject::Range(start, end) => Ok(OnionStaticObject::new(OnionObject::Integer(
(end - start) as i64,
))),
_ => Err(RuntimeError::InvalidOperation(format!(
"len() not supported for {:?}",
self
))),
})
}
pub fn contains(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Tuple(tuple), _) => tuple.contains(other_obj),
(OnionObject::String(s), OnionObject::String(other_s)) => {
Ok(s.contains(other_s.as_ref()))
}
(OnionObject::Bytes(b), OnionObject::Bytes(other_b)) => Ok(b
.windows(other_b.len())
.any(|window| window == other_b.as_slice())),
(OnionObject::Range(l, r), OnionObject::Integer(i)) => Ok(*i >= *l && *i < *r),
(OnionObject::Range(start, end), OnionObject::Float(f)) => {
Ok(*f >= *start as f64 && *f < *end as f64)
}
(OnionObject::Range(start, end), OnionObject::Range(other_start, other_end)) => {
Ok(*other_start >= *start && *other_end <= *end)
}
_ => Err(RuntimeError::InvalidOperation(format!(
"contains() not supported for {:?}",
obj
))),
})
})
}
pub fn clone_value(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| Ok(obj.clone().stabilize()))
}
pub fn with_data<T, F>(&self, f: F) -> Result<T, RuntimeError>
where
F: FnOnce(&OnionObject) -> Result<T, RuntimeError>,
{
match self {
OnionObject::Mut(weak) => {
if let Some(strong) = weak.upgrade() {
strong.as_ref().with_data(f)
} else {
Err(RuntimeError::BrokenReference)
}
}
_ => f(self),
}
}
pub fn with_data_mut<T, F>(&mut self, f: F) -> Result<T, RuntimeError>
where
F: FnOnce(&mut OnionObject) -> Result<T, RuntimeError>,
{
match self {
OnionObject::Mut(weak) => {
if let Some(strong) = weak.upgrade() {
strong.as_ref().with_data_mut(f)
} else {
Err(RuntimeError::BrokenReference)
}
}
_ => f(self),
}
}
pub fn assign(&self, other: &OnionObject) -> Result<(), RuntimeError> {
let OnionObject::Mut(weak) = self else {
return Err(RuntimeError::InvalidOperation(format!(
"Cannot assign to non-mutable object: {:?}",
self
)));
};
match weak.upgrade() {
Some(strong) => {
let new_value = other.with_data(|other| Ok(other.clone()))?;
strong.as_ref().with_data_mut(|obj| {
*obj = new_value;
Ok(())
})
}
None => Err(RuntimeError::BrokenReference),
}
}
pub fn with_data_ref_mut<T, F>(&self, f: F) -> Result<T, RuntimeError>
where
F: FnOnce(&mut OnionObject) -> Result<T, RuntimeError>,
{
let OnionObject::Mut(weak) = self else {
return Err(RuntimeError::InvalidOperation(format!(
"Cannot mutate non-mutable object: {:?}",
self
)));
};
match weak.upgrade() {
Some(strong) => strong.as_ref().with_data_mut(f),
None => Err(RuntimeError::BrokenReference),
}
}
pub fn replace_mut(&mut self, other: &OnionObject) -> Result<(), RuntimeError> {
match (self, other) {
(OnionObject::Mut(to), OnionObject::Mut(from)) => {
*to = from.clone();
Ok(())
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid replace_mut() operation"
))),
}
}
pub fn to_integer(&self) -> Result<i64, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(i) => Ok(*i),
OnionObject::Float(f) => Ok(*f as i64),
OnionObject::String(s) => s
.parse::<i64>()
.map_err(|e| RuntimeError::InvalidType(e.to_string())),
OnionObject::Boolean(b) => Ok(if *b { 1 } else { 0 }),
_ => Err(RuntimeError::InvalidType(format!(
"Cannot convert {:?} to Integer",
obj
))),
})
}
pub fn to_float(&self) -> Result<f64, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(i) => Ok(*i as f64),
OnionObject::Float(f) => Ok(*f),
OnionObject::String(s) => s
.parse::<f64>()
.map_err(|e| RuntimeError::InvalidType(e.to_string())),
OnionObject::Boolean(b) => Ok(if *b { 1.0 } else { 0.0 }),
_ => Err(RuntimeError::InvalidType(format!(
"Cannot convert {:?} to Float",
obj
))),
})
}
pub fn to_string(&self, ptrs: &Vec<*const OnionObject>) -> Result<String, RuntimeError> {
self.with_data(|obj| {
for ptr in ptrs {
if addr_eq(obj, *ptr) {
return Ok("...".to_string());
}
}
let mut new_ptrs = ptrs.clone();
new_ptrs.push(obj);
match obj {
OnionObject::Integer(i) => Ok(i.to_string()),
OnionObject::Float(f) => Ok(f.to_string()),
OnionObject::String(s) => Ok(s.as_ref().clone()),
OnionObject::Bytes(b) => Ok(format!(
"$\"{}\"",
general_purpose::STANDARD.encode(b.as_ref())
)),
OnionObject::Boolean(b) => Ok(if *b {
"true".to_string()
} else {
"false".to_string()
}),
OnionObject::Null => Ok("null".to_string()),
OnionObject::Undefined(s) => Ok(match s {
Some(s) => format!("undefined({:?})", s),
None => "undefined".to_string(),
}),
OnionObject::Range(start, end) => Ok(format!("{}..{}", start, end)),
OnionObject::Tuple(tuple) => match tuple.elements.len() {
0 => Ok("()".to_string()),
1 => {
let first = tuple.elements.first().unwrap();
Ok(format!(
"({},)",
first
.try_borrow()
.map_err(|_| RuntimeError::BorrowError(
"Failed to borrow tuple element at `to_string`".to_string(),
))?
.repr(&new_ptrs)?
))
}
_ => {
let elements: Result<Vec<String>, RuntimeError> = tuple
.elements
.iter()
.map(|e| {
e.try_borrow()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow tuple element at `to_string`"
.to_string(),
)
})?
.repr(&new_ptrs)
})
.collect();
Ok(format!("({})", elements?.join(", ")))
}
},
OnionObject::Pair(pair) => {
let left = pair.key.borrow().repr(&new_ptrs)?;
let right = pair.value.borrow().repr(&new_ptrs)?;
Ok(format!("{} : {}", left, right))
}
OnionObject::Named(named) => {
let name = named.key.try_borrow()?.repr(&new_ptrs)?;
let value = named.value.try_borrow()?.repr(&new_ptrs)?;
Ok(format!("{} => {}", name, value))
}
OnionObject::LazySet(lazy_set) => {
let container = lazy_set.container.try_borrow()?.repr(&new_ptrs)?;
let filter = lazy_set.filter.try_borrow()?.repr(&new_ptrs)?;
Ok(format!("[{} | {}]", container, filter))
}
OnionObject::InstructionPackage(_) => Ok("InstructionPackage(...)".to_string()),
OnionObject::Lambda(lambda) => {
let params = lambda.parameter.repr(&new_ptrs)?;
let body = lambda.body.to_string();
Ok(format!("{}::{} -> {}", lambda.signature, params, body))
}
_ => {
Ok(format!("{:?}", obj))
}
}
})
}
pub fn repr(&self, ptrs: &Vec<*const OnionObject>) -> Result<String, RuntimeError> {
self.with_data(|obj| {
for ptr in ptrs {
if addr_eq(obj, *ptr) {
return Ok("...".to_string());
}
}
let mut new_ptrs = ptrs.clone();
new_ptrs.push(obj);
match obj {
OnionObject::Integer(i) => Ok(format!("{}", i)),
OnionObject::Float(f) => Ok(format!("{}", f)),
OnionObject::String(s) => Ok(format!("{:?}", s)),
OnionObject::Bytes(b) => Ok(format!(
"$\"{}\"",
general_purpose::STANDARD.encode(b.as_ref())
)),
OnionObject::Boolean(b) => Ok(format!("{}", b)),
OnionObject::Null => Ok("null".to_string()),
OnionObject::Undefined(s) => Ok(match s {
Some(s) => format!("undefined({:?})", s),
None => "undefined".to_string(),
}),
OnionObject::Range(start, end) => Ok(format!("{}..{}", start, end)),
OnionObject::Tuple(tuple) => match tuple.elements.len() {
0 => Ok("()".to_string()),
1 => {
let first = tuple.elements.first().unwrap();
Ok(format!(
"({},)",
first
.try_borrow()
.map_err(|_| RuntimeError::BorrowError(
"Failed to borrow tuple element at `repr`".to_string(),
))?
.repr(&new_ptrs)?
))
}
_ => {
let elements: Result<Vec<String>, RuntimeError> = tuple
.elements
.iter()
.map(|e| {
e.try_borrow()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow tuple element at `repr`".to_string(),
)
})?
.repr(&new_ptrs)
})
.collect();
Ok(format!("({})", elements?.join(", ")))
}
},
OnionObject::Pair(pair) => {
let left = pair.key.borrow().repr(&new_ptrs)?;
let right = pair.value.borrow().repr(&new_ptrs)?;
Ok(format!("{} : {}", left, right))
}
OnionObject::Named(named) => {
let name = named.key.try_borrow()?.repr(&new_ptrs)?;
let value = named.value.try_borrow()?.repr(&new_ptrs)?;
Ok(format!("{} => {}", name, value))
}
OnionObject::LazySet(lazy_set) => {
let container = lazy_set.container.try_borrow()?.repr(&new_ptrs)?;
let filter = lazy_set.filter.try_borrow()?.repr(&new_ptrs)?;
Ok(format!("[{} | {}]", container, filter))
}
OnionObject::InstructionPackage(_) => Ok("InstructionPackage(...)".to_string()),
OnionObject::Lambda(lambda) => {
let params = lambda.parameter.repr(&new_ptrs)?;
Ok(format!(
"{}::{} -> {}",
lambda.signature, params, lambda.body
))
}
OnionObject::Mut(weak) => {
if let Some(strong) = weak.upgrade() {
let inner_repr = strong
.as_ref()
.try_borrow()
.map_err(|_| {
RuntimeError::BorrowError(
"Failed to borrow Mut object at `repr`".to_string(),
)
})?
.repr(&new_ptrs)?;
Ok(format!("mut ({})", inner_repr))
} else {
Ok("Mut(BrokenReference)".to_string())
}
}
}
})
}
pub fn to_bytes(&self) -> Result<Vec<u8>, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(i) => Ok(i.to_string().into_bytes()),
OnionObject::Float(f) => Ok(f.to_string().into_bytes()),
OnionObject::String(s) => Ok(s.as_bytes().to_vec()),
OnionObject::Bytes(b) => Ok(b.as_ref().clone()),
OnionObject::Boolean(b) => Ok(if *b {
b"true".to_vec()
} else {
b"false".to_vec()
}),
_ => Err(RuntimeError::InvalidType(format!(
"Cannot convert {:?} to Bytes",
obj
))),
})
}
pub fn to_boolean(&self) -> Result<bool, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(i) => Ok(*i != 0),
OnionObject::Float(f) => Ok(*f != 0.0),
OnionObject::String(s) => Ok(!s.is_empty()),
OnionObject::Bytes(b) => Ok(!b.is_empty()),
OnionObject::Boolean(b) => Ok(*b),
OnionObject::Null => Ok(false),
OnionObject::Undefined(_) => Ok(false),
_ => Err(RuntimeError::InvalidType(format!(
"Cannot convert {:?} to Boolean",
obj
))),
})
}
pub fn mutablize(self, gc: &mut GC<OnionObjectCell>) -> OnionStaticObject {
let arc = gc.create(OnionObjectCell::from(self));
OnionStaticObject {
obj: OnionObject::Mut(arc.as_weak()),
_arcs: GCArcStorage::Single(arc),
}
}
}
impl OnionObject {
pub fn equals(&self, other: &Self) -> Result<bool, RuntimeError> {
self.with_data(|left| {
other.with_data(|right| {
match (left, right) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => Ok(i1 == i2),
(OnionObject::Float(f1), OnionObject::Float(f2)) => Ok(f1 == f2),
(OnionObject::Integer(i1), OnionObject::Float(f2)) => Ok(*i1 as f64 == *f2),
(OnionObject::Float(f1), OnionObject::Integer(i2)) => Ok(*f1 == *i2 as f64),
(OnionObject::String(s1), OnionObject::String(s2)) => Ok(s1 == s2),
(OnionObject::Bytes(b1), OnionObject::Bytes(b2)) => Ok(b1 == b2),
(OnionObject::Boolean(b1), OnionObject::Boolean(b2)) => Ok(b1 == b2),
(OnionObject::Range(start1, end1), OnionObject::Range(start2, end2)) => {
Ok(start1 == start2 && end1 == end2)
}
(OnionObject::Null, OnionObject::Null) => Ok(true),
(OnionObject::Undefined(_), OnionObject::Undefined(_)) => Ok(true),
(OnionObject::Tuple(t1), _) => t1.equals(other),
(OnionObject::Pair(p1), _) => p1.equals(other),
(OnionObject::Named(n1), _) => n1.equals(other),
_ => Ok(false),
}
})
})
}
pub fn is_same(&self, other: &Self) -> Result<bool, RuntimeError> {
match (self, other) {
(OnionObject::Mut(weak1), OnionObject::Mut(weak2)) => {
if let (Some(strong1), Some(strong2)) = (weak1.upgrade(), weak2.upgrade()) {
Ok(addr_eq(strong1.as_ref(), strong2.as_ref()))
} else {
Ok(false)
}
}
_ => self.equals(other),
}
}
pub fn binary_add(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 + i2)))
}
(OnionObject::Float(f1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 + f2)))
}
(OnionObject::Integer(i1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(*i1 as f64 + f2)))
}
(OnionObject::Float(f1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 + *i2 as f64)))
}
(OnionObject::String(s1), OnionObject::String(s2)) => Ok(OnionStaticObject::new(
OnionObject::String(Arc::new(format!("{}{}", s1, s2))),
)),
(OnionObject::Bytes(b1), OnionObject::Bytes(b2)) => {
let mut new_bytes = b1.as_ref().clone();
new_bytes.extend_from_slice(b2);
Ok(OnionStaticObject::new(OnionObject::Bytes(Arc::new(
new_bytes,
))))
}
(OnionObject::Range(start1, end1), OnionObject::Range(start2, end2)) => Ok(
OnionStaticObject::new(OnionObject::Range(start1 + start2, end1 + end2)),
),
(OnionObject::Tuple(t1), _) => t1.binary_add(other_obj),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary add operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_sub(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 - i2)))
}
(OnionObject::Float(f1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 - f2)))
}
(OnionObject::Integer(i1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(*i1 as f64 - f2)))
}
(OnionObject::Float(f1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 - *i2 as f64)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary sub operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_mul(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 * i2)))
}
(OnionObject::Float(f1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 * f2)))
}
(OnionObject::Integer(i1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(*i1 as f64 * f2)))
}
(OnionObject::Float(f1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 * *i2 as f64)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary mul operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_div(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
if *i2 == 0 {
return Err(RuntimeError::InvalidOperation(
"Division by zero".to_string(),
));
}
Ok(OnionStaticObject::new(OnionObject::Integer(i1 / i2)))
}
(OnionObject::Float(f1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 / f2)))
}
(OnionObject::Integer(i1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(*i1 as f64 / f2)))
}
(OnionObject::Float(f1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 / *i2 as f64)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary div operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_mod(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
if *i2 == 0 {
return Err(RuntimeError::InvalidOperation(
"Division by zero".to_string(),
));
}
Ok(OnionStaticObject::new(OnionObject::Integer(i1 % i2)))
}
(OnionObject::Float(f1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 % f2)))
}
(OnionObject::Integer(i1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(*i1 as f64 % f2)))
}
(OnionObject::Float(f1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1 % *i2 as f64)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary mod operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_pow(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => Ok(OnionStaticObject::new(
OnionObject::Integer(i1.pow(*i2 as u32)),
)),
(OnionObject::Float(f1), OnionObject::Float(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Float(f1.powf(*f2))))
}
(OnionObject::Integer(i1), OnionObject::Float(f2)) => Ok(OnionStaticObject::new(
OnionObject::Float((*i1 as f64).powf(*f2)),
)),
(OnionObject::Float(f1), OnionObject::Integer(i2)) => Ok(OnionStaticObject::new(
OnionObject::Float(f1.powi(*i2 as i32)),
)),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary pow operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_and(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 & i2)))
}
(OnionObject::Boolean(f1), OnionObject::Boolean(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Boolean(*f1 && *f2)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary and operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_or(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 | i2)))
}
(OnionObject::Boolean(f1), OnionObject::Boolean(f2)) => {
Ok(OnionStaticObject::new(OnionObject::Boolean(*f1 || *f2)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary or operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_xor(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 ^ i2)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary xor operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_shl(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 << i2)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary shl operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_shr(&self, other: &Self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => {
Ok(OnionStaticObject::new(OnionObject::Integer(i1 >> i2)))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary shr operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_eq(&self, other: &Self) -> Result<bool, RuntimeError> {
self.equals(other)
}
pub fn binary_lt(&self, other: &Self) -> Result<bool, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => Ok(i1 < i2),
(OnionObject::Float(f1), OnionObject::Float(f2)) => Ok(f1 < f2),
(OnionObject::Integer(i1), OnionObject::Float(f2)) => Ok((*i1 as f64) < *f2),
(OnionObject::Float(f1), OnionObject::Integer(i2)) => Ok(*f1 < *i2 as f64),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary lt operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn binary_gt(&self, other: &Self) -> Result<bool, RuntimeError> {
self.with_data(|obj| {
other.with_data(|other_obj| match (obj, other_obj) {
(OnionObject::Integer(i1), OnionObject::Integer(i2)) => Ok(i1 > i2),
(OnionObject::Float(f1), OnionObject::Float(f2)) => Ok(f1 > f2),
(OnionObject::Integer(i1), OnionObject::Float(f2)) => Ok((*i1 as f64) > *f2),
(OnionObject::Float(f1), OnionObject::Integer(i2)) => Ok(*f1 > *i2 as f64),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid binary gt operation for {:?} and {:?}",
obj, other_obj
))),
})
})
}
pub fn unary_neg(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(i) => Ok(OnionStaticObject::new(OnionObject::Integer(-i))),
OnionObject::Float(f) => Ok(OnionStaticObject::new(OnionObject::Float(-f))),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid unary neg operation for {:?}",
obj
))),
})
}
pub fn unary_plus(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(i) => Ok(OnionStaticObject::new(OnionObject::Integer(i.abs()))),
OnionObject::Float(f) => Ok(OnionStaticObject::new(OnionObject::Float(f.abs()))),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid unary plus operation for {:?}",
obj
))),
})
}
pub fn unary_not(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Boolean(b) => Ok(OnionStaticObject::new(OnionObject::Boolean(!b))),
OnionObject::Integer(i) => Ok(OnionStaticObject::new(OnionObject::Integer(!i))),
_ => Err(RuntimeError::InvalidOperation(format!(
"Invalid unary not operation for {:?}",
obj
))),
})
}
pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
where
F: Fn(&OnionObject) -> Result<R, RuntimeError>,
{
self.with_data(|obj| match obj {
OnionObject::Tuple(tuple) => tuple.with_attribute(key, f),
OnionObject::Named(named) => named.with_attribute(key, f),
OnionObject::Pair(pair) => pair.with_attribute(key, f),
OnionObject::Lambda(lambda) => lambda.with_attribute(key, f),
OnionObject::LazySet(lazy_set) => lazy_set.with_attribute(key, f),
_ => Err(RuntimeError::InvalidOperation(format!(
"with_attribute() not supported for {:?}",
self
))),
})
}
pub fn with_attribute_mut<F, R>(&mut self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
where
F: Fn(&mut OnionObject) -> Result<R, RuntimeError>,
{
self.with_data_mut(|obj| match obj {
OnionObject::Tuple(tuple) => tuple.with_attribute_mut(key, f),
OnionObject::Named(named) => named.with_attribute_mut(key, f),
OnionObject::Pair(pair) => pair.with_attribute_mut(key, f),
OnionObject::Lambda(lambda) => lambda.with_attribute_mut(key, f),
OnionObject::LazySet(lazy_set) => lazy_set.with_attribute_mut(key, f),
_ => Err(RuntimeError::InvalidOperation(format!(
"with_attribute_mut() not supported for {:?}",
obj
))),
})
}
pub fn at(&self, index: i64) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Tuple(tuple) => tuple.at(index),
OnionObject::String(s) => {
if index < 0 || index >= s.len() as i64 {
return Err(RuntimeError::InvalidOperation(format!(
"Index out of bounds for String: {}",
s
)));
}
Ok(OnionStaticObject::new(OnionObject::String(Arc::new(
s.chars().nth(index as usize).unwrap().to_string(),
))))
}
OnionObject::Bytes(b) => {
if index < 0 || index >= b.len() as i64 {
return Err(RuntimeError::InvalidOperation(format!(
"Index out of bounds for Bytes: {:?}",
b
)));
}
Ok(OnionStaticObject::new(OnionObject::Bytes(Arc::new(vec![
b[index as usize],
]))))
}
_ => Err(RuntimeError::InvalidOperation(format!(
"index_of() not supported for {:?}",
self
))),
})
}
pub fn key_of(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Named(named) => Ok(named.get_key().clone().stabilize()),
OnionObject::Pair(pair) => Ok(pair.get_key().clone().stabilize()),
_ => Err(RuntimeError::InvalidOperation(format!(
"key_of() not supported for {:?}",
obj
))),
})
}
pub fn value_of(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Named(named) => Ok(named.get_value().clone().stabilize()),
OnionObject::Pair(pair) => Ok(pair.get_value().clone().stabilize()),
OnionObject::Undefined(s) => Ok(OnionStaticObject::new(OnionObject::String(Arc::new(
s.clone().unwrap_or_else(|| "".to_string()),
)))),
_ => Err(RuntimeError::InvalidOperation(format!(
"value_of() not supported for {:?}",
obj
))),
})
}
pub fn type_of(&self) -> Result<String, RuntimeError> {
self.with_data(|obj| match obj {
OnionObject::Integer(_) => Ok("Integer".to_string()),
OnionObject::Float(_) => Ok("Float".to_string()),
OnionObject::String(_) => Ok("String".to_string()),
OnionObject::Bytes(_) => Ok("Bytes".to_string()),
OnionObject::Boolean(_) => Ok("Boolean".to_string()),
OnionObject::Null => Ok("Null".to_string()),
OnionObject::Undefined(_) => Ok("Undefined".to_string()),
OnionObject::Tuple(_) => Ok("Tuple".to_string()),
OnionObject::Pair(_) => Ok("Pair".to_string()),
OnionObject::Named(_) => Ok("Named".to_string()),
OnionObject::LazySet(_) => Ok("LazySet".to_string()),
OnionObject::InstructionPackage(_) => Ok("InstructionPackage".to_string()),
OnionObject::Lambda(_) => Ok("Lambda".to_string()),
_ => Err(RuntimeError::InvalidOperation(format!(
"type_of() not supported for {:?}",
obj
))),
})
}
#[inline(always)]
pub fn copy(&self) -> Result<OnionStaticObject, RuntimeError> {
self.with_data(|obj| Ok(obj.clone().stabilize()))
}
}
#[derive(Clone)]
pub enum GCArcStorage {
None,
Single(GCArc<OnionObjectCell>),
Multiple(Vec<GCArc<OnionObjectCell>>),
}
#[derive(Clone)]
pub struct OnionStaticObject {
_arcs: GCArcStorage,
obj: OnionObject,
}
impl Default for OnionStaticObject {
fn default() -> Self {
OnionStaticObject {
obj: OnionObject::Undefined(None),
_arcs: GCArcStorage::None,
}
}
}
impl Debug for OnionStaticObject {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "OnionStaticObject({:?})", self.obj)
}
}
impl Display for OnionStaticObject {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "OnionStaticObject({:?})", self.obj)
}
}
impl OnionStaticObject {
#[inline(always)]
pub fn new(obj: OnionObject) -> Self {
let arcs = match &obj {
OnionObject::Mut(obj) => match obj.upgrade() {
None => GCArcStorage::None,
Some(arc) => GCArcStorage::Single(arc),
},
OnionObject::Boolean(_)
| OnionObject::Integer(_)
| OnionObject::Float(_)
| OnionObject::String(_)
| OnionObject::Bytes(_)
| OnionObject::Null
| OnionObject::Undefined(_)
| OnionObject::Range(_, _) => GCArcStorage::None,
_ => {
let mut arcs = vec![];
obj.upgrade(&mut arcs);
GCArcStorage::Multiple(arcs)
}
};
OnionStaticObject {
obj: obj,
_arcs: arcs,
}
}
#[inline(always)]
pub fn weak(&self) -> &OnionObject {
&self.obj
}
#[inline(always)]
pub fn mutablize(
&self,
gc: &mut GC<OnionObjectCell>,
) -> Result<OnionStaticObject, RuntimeError> {
self.obj.with_data(|obj| Ok(obj.clone().mutablize(gc)))
}
}
#[macro_export]
macro_rules! unwrap_object {
($obj:expr, $variant:path) => {
match $obj {
$variant(o) => Ok(o),
_ => Err(RuntimeError::InvalidType(format!(
"Expected {}, found {:?}",
stringify!($variant),
$obj
))),
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
#[test]
fn test_detailed_memory_sizes() {
println!("详细内存分析:");
println!(
"OnionObjectCell: {} bytes",
std::mem::size_of::<OnionObjectCell>()
);
println!("OnionObject: {} bytes", std::mem::size_of::<OnionObject>());
println!(
"OnionStaticObject: {} bytes",
std::mem::size_of::<OnionStaticObject>()
);
println!(
"GCArcStorage: {} bytes",
std::mem::size_of::<GCArcStorage>()
);
println!(
"GCArc<OnionObjectCell>: {} bytes",
std::mem::size_of::<GCArc<OnionObjectCell>>()
);
println!("Arc<String>: {} bytes", std::mem::size_of::<Arc<String>>());
println!(
"Arc<Vec<u8>>: {} bytes",
std::mem::size_of::<Arc<Vec<u8>>>()
);
println!(
"GCArcWeak<OnionObjectCell>: {} bytes",
std::mem::size_of::<GCArcWeak<OnionObjectCell>>()
);
println!("OnionTuple: {} bytes", std::mem::size_of::<OnionTuple>());
println!("OnionNamed: {} bytes", std::mem::size_of::<OnionNamed>());
println!("OnionPair: {} bytes", std::mem::size_of::<OnionPair>());
println!(
"OnionLazySet: {} bytes",
std::mem::size_of::<OnionLazySet>()
);
}
#[test]
fn benchmark_realistic_vm_operations() {
println!("真实VM操作性能测试 (使用OnionStaticObject + clone):");
let start = Instant::now();
let mut result_sum = 0i64;
for i in 0..5_000_000 {
let obj1 = OnionObject::Integer(i).stabilize();
let obj2 = OnionObject::Integer(i + 1).stabilize();
let result = obj1.weak().with_data(|data1| {
obj2.weak().with_data(|data2| {
match (data1, data2) {
(OnionObject::Integer(a), OnionObject::Integer(b)) => {
Ok(OnionObject::Integer(a + b).stabilize())
}
_ => Err(RuntimeError::InvalidOperation("Type error".to_string())),
}
})
});
if let Ok(sum) = result {
if let Ok(val) = sum.weak().with_data(|data| match data {
OnionObject::Integer(v) => Ok(*v),
_ => Err(RuntimeError::InvalidType("Not integer".to_string())),
}) {
result_sum += val;
}
}
}
let duration = start.elapsed();
println!("500万次VM风格整数运算: {:.2}s", duration.as_secs_f64());
println!("每秒操作数: {:.0}", 5_000_000.0 / duration.as_secs_f64());
println!("结果校验: {}", result_sum);
}
#[test]
fn benchmark_vm_style_arithmetic() {
println!("VM风格算术运算性能测试:");
let start = Instant::now();
let mut final_result = 0i64;
for i in 0..2_000_000 {
let left = OnionObject::Integer(i).stabilize();
let right = OnionObject::Integer(i + 1).stabilize();
if let Ok(result) = left
.weak()
.with_data(|l_data| right.weak().with_data(|r_data| l_data.binary_add(r_data)))
{
let multiplier = OnionObject::Integer(2).stabilize();
if let Ok(mul_result) = result.weak().with_data(|add_data| {
multiplier
.weak()
.with_data(|mul_data| add_data.binary_mul(mul_data))
}) {
if let Ok(val) = mul_result.weak().with_data(|data| data.to_integer()) {
final_result += val;
}
}
}
}
let duration = start.elapsed();
println!("200万次复合运算: {:.2}s", duration.as_secs_f64());
println!("每秒操作数: {:.0}", 2_000_000.0 / duration.as_secs_f64());
println!("最终结果: {}", final_result);
}
#[test]
fn benchmark_object_creation_overhead() {
println!("对象创建开销测试:");
let start = Instant::now();
let mut objects = Vec::with_capacity(1_000_000);
for i in 0..1_000_000 {
let obj = OnionObject::Integer(i).stabilize();
objects.push(obj);
}
let creation_time = start.elapsed();
println!(
"100万个OnionStaticObject创建: {:.2}s",
creation_time.as_secs_f64()
);
let start = Instant::now();
let mut sum = 0i64;
for obj in &objects {
if let Ok(val) = obj.weak().with_data(|data| data.to_integer()) {
sum += val;
}
}
let access_time = start.elapsed();
println!("100万次对象访问: {:.2}s", access_time.as_secs_f64());
println!("访问校验和: {}", sum);
let start = Instant::now();
let mut cloned_objects = Vec::with_capacity(objects.len());
for obj in &objects[..100_000] {
cloned_objects.push(obj.clone());
}
let clone_time = start.elapsed();
println!("10万个对象克隆: {:.2}s", clone_time.as_secs_f64());
}
#[test]
fn benchmark_string_operations_realistic() {
println!("真实字符串操作性能测试:");
let start = Instant::now();
let mut total_length = 0usize;
for i in 0..500_000 {
let str_obj = OnionObject::String(Arc::new(format!("string_{}", i))).stabilize();
if let Ok(len_obj) = str_obj.weak().with_data(|data| data.len()) {
if let Ok(length) = len_obj.weak().with_data(|data| data.to_integer()) {
total_length += length as usize;
}
}
let suffix = OnionObject::String(Arc::new("_suffix".to_string())).stabilize();
if let Ok(concat_result) = str_obj.weak().with_data(|str_data| {
suffix
.weak()
.with_data(|suffix_data| str_data.binary_add(suffix_data))
}) {
if let Ok(concat_str) = concat_result
.weak()
.with_data(|data| data.to_string(&mut vec![]))
{
total_length += concat_str.len();
}
}
}
let duration = start.elapsed();
println!("50万次字符串操作: {:.2}s", duration.as_secs_f64());
println!("每秒操作数: {:.0}", 500_000.0 / duration.as_secs_f64());
println!("总字符串长度: {}", total_length);
}
#[test]
fn benchmark_refcell_overhead() {
println!("RefCell开销分析:");
let direct_integers: Vec<i64> = (0..1_000_000).collect();
let wrapped_integers: Vec<OnionStaticObject> = (0..1_000_000)
.map(|i| OnionObject::Integer(i).stabilize())
.collect();
let start = Instant::now();
let mut sum1 = 0i64;
for &val in &direct_integers {
sum1 += val * 2;
}
let direct_time = start.elapsed();
let start = Instant::now();
let mut sum2 = 0i64;
for obj in &wrapped_integers {
if let Ok(val) = obj.weak().with_data(|data| match data {
OnionObject::Integer(i) => Ok(*i),
_ => Err(RuntimeError::InvalidType("Not integer".to_string())),
}) {
sum2 += val * 2;
}
}
let refcell_time = start.elapsed();
println!("直接访问100万个i64: {:.2}s", direct_time.as_secs_f64());
println!(
"RefCell访问100万个OnionObject: {:.2}s",
refcell_time.as_secs_f64()
);
println!(
"RefCell开销倍数: {:.1}x",
refcell_time.as_secs_f64() / direct_time.as_secs_f64()
);
println!("校验: {} vs {}", sum1, sum2);
}
}