Phase 2 Complete: NyashValue extended with WeakBox support

Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-08-12 20:15:44 +00:00
parent f6d3669c68
commit 0f0edeeb9c

View File

@ -7,7 +7,7 @@
* Inspired by Lua's TValue system for performance-critical language implementations. * Inspired by Lua's TValue system for performance-critical language implementations.
*/ */
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex, Weak};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Display, Debug}; use std::fmt::{self, Display, Debug};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
@ -28,6 +28,9 @@ pub enum NyashValue {
// Legacy Box compatibility and custom types // Legacy Box compatibility and custom types
Box(Arc<Mutex<dyn NyashBox>>), Box(Arc<Mutex<dyn NyashBox>>),
// 🔗 Weak reference system - prevents circular reference memory leaks
WeakBox(Weak<Mutex<dyn NyashBox>>),
// Special values // Special values
Null, Null,
Void, Void,
@ -101,6 +104,17 @@ impl NyashValue {
"Box (locked)".to_string() "Box (locked)".to_string()
} }
}, },
NyashValue::WeakBox(weak_ref) => {
if let Some(arc) = weak_ref.upgrade() {
if let Ok(guard) = arc.try_lock() {
format!("WeakRef({})", guard.to_string_box().value)
} else {
"WeakRef(locked)".to_string()
}
} else {
"WeakRef(null)".to_string()
}
},
NyashValue::Null => "null".to_string(), NyashValue::Null => "null".to_string(),
NyashValue::Void => "void".to_string(), NyashValue::Void => "void".to_string(),
} }
@ -143,6 +157,10 @@ impl NyashValue {
NyashValue::String(s) => Ok(!s.is_empty()), NyashValue::String(s) => Ok(!s.is_empty()),
NyashValue::Null => Ok(false), NyashValue::Null => Ok(false),
NyashValue::Void => Ok(false), NyashValue::Void => Ok(false),
NyashValue::WeakBox(weak_ref) => {
// WeakBox is truthy if it can be upgraded (still alive)
Ok(weak_ref.upgrade().is_some())
},
_ => Ok(true), // Arrays, Maps, Boxes are truthy _ => Ok(true), // Arrays, Maps, Boxes are truthy
} }
} }
@ -157,6 +175,7 @@ impl NyashValue {
NyashValue::Array(_) => "Array", NyashValue::Array(_) => "Array",
NyashValue::Map(_) => "Map", NyashValue::Map(_) => "Map",
NyashValue::Box(_) => "Box", NyashValue::Box(_) => "Box",
NyashValue::WeakBox(_) => "WeakBox",
NyashValue::Null => "Null", NyashValue::Null => "Null",
NyashValue::Void => "Void", NyashValue::Void => "Void",
} }
@ -172,6 +191,43 @@ impl NyashValue {
matches!(self, NyashValue::Null | NyashValue::Void) || matches!(self, NyashValue::Null | NyashValue::Void) ||
self.to_bool().unwrap_or(false) == false self.to_bool().unwrap_or(false) == false
} }
/// 🔗 Weak Reference System - Core functionality
/// Upgrade a weak reference to a strong reference
/// Returns None if the referenced object has been dropped
pub fn upgrade_weak(&self) -> Option<NyashValue> {
match self {
NyashValue::WeakBox(weak_ref) => {
weak_ref.upgrade().map(|arc| NyashValue::Box(arc))
},
_ => Some(self.clone()), // Non-weak values return themselves
}
}
/// Downgrade a strong reference to a weak reference
/// Only works on Box values, others return None
pub fn downgrade_to_weak(&self) -> Option<NyashValue> {
match self {
NyashValue::Box(arc) => {
Some(NyashValue::WeakBox(Arc::downgrade(arc)))
},
_ => None, // Can only create weak refs from strong Box refs
}
}
/// Check if this is a weak reference
pub fn is_weak_reference(&self) -> bool {
matches!(self, NyashValue::WeakBox(_))
}
/// Check if a weak reference is still valid (not dropped)
pub fn is_weak_alive(&self) -> bool {
match self {
NyashValue::WeakBox(weak_ref) => weak_ref.strong_count() > 0,
_ => true, // Non-weak values are always "alive"
}
}
} }
impl PartialEq for NyashValue { impl PartialEq for NyashValue {
@ -194,6 +250,32 @@ impl PartialEq for NyashValue {
(NyashValue::Map(a), NyashValue::Map(b)) => Arc::ptr_eq(a, b), (NyashValue::Map(a), NyashValue::Map(b)) => Arc::ptr_eq(a, b),
(NyashValue::Box(a), NyashValue::Box(b)) => Arc::ptr_eq(a, b), (NyashValue::Box(a), NyashValue::Box(b)) => Arc::ptr_eq(a, b),
// Weak reference equality
(NyashValue::WeakBox(a), NyashValue::WeakBox(b)) => {
// Compare if they point to the same object (if both still alive)
match (a.upgrade(), b.upgrade()) {
(Some(arc_a), Some(arc_b)) => Arc::ptr_eq(&arc_a, &arc_b),
(None, None) => true, // Both dropped, consider equal
_ => false, // One dropped, one alive
}
},
// WeakBox vs Box comparison (upgrade weak, then compare)
(NyashValue::WeakBox(weak), NyashValue::Box(strong)) => {
if let Some(upgraded) = weak.upgrade() {
Arc::ptr_eq(&upgraded, strong)
} else {
false // Dropped weak ref != any strong ref
}
},
(NyashValue::Box(strong), NyashValue::WeakBox(weak)) => {
if let Some(upgraded) = weak.upgrade() {
Arc::ptr_eq(strong, &upgraded)
} else {
false // Dropped weak ref != any strong ref
}
},
// Everything else is not equal // Everything else is not equal
_ => false, _ => false,
} }
@ -216,6 +298,13 @@ impl Debug for NyashValue {
NyashValue::Array(_) => write!(f, "Array([...])"), NyashValue::Array(_) => write!(f, "Array([...])"),
NyashValue::Map(_) => write!(f, "Map({{...}})"), NyashValue::Map(_) => write!(f, "Map({{...}})"),
NyashValue::Box(_) => write!(f, "Box(...)"), NyashValue::Box(_) => write!(f, "Box(...)"),
NyashValue::WeakBox(weak_ref) => {
if weak_ref.upgrade().is_some() {
write!(f, "WeakBox(alive)")
} else {
write!(f, "WeakBox(dropped)")
}
},
NyashValue::Null => write!(f, "Null"), NyashValue::Null => write!(f, "Null"),
NyashValue::Void => write!(f, "Void"), NyashValue::Void => write!(f, "Void"),
} }
@ -293,6 +382,13 @@ impl NyashValue {
NyashValue::Box(b) => { NyashValue::Box(b) => {
Ok(b.clone()) Ok(b.clone())
}, },
NyashValue::WeakBox(weak_ref) => {
// Try to upgrade weak reference
match weak_ref.upgrade() {
Some(arc) => Ok(arc),
None => Err("Cannot convert dropped weak reference to Box".to_string()),
}
},
_ => Err(format!("Cannot convert {} to legacy Box", self.type_name())), _ => Err(format!("Cannot convert {} to legacy Box", self.type_name())),
} }
} }
@ -407,4 +503,73 @@ mod tests {
assert_eq!(NyashValue::new_null().type_name(), "Null"); assert_eq!(NyashValue::new_null().type_name(), "Null");
assert_eq!(NyashValue::new_void().type_name(), "Void"); assert_eq!(NyashValue::new_void().type_name(), "Void");
} }
#[test]
fn test_weak_reference_basic() {
use crate::box_trait::StringBox;
// Create a strong reference
let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string()))));
// Create weak reference
let weak_ref = strong_ref.downgrade_to_weak().unwrap();
assert!(weak_ref.is_weak_reference());
assert_eq!(weak_ref.type_name(), "WeakBox");
// Upgrade should work
let upgraded = weak_ref.upgrade_weak().unwrap();
assert_eq!(upgraded, strong_ref);
// Both should be alive initially
assert!(weak_ref.is_weak_alive());
}
#[test]
fn test_weak_reference_drop() {
use crate::box_trait::StringBox;
let weak_ref = {
let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string()))));
strong_ref.downgrade_to_weak().unwrap()
}; // strong_ref goes out of scope and is dropped
// Weak reference should now be invalid
assert!(!weak_ref.is_weak_alive());
assert!(weak_ref.upgrade_weak().is_none());
// to_bool should return false for dropped weak ref
assert_eq!(weak_ref.to_bool().unwrap(), false);
}
#[test]
fn test_weak_reference_equality() {
use crate::box_trait::StringBox;
let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string()))));
let weak_ref1 = strong_ref.downgrade_to_weak().unwrap();
let weak_ref2 = strong_ref.downgrade_to_weak().unwrap();
// Weak refs to same object should be equal
assert_eq!(weak_ref1, weak_ref2);
// Weak ref should equal its strong ref
assert_eq!(weak_ref1, strong_ref);
assert_eq!(strong_ref, weak_ref1);
}
#[test]
fn test_weak_reference_string_representation() {
use crate::box_trait::StringBox;
let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("hello".to_string()))));
let weak_ref = strong_ref.downgrade_to_weak().unwrap();
// Should show weak reference to the content
assert!(weak_ref.to_string().contains("WeakRef"));
assert!(weak_ref.to_string().contains("hello"));
// After dropping strong ref
drop(strong_ref);
assert_eq!(weak_ref.to_string(), "WeakRef(null)");
}
} }