1use core::{
2 fmt::{self, Display, Formatter, Write},
3 mem,
4};
5
6use crate::vm::{
7 Any, Array, BigInt, Function, IVm, Number, Object, String, Unpacked, dispatch::Dispatch,
8 nullish::Nullish, string_coercion::number_to_string,
9};
10
11#[derive(Debug, PartialEq)]
24pub enum JsonError {
25 Undefined,
26 NonFiniteNumber(Number),
27 BigInt,
28 Function,
29}
30
31fn non_finite_spelling(v: Number) -> &'static str {
34 if v.is_nan() {
35 "NaN"
36 } else if v > 0.into() {
37 "Infinity"
38 } else {
39 "-Infinity"
40 }
41}
42
43impl Display for JsonError {
44 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
45 match self {
46 JsonError::Undefined => write!(f, "`undefined` has no JSON representation"),
47 JsonError::NonFiniteNumber(v) => {
48 write!(f, "{} has no JSON representation", non_finite_spelling(*v))
49 }
50 JsonError::BigInt => write!(f, "a BigInt has no JSON representation"),
51 JsonError::Function => write!(f, "a function has no JSON representation"),
52 }
53 }
54}
55
56fn json_string<A: IVm>(v: String<A>) -> std::string::String {
62 let mut out = std::string::String::from("\"");
63 for r in char::decode_utf16(v) {
64 match r {
65 Ok('"') => out.push_str("\\\""),
66 Ok('\\') => out.push_str("\\\\"),
67 Ok('\u{8}') => out.push_str("\\b"),
68 Ok('\u{c}') => out.push_str("\\f"),
69 Ok('\n') => out.push_str("\\n"),
70 Ok('\r') => out.push_str("\\r"),
71 Ok('\t') => out.push_str("\\t"),
72 Ok(c) if (c as u32) < 0x20 => {
73 let _ = write!(out, "\\u{:04x}", c as u32);
74 }
75 Ok(c) => out.push(c),
76 Err(e) => {
83 let _ = write!(out, "\\u{:04x}", e.unpaired_surrogate());
84 }
85 }
86 }
87 out.push('"');
88 out
89}
90
91struct ToJson;
92
93impl<A: IVm> Dispatch<A> for ToJson {
94 type Result = Result<std::string::String, JsonError>;
95
96 fn nullish(self, v: Nullish) -> Self::Result {
97 match v {
98 Nullish::Null => Ok("null".into()),
99 Nullish::Undefined => Err(JsonError::Undefined),
100 }
101 }
102
103 fn bool(self, v: bool) -> Self::Result {
104 Ok(if v { "true" } else { "false" }.into())
105 }
106
107 fn number(self, v: Number) -> Self::Result {
108 if !v.is_finite() {
109 return Err(JsonError::NonFiniteNumber(v));
110 }
111 Ok(number_to_string::<A>(v).into())
119 }
120
121 fn string(self, v: String<A>) -> Self::Result {
122 Ok(json_string(v))
123 }
124
125 fn bigint(self, _: BigInt<A>) -> Self::Result {
126 Err(JsonError::BigInt)
127 }
128
129 fn object(self, _: Object<A>) -> Self::Result {
131 unreachable!("an object is walked by `to_json`'s stack")
132 }
133
134 fn array(self, _: Array<A>) -> Self::Result {
135 unreachable!("an array is walked by `to_json`'s stack")
136 }
137
138 fn function(self, _: Function<A>) -> Self::Result {
139 Err(JsonError::Function)
140 }
141}
142
143enum Open<A: IVm> {
146 Array(<Array<A> as IntoIterator>::IntoIter, bool),
147 Object(std::vec::IntoIter<(String<A>, Any<A>)>, bool),
148}
149
150impl<A: IVm> Any<A> {
151 pub fn to_json(self) -> Result<std::string::String, JsonError> {
167 let mut out = std::string::String::new();
168 let mut stack: Vec<Open<A>> = Vec::new();
169 let mut value = Some(self);
170 loop {
171 if let Some(v) = value.take() {
172 match Unpacked::from(v) {
173 Unpacked::Array(a) => {
174 out.push('[');
175 stack.push(Open::Array(a.into_iter(), false));
176 }
177 Unpacked::Object(o) => {
178 out.push('{');
179 stack.push(Open::Object(o.own_entries().into_iter(), false));
180 }
181 scalar => out.push_str(&Any::from(scalar).dispatch(ToJson)?),
182 }
183 }
184 match stack.last_mut() {
185 None => return Ok(out),
186 Some(Open::Array(items, comma)) => match items.next() {
187 Some(item) => {
188 if mem::replace(comma, true) {
189 out.push(',');
190 }
191 value = Some(item);
192 }
193 None => {
194 out.push(']');
195 stack.pop();
196 }
197 },
198 Some(Open::Object(entries, comma)) => match entries.next() {
199 Some((key, item)) => {
200 if mem::replace(comma, true) {
201 out.push(',');
202 }
203 out.push_str(&json_string(key));
204 out.push(':');
205 value = Some(item);
206 }
207 None => {
208 out.push('}');
209 stack.pop();
210 }
211 },
212 }
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use crate::{
220 naive::Naive,
221 vm::{Any, Function, IVm, Nullish, String, ToAny, ToArray, ToObject, ToString},
222 };
223
224 type A = Naive;
225
226 fn s(v: &str) -> Any<A> {
227 String::<A>::from(v).to_any()
228 }
229
230 #[test]
231 fn number() {
232 assert_eq!(42.0.to_any::<A>().to_json(), Ok("42".into()));
233 assert_eq!((-1.5).to_any::<A>().to_json(), Ok("-1.5".into()));
234 }
235
236 #[test]
237 fn negative_zero_renders_as_zero() {
238 assert_eq!((-0.0).to_any::<A>().to_json(), Ok("0".into()));
241 }
242
243 #[test]
244 fn large_and_small_magnitudes_use_exponential_notation() {
245 assert_eq!(1e21.to_any::<A>().to_json(), Ok("1e+21".into()));
249 assert_eq!(1e-7.to_any::<A>().to_json(), Ok("1e-7".into()));
250 }
251
252 #[test]
253 fn non_finite_number_errors() {
254 assert!(matches!(
257 f64::NAN.to_any::<A>().to_json(),
258 Err(super::JsonError::NonFiniteNumber(v)) if v.is_nan()
259 ));
260 assert_eq!(
261 f64::INFINITY.to_any::<A>().to_json(),
262 Err(super::JsonError::NonFiniteNumber(f64::INFINITY.into()))
263 );
264 }
265
266 #[test]
268 fn non_finite_number_error_message() {
269 let message = |v: f64| format!("{}", v.to_any::<A>().to_json().unwrap_err());
270 assert_eq!(message(f64::NAN), "NaN has no JSON representation");
271 assert_eq!(
272 message(f64::INFINITY),
273 "Infinity has no JSON representation"
274 );
275 assert_eq!(
276 message(f64::NEG_INFINITY),
277 "-Infinity has no JSON representation"
278 );
279 }
280
281 #[test]
282 fn string() {
283 assert_eq!(s("hello").to_json(), Ok(r#""hello""#.into()));
284 assert_eq!(s("a\"b\\c\nd").to_json(), Ok(r#""a\"b\\c\nd""#.into()));
285 }
286
287 #[test]
288 fn backspace_and_form_feed_use_short_escapes() {
289 assert_eq!(s("\u{8}").to_json(), Ok(r#""\b""#.into()));
292 assert_eq!(s("\u{c}").to_json(), Ok(r#""\f""#.into()));
293 }
294
295 #[test]
296 fn unpaired_surrogate_is_escaped_not_an_error() {
297 let lone_high_surrogate: String<A> = [0xd800u16].to_string();
298 assert_eq!(
299 lone_high_surrogate.to_any::<A>().to_json(),
300 Ok(r#""\ud800""#.into())
301 );
302 }
303
304 #[test]
305 fn bigint_errors() {
306 let bi: crate::vm::BigInt<A> = 123u64.into();
307 assert_eq!(bi.to_any::<A>().to_json(), Err(super::JsonError::BigInt));
308 }
309
310 #[test]
311 fn empty_array() {
312 let a = [].to_array::<A>();
313 assert_eq!(a.to_any::<A>().to_json(), Ok("[]".into()));
314 }
315
316 #[test]
317 fn array_of_scalars() {
318 let a: crate::vm::Array<A> = [1.0.to_any(), true.to_any(), s("x")].to_array();
319 assert_eq!(a.to_any::<A>().to_json(), Ok(r#"[1,true,"x"]"#.into()));
320 }
321
322 #[test]
323 fn nested_arrays() {
324 let inner: crate::vm::Array<A> = [1.0.to_any()].to_array();
325 let outer: crate::vm::Array<A> = [inner.to_any(), 2.0.to_any()].to_array();
326 assert_eq!(outer.to_any::<A>().to_json(), Ok("[[1],2]".into()));
327 }
328
329 #[test]
330 fn array_element_error_propagates_as_the_scalar_it_is() {
331 let a: crate::vm::Array<A> = [1.0.to_any(), Any::undefined()].to_array();
335 assert_eq!(a.to_any::<A>().to_json(), Err(super::JsonError::Undefined));
336 }
337
338 #[test]
339 fn empty_object() {
340 let o = [].to_object::<A>();
341 assert_eq!(o.to_any::<A>().to_json(), Ok("{}".into()));
342 }
343
344 #[test]
345 fn object_of_scalars() {
346 let o: crate::vm::Object<A> =
347 [("a".into(), 1.0.to_any()), ("b".into(), s("two"))].to_object();
348 assert_eq!(o.to_any::<A>().to_json(), Ok(r#"{"a":1,"b":"two"}"#.into()));
349 }
350
351 #[test]
352 fn nested_objects() {
353 let inner: crate::vm::Object<A> = [("x".into(), 1.0.to_any())].to_object();
354 let outer: crate::vm::Object<A> = [("a".into(), inner.to_any())].to_object();
355 assert_eq!(outer.to_any::<A>().to_json(), Ok(r#"{"a":{"x":1}}"#.into()));
356 }
357
358 #[test]
359 fn duplicate_key_keeps_last_value_at_first_position() {
360 let o: crate::vm::Object<A> = [
364 ("a".into(), 1.0.to_any()),
365 ("b".into(), 2.0.to_any()),
366 ("a".into(), 3.0.to_any()),
367 ]
368 .to_object();
369 assert_eq!(o.to_any::<A>().to_json(), Ok(r#"{"a":3,"b":2}"#.into()));
370 }
371
372 #[test]
373 fn object_member_error_propagates_as_the_scalar_it_is() {
374 let o: crate::vm::Object<A> = [("a".into(), Any::undefined())].to_object();
375 assert_eq!(o.to_any::<A>().to_json(), Err(super::JsonError::Undefined));
376 }
377
378 #[test]
379 fn integer_like_keys_sort_ascending_ahead_of_other_keys() {
380 let o: crate::vm::Object<A> = [
384 ("a".into(), 0.0.to_any()),
385 ("2".into(), 2.0.to_any()),
386 ("1".into(), 1.0.to_any()),
387 ]
388 .to_object();
389 assert_eq!(
390 o.to_any::<A>().to_json(),
391 Ok(r#"{"1":1,"2":2,"a":0}"#.into())
392 );
393 }
394
395 #[test]
396 fn integer_like_keys_sort_numerically_not_lexicographically() {
397 let o: crate::vm::Object<A> = [("10".into(), s("b")), ("2".into(), s("a"))].to_object();
399 assert_eq!(
400 o.to_any::<A>().to_json(),
401 Ok(r#"{"2":"a","10":"b"}"#.into())
402 );
403 }
404
405 #[test]
406 fn zero_key_sorts_first() {
407 let o: crate::vm::Object<A> =
408 [("b".into(), 1.0.to_any()), ("0".into(), 2.0.to_any())].to_object();
409 assert_eq!(o.to_any::<A>().to_json(), Ok(r#"{"0":2,"b":1}"#.into()));
410 }
411
412 #[test]
413 fn duplicate_integer_like_key_keeps_last_value_sorted_by_number() {
414 let o: crate::vm::Object<A> = [
419 ("1".into(), s("a")),
420 ("0".into(), s("x")),
421 ("1".into(), s("b")),
422 ]
423 .to_object();
424 assert_eq!(o.to_any::<A>().to_json(), Ok(r#"{"0":"x","1":"b"}"#.into()));
425 }
426
427 #[test]
428 fn leading_zero_key_is_not_integer_like() {
429 let o: crate::vm::Object<A> = [("01".into(), s("a"))].to_object();
433 assert_eq!(o.to_any::<A>().to_json(), Ok(r#"{"01":"a"}"#.into()));
434 }
435
436 #[test]
437 fn max_uint32_key_is_not_integer_like() {
438 let o: crate::vm::Object<A> = [("4294967295".into(), s("a"))].to_object();
441 assert_eq!(
442 o.to_any::<A>().to_json(),
443 Ok(r#"{"4294967295":"a"}"#.into())
444 );
445 }
446
447 #[test]
448 fn overflowing_digit_string_key_is_not_integer_like() {
449 let o: crate::vm::Object<A> = [("99999999999".into(), s("a"))].to_object();
450 assert_eq!(
451 o.to_any::<A>().to_json(),
452 Ok(r#"{"99999999999":"a"}"#.into())
453 );
454 }
455
456 #[test]
457 fn integer_like_key_nested_in_an_array_serializes() {
458 let o: crate::vm::Object<A> = [("0".into(), 1.0.to_any())].to_object();
459 let a: crate::vm::Array<A> = [o.to_any()].to_array();
460 assert_eq!(a.to_any::<A>().to_json(), Ok(r#"[{"0":1}]"#.into()));
461 }
462
463 #[test]
464 fn function_errors() {
465 let f: Function<A> =
466 A::static_function(|_, _| Ok(Any::undefined()), 0, [].to_array(), None);
467 assert_eq!(f.to_any::<A>().to_json(), Err(super::JsonError::Function));
468 }
469
470 #[test]
471 fn boolean() {
472 assert_eq!(true.to_any::<A>().to_json(), Ok("true".into()));
473 assert_eq!(false.to_any::<A>().to_json(), Ok("false".into()));
474 }
475
476 #[test]
477 fn null_and_undefined() {
478 assert_eq!(Nullish::Null.to_any::<A>().to_json(), Ok("null".into()));
479 assert_eq!(
480 Any::<A>::undefined().to_json(),
481 Err(super::JsonError::Undefined)
482 );
483 }
484
485 #[test]
489 fn writes_a_deeply_nested_value() {
490 use crate::vm::test::deep::{DEPTH, leak, nested_arrays, nested_objects, small_stack};
491 small_stack(|| {
492 let arrays = nested_arrays(DEPTH, Nullish::Null.to_any());
493 leak(arrays.clone());
494 assert_eq!(
495 arrays.clone().to_json(),
496 Ok(format!("{}null{}", "[".repeat(DEPTH), "]".repeat(DEPTH)))
497 );
498 let objects = nested_objects(DEPTH, Nullish::Null.to_any());
499 leak(objects.clone());
500 assert_eq!(
501 objects.clone().to_json(),
502 Ok(format!(
503 "{}null{}",
504 r#"{"a":"#.repeat(DEPTH),
505 "}".repeat(DEPTH)
506 ))
507 );
508 });
509 }
510
511 #[test]
513 fn a_deep_refusal_is_reported() {
514 use crate::vm::test::deep::{DEPTH, leak, nested_arrays, small_stack};
515 small_stack(|| {
516 let a = nested_arrays(DEPTH, Any::undefined());
517 leak(a.clone());
518 assert_eq!(a.clone().to_json(), Err(super::JsonError::Undefined));
519 });
520 }
521
522 #[test]
524 fn commas_between_siblings_in_nested_containers() {
525 let inner: crate::vm::Any<A> = [s("x"), s("y")].to_array().to_any();
526 let o: crate::vm::Any<A> = [
527 ("a".into(), inner.clone()),
528 ("b".into(), [].to_array().to_any()),
529 ("c".into(), [inner, s("z")].to_array().to_any()),
530 ]
531 .to_object()
532 .to_any();
533 assert_eq!(
534 o.to_json(),
535 Ok(r#"{"a":["x","y"],"b":[],"c":[["x","y"],"z"]}"#.into())
536 );
537 }
538}