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 names = Vec::new();
56 for parent_name in &def.extends {
57 let parent_def_opt =
58 CLASS_DEFS_REGISTRY.with(|cell| cell.borrow().get(parent_name).cloned());
59 if let Some(parent_def) = parent_def_opt {
60 names.extend(class_field_names(&parent_def));
61 }
62 }
63 for f in &def.fields {
64 names.push(f.name.clone());
65 }
66 names
67}
68
69pub fn deep_normalize(v: &StrykeValue) -> StrykeValue {
79 let mut visited: std::collections::HashSet<usize> = std::collections::HashSet::new();
80 deep_normalize_inner(v, &mut visited)
81}
82
83fn deep_normalize_inner(
84 v: &StrykeValue,
85 visited: &mut std::collections::HashSet<usize>,
86) -> StrykeValue {
87 if let Some(c) = v.as_class_inst() {
88 let names = class_field_names(&c.def);
89 let values = c.get_values();
90 let mut map = IndexMap::new();
91 let n = names.len().min(values.len());
95 for i in 0..n {
96 map.insert(names[i].clone(), deep_normalize_inner(&values[i], visited));
97 }
98 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
99 }
100 if let Some(s) = v.as_struct_inst() {
101 let values = s.get_values();
102 let mut map = IndexMap::new();
103 for (i, field) in s.def.fields.iter().enumerate() {
104 if let Some(elem) = values.get(i) {
105 map.insert(field.name.clone(), deep_normalize_inner(elem, visited));
106 }
107 }
108 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
109 }
110 if let Some(e) = v.as_enum_inst() {
111 let mut map = IndexMap::new();
115 map.insert(
116 "variant".to_string(),
117 StrykeValue::string(e.variant_name().to_string()),
118 );
119 if !e.data.is_undef() {
120 map.insert("value".to_string(), deep_normalize_inner(&e.data, visited));
121 }
122 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
123 }
124 if let Some(r) = v.as_hash_ref() {
125 let addr = Arc::as_ptr(&r) as usize;
129 if !visited.insert(addr) {
130 return StrykeValue::UNDEF;
131 }
132 let inner = r.read().clone();
133 let mut map = IndexMap::new();
134 for (k, val) in inner.into_iter() {
135 map.insert(k, deep_normalize_inner(&val, visited));
136 }
137 visited.remove(&addr);
138 return StrykeValue::hash_ref(Arc::new(RwLock::new(map)));
139 }
140 if let Some(r) = v.as_array_ref() {
141 let addr = Arc::as_ptr(&r) as usize;
142 if !visited.insert(addr) {
143 return StrykeValue::UNDEF;
144 }
145 let inner = r.read().clone();
146 let out: Vec<StrykeValue> = inner
147 .iter()
148 .map(|elem| deep_normalize_inner(elem, visited))
149 .collect();
150 visited.remove(&addr);
151 return StrykeValue::array_ref(Arc::new(RwLock::new(out)));
152 }
153 v.clone()
154}
155
156pub fn normalize_args_head(args: &[StrykeValue]) -> Vec<StrykeValue> {
161 if args.is_empty() {
162 return Vec::new();
163 }
164 let mut out = Vec::with_capacity(args.len());
165 out.push(deep_normalize(&args[0]));
166 out.extend(args[1..].iter().cloned());
167 out
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use indexmap::IndexMap;
174
175 fn aref(v: Vec<StrykeValue>) -> StrykeValue {
176 StrykeValue::array_ref(Arc::new(RwLock::new(v)))
177 }
178
179 fn href(pairs: &[(&str, StrykeValue)]) -> StrykeValue {
180 let mut m = IndexMap::new();
181 for (k, v) in pairs {
182 m.insert((*k).to_string(), v.clone());
183 }
184 StrykeValue::hash_ref(Arc::new(RwLock::new(m)))
185 }
186
187 #[test]
188 fn deep_normalize_scalars_roundtrip_unchanged() {
189 for v in [
190 StrykeValue::integer(42),
191 StrykeValue::float(3.5),
192 StrykeValue::string("hi".into()),
193 StrykeValue::UNDEF,
194 ] {
195 let out = deep_normalize(&v);
196 assert_eq!(out.to_string(), v.to_string());
197 }
198 }
199
200 #[test]
201 fn deep_normalize_walks_nested_arrayref_hashref() {
202 let inner = href(&[("x", StrykeValue::integer(1))]);
203 let outer = aref(vec![inner, StrykeValue::integer(2)]);
204 let out = deep_normalize(&outer);
205 let arr = out.as_array_ref().expect("array_ref outer survives");
206 let arr = arr.read();
207 assert_eq!(arr.len(), 2);
208 let h = arr[0].as_hash_ref().expect("nested hash_ref survives");
209 let h = h.read();
210 assert_eq!(h.get("x").unwrap().to_int(), 1);
211 assert_eq!(arr[1].to_int(), 2);
212 }
213
214 #[test]
215 fn deep_normalize_does_not_share_storage_with_input() {
216 let arr = Arc::new(RwLock::new(vec![StrykeValue::integer(1)]));
217 let v = StrykeValue::array_ref(arr.clone());
218 let out = deep_normalize(&v);
219 let out_arr = out.as_array_ref().expect("array_ref");
220 out_arr.write().push(StrykeValue::integer(2));
221 assert_eq!(
222 arr.read().len(),
223 1,
224 "deep_normalize must clone, not alias, ref storage"
225 );
226 }
227
228 #[test]
229 fn normalize_args_head_normalizes_first_only() {
230 let h = href(&[("k", StrykeValue::integer(7))]);
231 let tail = StrykeValue::string("opt".into());
232 let out = normalize_args_head(&[h, tail.clone()]);
233 assert_eq!(out.len(), 2);
234 assert!(out[0].as_hash_ref().is_some());
235 assert_eq!(out[1].to_string(), tail.to_string());
236 }
237
238 #[test]
239 fn normalize_args_head_empty_returns_empty() {
240 assert!(normalize_args_head(&[]).is_empty());
241 }
242
243 #[test]
244 fn register_class_def_appears_in_field_names() {
245 use crate::ast::ClassDef;
246 let prev = install_class_defs(HashMap::new());
247 let def = Arc::new(ClassDef {
248 name: "T".into(),
249 is_abstract: false,
250 is_final: false,
251 extends: vec![],
252 implements: vec![],
253 fields: vec![],
254 methods: vec![],
255 static_fields: vec![],
256 });
257 register_class_def(def);
258 let names = CLASS_DEFS_REGISTRY.with(|c| c.borrow().keys().cloned().collect::<Vec<_>>());
259 assert!(names.contains(&"T".to_string()));
260 install_class_defs(prev);
262 }
263}