1use crate::object::array_obj::JSArrayObject;
2use crate::object::object::JSObject;
3use crate::runtime::context::JSContext;
4use crate::value::JSValue;
5
6pub fn json_parse(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
7 if args.is_empty() || !args[0].is_string() {
8 return JSValue::undefined();
9 }
10
11 let atom = args[0].get_atom();
12 let s = ctx.get_atom_str(atom).to_string();
13
14 match super::json_parser::JsonParser::new(&s).parse_value(ctx) {
15 Ok(v) => v,
16 Err(_) => JSValue::undefined(),
17 }
18}
19
20pub fn json_stringify(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
21 if args.is_empty() {
22 return JSValue::undefined();
23 }
24
25 let value = &args[0];
26 let replacer = args.get(1);
27 let space = args.get(2);
28
29 let json_str = jsvalue_to_json_with_options(ctx, value, replacer, space, &mut Vec::new());
30
31 JSValue::new_string(ctx.intern(&json_str))
32}
33
34fn jsvalue_to_json_with_options(
35 ctx: &mut JSContext,
36 value: &JSValue,
37 replacer: Option<&JSValue>,
38 space: Option<&JSValue>,
39 seen: &mut Vec<usize>,
40) -> String {
41 let processed_value = if let Some(repl) = replacer {
42 if repl.is_function() {
43 let vm_ptr = ctx.get_register_vm_ptr();
44 if let Some(ptr) = vm_ptr {
45 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
46 let args = vec![JSValue::new_string(ctx.intern("")), *value];
47 if let Ok(result) = vm.call_function(ctx, *repl, &args) {
48 result
49 } else {
50 *value
51 }
52 } else {
53 *value
54 }
55 } else {
56 *value
57 }
58 } else {
59 *value
60 };
61
62 let base_json = jsvalue_to_json_internal(ctx, &processed_value, replacer, seen);
63
64 if let Some(sp) = space {
65 let indent = get_indent_string_with_ctx(ctx, sp);
66 if !indent.is_empty() {
67 return format_json(&base_json, &indent);
68 }
69 }
70
71 base_json
72}
73
74fn get_indent_string_with_ctx(ctx: &mut JSContext, space: &JSValue) -> String {
75 if space.is_int() {
76 let n = space.get_int().min(10).max(0) as usize;
77 " ".repeat(n)
78 } else if space.is_string() {
79 let atom = space.get_atom();
80 let s_str = ctx.get_atom_str(atom).to_string();
81 s_str.chars().take(10).collect()
82 } else {
83 String::new()
84 }
85}
86
87fn format_json(json: &str, indent: &str) -> String {
88 let mut result = String::new();
89 let mut level = 0;
90 let mut in_string = false;
91 let mut escape = false;
92 let chars: Vec<char> = json.chars().collect();
93
94 for i in 0..chars.len() {
95 let c = chars[i];
96
97 if escape {
98 escape = false;
99 result.push(c);
100 continue;
101 }
102
103 if c == '\\' && in_string {
104 escape = true;
105 result.push(c);
106 continue;
107 }
108
109 if c == '"' {
110 in_string = !in_string;
111 result.push(c);
112 continue;
113 }
114
115 if in_string {
116 result.push(c);
117 continue;
118 }
119
120 match c {
121 '{' | '[' => {
122 result.push(c);
123 level += 1;
124 result.push('\n');
125 result.push_str(&indent.repeat(level));
126 }
127 '}' | ']' => {
128 level = level.saturating_sub(1);
129 result.push('\n');
130 result.push_str(&indent.repeat(level));
131 result.push(c);
132 }
133 ',' => {
134 result.push(c);
135 result.push('\n');
136 result.push_str(&indent.repeat(level));
137 }
138 ':' => {
139 result.push(c);
140 result.push(' ');
141 }
142 _ => {
143 result.push(c);
144 }
145 }
146 }
147
148 result
149}
150
151fn jsvalue_to_json_internal(
152 ctx: &mut JSContext,
153 value: &JSValue,
154 replacer: Option<&JSValue>,
155 seen: &mut Vec<usize>,
156) -> String {
157 if value.is_null() {
158 return "null".to_string();
159 }
160 if value.is_bool() {
161 return value.get_bool().to_string();
162 }
163 if value.is_int() {
164 return value.get_int().to_string();
165 }
166 if value.is_float() {
167 let f = value.get_float();
168 if f.is_nan() || f.is_infinite() {
169 return "null".to_string();
170 }
171 return f.to_string();
172 }
173 if value.is_string() {
174 let atom = value.get_atom();
175 let s = ctx.get_atom_str(atom);
176
177 let escaped = s
178 .replace('\\', "\\\\")
179 .replace('"', "\\\"")
180 .replace('\n', "\\n")
181 .replace('\r', "\\r")
182 .replace('\t', "\\t");
183 return format!("\"{}\"", escaped);
184 }
185 if value.is_undefined() || value.is_function() || value.is_symbol() {
186 return "null".to_string();
187 }
188
189 if value.is_object() {
190 let ptr = value.get_ptr() as usize;
191
192 if seen.contains(&ptr) {
193 return "null".to_string();
194 }
195 seen.push(ptr);
196
197 let obj = value.as_object();
198
199 if obj.is_array() {
200 let result = array_to_json(ctx, obj, replacer, seen);
201 seen.pop();
202 return result;
203 } else {
204 let result = object_to_json(ctx, obj, replacer, seen);
205 seen.pop();
206 return result;
207 }
208 }
209
210 "null".to_string()
211}
212
213fn array_to_json(
214 ctx: &mut JSContext,
215 arr: &JSObject,
216 replacer: Option<&JSValue>,
217 seen: &mut Vec<usize>,
218) -> String {
219 let len_atom = ctx.common_atoms.length;
220 let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
221
222 let mut elements = Vec::new();
223 let arr_ptr = arr as *const JSObject as usize;
224 let is_jsarray = arr.is_dense_array();
225 for i in 0..len {
226 let val = if is_jsarray {
227 unsafe { &*(arr_ptr as *const JSArrayObject) }.get(i)
228 } else if let Some(v) = arr.get_indexed(i) {
229 Some(v)
230 } else {
231 let key = ctx.int_atom_mut(i);
232 arr.get(key)
233 };
234 if let Some(val) = val {
235 let processed = if let Some(repl) = replacer {
236 if repl.is_function() {
237 let vm_ptr = ctx.get_register_vm_ptr();
238 if let Some(ptr) = vm_ptr {
239 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
240 let args = vec![JSValue::new_string(ctx.intern(&i.to_string())), val];
241 if let Ok(result) = vm.call_function(ctx, *repl, &args) {
242 result
243 } else {
244 val
245 }
246 } else {
247 val
248 }
249 } else {
250 val
251 }
252 } else {
253 val
254 };
255 elements.push(jsvalue_to_json_internal(ctx, &processed, replacer, seen));
256 } else {
257 elements.push("null".to_string());
258 }
259 }
260
261 format!("[{}]", elements.join(","))
262}
263
264fn object_to_json(
265 ctx: &mut JSContext,
266 obj: &JSObject,
267 replacer: Option<&JSValue>,
268 seen: &mut Vec<usize>,
269) -> String {
270 let mut pairs = Vec::new();
271
272 let filter_keys: Option<Vec<crate::runtime::atom::Atom>> = if let Some(repl) = replacer {
273 if repl.is_object() {
274 let repl_obj = repl.as_object();
275 if repl_obj.is_array() {
276 let len_atom = ctx.common_atoms.length;
277 let len = repl_obj
278 .get(len_atom)
279 .map(|v| v.get_int() as usize)
280 .unwrap_or(0);
281 let mut keys = Vec::new();
282 let repl_ptr = repl_obj as *const JSObject as usize;
283 let is_jsarray = repl_obj.is_dense_array();
284 for i in 0..len {
285 let val = if is_jsarray {
286 unsafe { &*(repl_ptr as *const JSArrayObject) }.get(i)
287 } else if let Some(v) = repl_obj.get_indexed(i) {
288 Some(v)
289 } else {
290 let key = ctx.int_atom_mut(i);
291 repl_obj.get(key)
292 };
293 if let Some(k) = val {
294 if k.is_string() {
295 keys.push(k.get_atom());
296 }
297 }
298 }
299 Some(keys)
300 } else {
301 None
302 }
303 } else {
304 None
305 }
306 } else {
307 None
308 };
309
310 let properties: Vec<(crate::runtime::atom::Atom, JSValue)> = obj.own_properties();
311
312 for (key, val) in properties {
313 if let Some(ref filter) = filter_keys {
314 if !filter.contains(&key) {
315 continue;
316 }
317 }
318
319 let key_str = ctx.get_atom_str(key).to_string();
320
321 let processed = if let Some(repl) = replacer {
322 if repl.is_function() {
323 let vm_ptr = ctx.get_register_vm_ptr();
324 if let Some(ptr) = vm_ptr {
325 let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
326 let args = vec![JSValue::new_string(ctx.intern(&key_str)), val];
327 if let Ok(result) = vm.call_function(ctx, *repl, &args) {
328 result
329 } else {
330 val
331 }
332 } else {
333 val
334 }
335 } else {
336 val
337 }
338 } else {
339 val
340 };
341
342 if processed.is_undefined() || processed.is_function() || processed.is_symbol() {
343 continue;
344 }
345
346 let value_str = jsvalue_to_json_internal(ctx, &processed, replacer, seen);
347 let escaped_key = key_str.replace('\\', "\\\\").replace('"', "\\\"");
348 pairs.push(format!("\"{}\":{}", escaped_key, value_str));
349 }
350
351 format!("{{{}}}", pairs.join(","))
352}