stryke/
serialize_normalize.rs1use indexmap::IndexMap;
15use parking_lot::RwLock;
16use std::cell::RefCell;
17use std::collections::HashMap;
18use std::sync::Arc;
19
20use crate::ast::ClassDef;
21use crate::value::StrykeValue;
22
23thread_local! {
24 pub(crate) static CLASS_DEFS_REGISTRY: RefCell<HashMap<String, Arc<ClassDef>>> =
29 RefCell::new(HashMap::new());
30}
31
32pub(crate) fn install_class_defs(
36 defs: HashMap<String, Arc<ClassDef>>,
37) -> HashMap<String, Arc<ClassDef>> {
38 CLASS_DEFS_REGISTRY.with(|cell| std::mem::replace(&mut *cell.borrow_mut(), defs))
39}
40
41pub(crate) fn register_class_def(def: Arc<ClassDef>) {
44 CLASS_DEFS_REGISTRY.with(|cell| {
45 cell.borrow_mut().insert(def.name.clone(), def);
46 });
47}
48
49fn class_field_names(def: &ClassDef) -> Vec<String> {
55 let mut visited: std::collections::HashSet<String> = std::collections::HashSet::new();
56 class_field_names_dedup(def, &mut visited)
57}
58
59fn class_field_names_dedup(
60 def: &ClassDef,
61 visited_classes: &mut std::collections::HashSet<String>,
62) -> Vec<String> {
63 let mut names = Vec::new();
64 for parent_name in &def.extends {
70 if !visited_classes.insert(parent_name.clone()) {
71 continue;
72 }
73 let parent_def_opt =
74 CLASS_DEFS_REGISTRY.with(|cell| cell.borrow().get(parent_name).cloned());
75 if let Some(parent_def) = parent_def_opt {
76 names.extend(class_field_names_dedup(&parent_def, visited_classes));
77 }
78 }
79 for f in &def.fields {
80 names.push(f.name.clone());
81 }
82 names
83}
84
85pub fn deep_normalize(v: &StrykeValue) -> StrykeValue {
95 let mut visited: std::collections::HashSet<usize> = std::collections::HashSet::new();
96 deep_normalize_inner(v, &mut visited)
97}
98
99fn deep_normalize_inner(
100 v: &StrykeValue,
101 visited: &mut std::collections::HashSet<usize>,
102) -> StrykeValue {
103 if let Some(c) = v.as_class_inst() {
104 let names = class_field_names(&c.def);
105 let values = c.get_values();
106 let mut map = IndexMap::new();
107 let n = names.len().min(values.len());
111 for i in 0..n {
112 map.insert(names[i].clone(), deep_normalize_inner(&values[i], visited));
113 }
114 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
115 }
116 if let Some(s) = v.as_struct_inst() {
117 let values = s.get_values();
118 let mut map = IndexMap::new();
119 for (i, field) in s.def.fields.iter().enumerate() {
120 if let Some(elem) = values.get(i) {
121 map.insert(field.name.clone(), deep_normalize_inner(elem, visited));
122 }
123 }
124 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
125 }
126 if let Some(e) = v.as_enum_inst() {
127 let mut map = IndexMap::new();
131 map.insert(
132 "variant".to_string(),
133 StrykeValue::string(e.variant_name().to_string()),
134 );
135 if !e.data.is_undef() {
136 map.insert("value".to_string(), deep_normalize_inner(&e.data, visited));
137 }
138 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
139 }
140 if let Some(r) = v.as_hash_ref() {
141 let addr = Arc::as_ptr(&r) as usize;
145 if !visited.insert(addr) {
146 return StrykeValue::UNDEF;
147 }
148 let inner = r.read().clone();
149 let mut map = IndexMap::new();
150 for (k, val) in inner.into_iter() {
151 map.insert(k, deep_normalize_inner(&val, visited));
152 }
153 visited.remove(&addr);
154 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
155 }
156 if let Some(r) = v.as_array_ref() {
157 let addr = Arc::as_ptr(&r) as usize;
158 if !visited.insert(addr) {
159 return StrykeValue::UNDEF;
160 }
161 let inner = r.read().clone();
162 let out: Vec<StrykeValue> = inner
163 .iter()
164 .map(|elem| deep_normalize_inner(elem, visited))
165 .collect();
166 visited.remove(&addr);
167 return StrykeValue::array_ref(Arc::new(RwLock::new(out)));
168 }
169 v.clone()
170}
171
172pub fn normalize_args_head(args: &[StrykeValue]) -> Vec<StrykeValue> {
177 if args.is_empty() {
178 return Vec::new();
179 }
180 let mut out = Vec::with_capacity(args.len());
181 out.push(deep_normalize(&args[0]));
182 out.extend(args[1..].iter().cloned());
183 out
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use indexmap::IndexMap;
190
191 fn aref(v: Vec<StrykeValue>) -> StrykeValue {
192 StrykeValue::array_ref(Arc::new(RwLock::new(v)))
193 }
194
195 fn href(pairs: &[(&str, StrykeValue)]) -> StrykeValue {
196 let mut m = IndexMap::new();
197 for (k, v) in pairs {
198 m.insert((*k).to_string(), v.clone());
199 }
200 StrykeValue::hash_ref(Arc::new(RwLock::new(m)))
201 }
202
203 #[test]
204 fn deep_normalize_scalars_roundtrip_unchanged() {
205 for v in [
206 StrykeValue::integer(42),
207 StrykeValue::float(3.5),
208 StrykeValue::string("hi".into()),
209 StrykeValue::UNDEF,
210 ] {
211 let out = deep_normalize(&v);
212 assert_eq!(out.to_string(), v.to_string());
213 }
214 }
215
216 #[test]
217 fn deep_normalize_walks_nested_arrayref_hashref() {
218 let inner = href(&[("x", StrykeValue::integer(1))]);
219 let outer = aref(vec![inner, StrykeValue::integer(2)]);
220 let out = deep_normalize(&outer);
221 let arr = out.as_array_ref().expect("array_ref outer survives");
222 let arr = arr.read();
223 assert_eq!(arr.len(), 2);
224 let h = arr[0].as_hash_ref().expect("nested hash_ref survives");
225 let h = h.read();
226 assert_eq!(h.get("x").unwrap().to_int(), 1);
227 assert_eq!(arr[1].to_int(), 2);
228 }
229
230 #[test]
231 fn deep_normalize_does_not_share_storage_with_input() {
232 let arr = Arc::new(RwLock::new(vec![StrykeValue::integer(1)]));
233 let v = StrykeValue::array_ref(arr.clone());
234 let out = deep_normalize(&v);
235 let out_arr = out.as_array_ref().expect("array_ref");
236 out_arr.write().push(StrykeValue::integer(2));
237 assert_eq!(
238 arr.read().len(),
239 1,
240 "deep_normalize must clone, not alias, ref storage"
241 );
242 }
243
244 #[test]
245 fn normalize_args_head_normalizes_first_only() {
246 let h = href(&[("k", StrykeValue::integer(7))]);
247 let tail = StrykeValue::string("opt".into());
248 let out = normalize_args_head(&[h, tail.clone()]);
249 assert_eq!(out.len(), 2);
250 assert!(out[0].as_hash_ref().is_some());
251 assert_eq!(out[1].to_string(), tail.to_string());
252 }
253
254 #[test]
255 fn normalize_args_head_empty_returns_empty() {
256 assert!(normalize_args_head(&[]).is_empty());
257 }
258
259 #[test]
260 fn register_class_def_appears_in_field_names() {
261 use crate::ast::ClassDef;
262 let prev = install_class_defs(HashMap::new());
263 let def = Arc::new(ClassDef {
264 name: "T".into(),
265 is_abstract: false,
266 is_final: false,
267 extends: vec![],
268 implements: vec![],
269 fields: vec![],
270 methods: vec![],
271 static_fields: vec![],
272 });
273 register_class_def(def);
274 let names = CLASS_DEFS_REGISTRY.with(|c| c.borrow().keys().cloned().collect::<Vec<_>>());
275 assert!(names.contains(&"T".to_string()));
276 install_class_defs(prev);
278 }
279}