sim_lib_lang_javascript/
managed.rs1use sim_lib_mutation::{
2 ArenaError, EdgeId, EphemeronMutationError, ManagedHandle, ManagedNode,
3 StrongEdgeMutationError, WeakEdgeMutationError,
4};
5
6#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
8pub enum JavascriptManagedKind {
9 #[default]
11 Object,
12 Environment,
14 Frame,
16 Function,
18}
19
20pub type JavascriptManagedObject = ManagedNode<JavascriptManagedKind>;
22
23pub type JavascriptHeap = sim_lib_gc_tracing::ManagedHeap<JavascriptManagedObject>;
25
26pub type JavascriptHeapPolicy = sim_lib_gc_tracing::ManagedHeapPolicy;
28
29pub trait JavascriptHeapExt {
31 fn connect(
33 &mut self,
34 from: ManagedHandle,
35 to: ManagedHandle,
36 ) -> Result<EdgeId, JavascriptManagedMutationError>;
37
38 fn connect_weak(
40 &mut self,
41 from: ManagedHandle,
42 to: ManagedHandle,
43 ) -> Result<EdgeId, JavascriptManagedMutationError>;
44
45 fn connect_ephemeron(
47 &mut self,
48 from: ManagedHandle,
49 key: ManagedHandle,
50 value: ManagedHandle,
51 ) -> Result<EdgeId, JavascriptManagedMutationError>;
52}
53
54#[derive(Clone, Debug, Eq, PartialEq)]
56pub enum JavascriptManagedMutationError {
57 Arena(ArenaError),
59 Strong(StrongEdgeMutationError),
61 Weak(WeakEdgeMutationError),
63 Ephemeron(EphemeronMutationError),
65}
66
67impl From<ArenaError> for JavascriptManagedMutationError {
68 fn from(value: ArenaError) -> Self {
69 Self::Arena(value)
70 }
71}
72
73impl JavascriptHeapExt for JavascriptHeap {
74 fn connect(
75 &mut self,
76 from: ManagedHandle,
77 to: ManagedHandle,
78 ) -> Result<EdgeId, JavascriptManagedMutationError> {
79 self.get_mut(from)?
80 .insert_strong(to.id())
81 .map_err(JavascriptManagedMutationError::Strong)
82 }
83
84 fn connect_weak(
85 &mut self,
86 from: ManagedHandle,
87 to: ManagedHandle,
88 ) -> Result<EdgeId, JavascriptManagedMutationError> {
89 self.get_mut(from)?
90 .insert_weak(to.id())
91 .map_err(JavascriptManagedMutationError::Weak)
92 }
93
94 fn connect_ephemeron(
95 &mut self,
96 from: ManagedHandle,
97 key: ManagedHandle,
98 value: ManagedHandle,
99 ) -> Result<EdgeId, JavascriptManagedMutationError> {
100 self.get_mut(from)?
101 .insert_ephemeron(key.id(), value.id())
102 .map_err(JavascriptManagedMutationError::Ephemeron)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use sim_lib_gc_tracing::CollectionLimits;
110
111 fn limits() -> CollectionLimits {
112 CollectionLimits {
113 objects: 8,
114 edges: 16,
115 stack: 8,
116 work: 64,
117 clears: 8,
118 finalizers: 0,
119 }
120 }
121
122 #[test]
123 fn shared_node_preserves_strong_cycle_behavior() {
124 let mut heap = JavascriptHeap::tracing(8, limits()).unwrap();
125 let first = heap
126 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
127 .unwrap();
128 let second = heap
129 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
130 .unwrap();
131 heap.connect(first, second).unwrap();
132 heap.connect(second, first).unwrap();
133 assert_eq!(
134 heap.collect().unwrap().unwrap().swept,
135 [first.id(), second.id()]
136 );
137 }
138
139 #[test]
140 fn shared_heap_preserves_explicit_retention_gap() {
141 let mut heap = JavascriptHeap::retaining(2).unwrap();
142 heap.allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
143 .unwrap();
144 assert!(heap.cycle_leak_gap().unwrap().contains("cycles"));
145 assert_eq!(heap.collect().unwrap(), None);
146 assert_eq!(heap.live_len(), 1);
147 }
148
149 #[test]
150 fn javascript_weak_and_ephemeron_edges_clear_on_shared_collector() {
151 let mut heap = JavascriptHeap::tracing(8, limits()).unwrap();
152 let owner = heap
153 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
154 .unwrap();
155 let weak_target = heap
156 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
157 .unwrap();
158 let key = heap
159 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
160 .unwrap();
161 let value = heap
162 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))
163 .unwrap();
164 let weak = heap.connect_weak(owner, weak_target).unwrap();
165 let ephemeron = heap.connect_ephemeron(owner, key, value).unwrap();
166 let root = heap.root(owner).unwrap();
167
168 let receipt = heap.collect().unwrap().unwrap();
169 assert_eq!(receipt.swept, [weak_target.id(), key.id(), value.id()]);
170 assert_eq!(receipt.cleared_weak, [(owner.id(), weak)]);
171 assert_eq!(receipt.cleared_ephemerons, [(owner.id(), ephemeron)]);
172 assert!(heap.get(owner).unwrap().edge_snapshot().is_empty());
173 heap.release_root(root).unwrap();
174 }
175}