Line data Source code
1 : /* JSON trees
2 : Copyright (C) 2017-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "config.h"
22 : #include "system.h"
23 : #include "coretypes.h"
24 : #include "json.h"
25 : #include "pretty-print.h"
26 : #include "math.h"
27 : #include "selftest.h"
28 :
29 : using namespace json;
30 :
31 : /* Print a JSON string to PP, escaping '"', control characters,
32 : and embedded null bytes.
33 : The string is required to be UTF-8 encoded. */
34 :
35 : static void
36 1230874 : print_escaped_json_string (pretty_printer *pp,
37 : const char *utf8_str,
38 : size_t len)
39 : {
40 1230874 : pp_character (pp, '"');
41 16848931 : for (size_t i = 0; i != len; ++i)
42 : {
43 15618057 : char ch = utf8_str[i];
44 15618057 : switch (ch)
45 : {
46 3018 : case '"':
47 3018 : pp_string (pp, "\\\"");
48 3018 : break;
49 1609 : case '\\':
50 1609 : pp_string (pp, "\\\\");
51 1609 : break;
52 8 : case '\b':
53 8 : pp_string (pp, "\\b");
54 8 : break;
55 8 : case '\f':
56 8 : pp_string (pp, "\\f");
57 8 : break;
58 48337 : case '\n':
59 48337 : pp_string (pp, "\\n");
60 48337 : break;
61 8 : case '\r':
62 8 : pp_string (pp, "\\r");
63 8 : break;
64 2704 : case '\t':
65 2704 : pp_string (pp, "\\t");
66 2704 : break;
67 4 : case '\0':
68 4 : pp_string (pp, "\\0");
69 4 : break;
70 15562361 : default:
71 15562361 : pp_character (pp, ch);
72 : }
73 : }
74 1230874 : pp_character (pp, '"');
75 1230874 : }
76 :
77 : /* class pointer::token. */
78 :
79 4100543 : pointer::token::token ()
80 : {
81 4100543 : m_parent = nullptr;
82 4100543 : m_data.u_member = nullptr;
83 4100543 : m_kind = kind::root_value;
84 4100543 : }
85 :
86 3304803 : pointer::token::token (json::object &parent, const char *member)
87 : {
88 3304803 : m_parent = &parent;
89 3304803 : m_data.u_member = xstrdup (member); // ideally we'd share
90 3304803 : m_kind = kind::object_member;
91 3304803 : }
92 :
93 789805 : pointer::token::token (json::array &parent, size_t index)
94 : {
95 789805 : m_parent = &parent;
96 789805 : m_data.u_index = index;
97 789805 : m_kind = kind::array_index;
98 789805 : }
99 :
100 8193793 : pointer::token::~token ()
101 : {
102 8193793 : if (m_kind == kind::object_member)
103 : {
104 3303664 : gcc_assert (m_data.u_member);
105 3303664 : free (m_data.u_member);
106 : }
107 8193793 : }
108 :
109 : pointer::token &
110 4094608 : pointer::token::operator= (pointer::token &&other)
111 : {
112 4094608 : m_parent = other.m_parent;
113 4094608 : m_data = other.m_data;
114 4094608 : m_kind = other.m_kind;
115 :
116 4094608 : other.m_parent = nullptr;
117 4094608 : other.m_data.u_member = nullptr;
118 4094608 : other.m_kind = kind::root_value;
119 :
120 4094608 : return *this;
121 : }
122 :
123 : /* Print this to PP as an RFC 6901 section 3 reference-token. */
124 :
125 : void
126 60 : pointer::token::print (pretty_printer *pp) const
127 : {
128 60 : switch (m_kind)
129 : {
130 : case kind::root_value:
131 : break;
132 :
133 24 : case kind::object_member:
134 24 : {
135 96 : for (const char *ch = m_data.u_member; *ch; ++ch)
136 : {
137 72 : switch (*ch)
138 : {
139 4 : case '~':
140 4 : pp_string (pp, "~0");
141 4 : break;
142 4 : case '/':
143 4 : pp_string (pp, "~1");
144 4 : break;
145 64 : default:
146 64 : pp_character (pp, *ch);
147 64 : break;
148 : }
149 : }
150 : }
151 : break;
152 :
153 8 : case kind::array_index:
154 8 : pp_scalar (pp, HOST_SIZE_T_PRINT_UNSIGNED, (fmt_size_t) m_data.u_index);
155 8 : break;
156 : }
157 60 : }
158 :
159 : /* class json::value. */
160 :
161 : /* Dump this json::value tree to OUTF.
162 :
163 : The key/value pairs of json::objects are printed in the order
164 : in which the keys were originally inserted. */
165 :
166 : void
167 114 : value::dump (FILE *outf, bool formatted) const
168 : {
169 114 : pretty_printer pp;
170 114 : pp_buffer (&pp)->m_stream = outf;
171 114 : print (&pp, formatted);
172 114 : pp_flush (&pp);
173 114 : }
174 :
175 : /* A convenience function for debugging.
176 : Dump to stderr with formatting, and a trailing newline. */
177 :
178 : void
179 0 : value::dump () const
180 : {
181 0 : dump (stderr, true);
182 0 : fprintf (stderr, "\n");
183 0 : }
184 :
185 : /* A deterministic total ordering for comparing json values, so that we
186 : can e.g. put them in std::map.
187 :
188 : This is intended to follow the condition for equality described in
189 : the JSON Schema standard (§4.3, “Instance equality”), as referenced
190 : by SARIF v2.1.0 (§3.7.3 "Array properties with unique values"), but has
191 : the following limitations:
192 : - numbers are supposed to be checked for "the same mathematical value",
193 : but in this implementation int vs float numbers won't compare as equal,
194 : and float number comparison is bitwise
195 : - strings are supposed to be "the same codepoint-for-codepoint", but
196 : this implementation doesn't take into account canonicalization issues. */
197 :
198 : int
199 16798 : value::compare (const value &val_a, const value &val_b)
200 : {
201 16798 : enum kind kind_a = val_a.get_kind ();
202 16798 : enum kind kind_b = val_b.get_kind ();
203 16798 : if (kind_a != kind_b)
204 32 : return (int)kind_a - (int)kind_b;
205 :
206 16766 : switch (kind_a)
207 : {
208 0 : default:
209 0 : gcc_unreachable ();
210 :
211 5402 : case JSON_OBJECT:
212 5402 : {
213 5402 : const object &obj_a = (const object &)val_a;
214 5402 : const object &obj_b = (const object &)val_b;
215 5402 : return object::compare (obj_a, obj_b);
216 : }
217 56 : break;
218 :
219 56 : case JSON_ARRAY:
220 56 : {
221 56 : const array &arr_a = (const array &)val_a;
222 56 : const array &arr_b = (const array &)val_b;
223 128 : if (int cmp_size = (int)arr_a.size () - (int)arr_b.size ())
224 : return cmp_size;
225 64 : for (size_t idx = 0; idx < arr_a.size (); ++idx)
226 24 : if (int cmp_element = compare (*arr_a[idx], *arr_b[idx]))
227 : return cmp_element;
228 : return 0;
229 : }
230 81 : break;
231 :
232 81 : case JSON_INTEGER:
233 81 : {
234 81 : const integer_number &int_a = (const integer_number &)val_a;
235 81 : const integer_number &int_b = (const integer_number &)val_b;
236 81 : return int_a.get () - int_b.get ();
237 : }
238 32 : break;
239 :
240 32 : case JSON_FLOAT:
241 32 : {
242 32 : const float_number &float_a = (const float_number &)val_a;
243 32 : const float_number &float_b = (const float_number &)val_b;
244 32 : union u
245 : {
246 : double u_double;
247 : char u_buf[sizeof(double)];
248 : };
249 32 : union u u_a, u_b;
250 32 : u_a.u_double = float_a.get ();
251 32 : u_b.u_double = float_b.get ();
252 32 : return memcmp (&u_a, &u_b, sizeof(double));
253 : }
254 11155 : break;
255 :
256 11155 : case JSON_STRING:
257 11155 : {
258 11155 : const string &str_a = (const string &)val_a;
259 11155 : const string &str_b = (const string &)val_b;
260 11155 : return strcmp (str_a.get_string (), str_b.get_string ());
261 : }
262 : break;
263 :
264 : case JSON_TRUE:
265 : case JSON_FALSE:
266 : case JSON_NULL:
267 : /* All instances of literals compare equal to instances
268 : of the same literal. */
269 : return 0;
270 : }
271 : }
272 :
273 : /* Print this value's JSON Pointer to PP. */
274 :
275 : void
276 28 : value::print_pointer (pretty_printer *pp) const
277 : {
278 : /* Get path from this value to root. */
279 28 : auto_vec<const pointer::token *> ancestry;
280 88 : for (auto *iter = this; iter; iter = iter->m_pointer_token.m_parent)
281 60 : ancestry.safe_push (&iter->m_pointer_token);
282 :
283 : /* Walk backward, going from root to this value. */
284 28 : ancestry.reverse ();
285 28 : bool first = true;
286 144 : for (auto iter : ancestry)
287 : {
288 60 : if (first)
289 : first = false;
290 : else
291 32 : pp_character (pp, '/');
292 60 : iter->print (pp);
293 : }
294 28 : }
295 :
296 : /* class json::object, a subclass of json::value, representing
297 : an ordered collection of key/value pairs. */
298 :
299 : /* json:object's dtor. */
300 :
301 1474260 : object::~object ()
302 : {
303 10656998 : for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
304 : {
305 3303586 : free (const_cast <char *>((*it).first));
306 3303586 : delete ((*it).second);
307 : }
308 1474260 : }
309 :
310 : /* Implementation of json::value::print for json::object. */
311 :
312 : void
313 234500 : object::print (pretty_printer *pp, bool formatted) const
314 : {
315 234500 : pp_character (pp, '{');
316 234500 : if (formatted)
317 10552 : pp_indentation (pp) += 1;
318 :
319 : /* Iterate in the order that the keys were inserted. */
320 : unsigned i;
321 : const char *key;
322 994972 : FOR_EACH_VEC_ELT (m_keys, i, key)
323 : {
324 760472 : if (i > 0)
325 : {
326 525980 : pp_string (pp, ",");
327 525980 : if (formatted)
328 : {
329 14613 : pp_newline (pp);
330 14613 : pp_indent (pp);
331 : }
332 : else
333 511367 : pp_space (pp);
334 : }
335 760472 : map_t &mut_map = const_cast<map_t &> (m_map);
336 760472 : value *value = *mut_map.get (key);
337 760472 : print_escaped_json_string (pp, key, strlen (key));
338 760472 : pp_string (pp, ": ");
339 760472 : const int indent = strlen (key) + 4;
340 760472 : if (formatted)
341 25157 : pp_indentation (pp) += indent;
342 760472 : value->print (pp, formatted);
343 760472 : if (formatted)
344 25157 : pp_indentation (pp) -= indent;
345 : }
346 234500 : if (formatted)
347 10552 : pp_indentation (pp) -= 1;
348 234500 : pp_character (pp, '}');
349 234500 : }
350 :
351 : std::unique_ptr<value>
352 4 : object::clone () const
353 : {
354 4 : return clone_as_object ();
355 : }
356 :
357 : std::unique_ptr<object>
358 4 : object::clone_as_object () const
359 : {
360 4 : auto result = std::make_unique<object> ();
361 :
362 : /* Iterate in the order that the keys were inserted. */
363 4 : unsigned i;
364 4 : const char *key;
365 8 : FOR_EACH_VEC_ELT (m_keys, i, key)
366 : {
367 4 : map_t &mut_map = const_cast<map_t &> (m_map);
368 4 : value *value = *mut_map.get (key);
369 4 : result->set (key, value->clone ());
370 : }
371 :
372 4 : return result;
373 : }
374 :
375 : /* Set the json::value * for KEY, taking ownership of V
376 : (and taking a copy of KEY if necessary). */
377 :
378 : void
379 3304803 : object::set (const char *key, value *v)
380 : {
381 3304803 : gcc_assert (key);
382 3304803 : gcc_assert (v);
383 :
384 3304803 : value **ptr = m_map.get (key);
385 3304803 : if (ptr)
386 : {
387 : /* If the key is already present, delete the existing value
388 : and overwrite it. */
389 78 : delete *ptr;
390 78 : *ptr = v;
391 : }
392 : else
393 : {
394 : /* If the key wasn't already present, take a copy of the key,
395 : and store the value. */
396 3304725 : char *owned_key = xstrdup (key);
397 3304725 : m_map.put (owned_key, v);
398 3304725 : m_keys.safe_push (owned_key);
399 : }
400 :
401 3304803 : v->m_pointer_token = pointer::token (*this, key);
402 3304803 : }
403 :
404 : /* Get the json::value * for KEY.
405 :
406 : The object retains ownership of the value. */
407 :
408 : value *
409 25756 : object::get (const char *key) const
410 : {
411 25756 : gcc_assert (key);
412 :
413 25756 : value **ptr = const_cast <map_t &> (m_map).get (key);
414 25756 : if (ptr)
415 23205 : return *ptr;
416 : else
417 : return NULL;
418 : }
419 :
420 : /* Set value of KEY within this object to a JSON
421 : string value based on UTF8_VALUE. */
422 :
423 : void
424 1338388 : object::set_string (const char *key, const char *utf8_value)
425 : {
426 1338388 : set (key, new json::string (utf8_value));
427 1338388 : }
428 :
429 : /* Set value of KEY within this object to a JSON
430 : integer value based on V. */
431 :
432 : void
433 667978 : object::set_integer (const char *key, long v)
434 : {
435 667978 : set (key, new json::integer_number (v));
436 667978 : }
437 :
438 : /* Set value of KEY within this object to a JSON
439 : floating point value based on V. */
440 :
441 : void
442 16 : object::set_float (const char *key, double v)
443 : {
444 16 : set (key, new json::float_number (v));
445 16 : }
446 :
447 : /* Set value of KEY within this object to the JSON
448 : literal true or false, based on V. */
449 :
450 : void
451 489 : object::set_bool (const char *key, bool v)
452 : {
453 978 : set (key, new json::literal (v));
454 489 : }
455 :
456 : void
457 180 : object::set_string (const string_property &property, const char *utf8_value)
458 : {
459 180 : set_string (property.m_key.get (), utf8_value);
460 180 : }
461 :
462 : void
463 240 : object::set_integer (const integer_property &property, long value)
464 : {
465 240 : set_integer (property.m_key.get (), value);
466 240 : }
467 :
468 : void
469 0 : object::set_bool (const bool_property &property, bool value)
470 : {
471 0 : set_bool (property.m_key.get (), value);
472 0 : }
473 :
474 : void
475 780 : object::set_array_of_string (const array_of_string_property &property,
476 : std::unique_ptr<json::array> value)
477 : {
478 780 : set<array> (property.m_key.get (), std::move (value));
479 780 : }
480 :
481 : /* Subroutine of json::compare for comparing a pairs of objects. */
482 :
483 : int
484 5402 : object::compare (const json::object &obj_a, const json::object &obj_b)
485 : {
486 16130 : if (int cmp_size = (int)obj_a.m_keys.length () - (int)obj_b.m_keys.length ())
487 : return cmp_size;
488 :
489 18171 : for (auto iter_a : obj_a.m_map)
490 : {
491 11108 : const char *key = iter_a.first;
492 11108 : const value *value_a = iter_a.second;
493 11108 : gcc_assert (value_a);
494 :
495 11108 : const value *value_b = obj_b.get (key);
496 11108 : if (!value_b)
497 : /* Key is in OBJ_A but not in OBJ_B. */
498 4623 : return 1;
499 : /* If key is OBJ_B but not in OBJ_A, then the
500 : count of keys will have been different, or
501 : OBJ_A would have had a key not in OBJ_B. */
502 11108 : if (int cmp_value = value::compare (*value_a, *value_b))
503 : /* Values for key are non-equal. */
504 : return cmp_value;
505 : }
506 :
507 : /* Objects are equal. */
508 578 : return 0;
509 : }
510 :
511 : /* class json::array, a subclass of json::value, representing
512 : an ordered collection of values. */
513 :
514 : /* json::array's dtor. */
515 :
516 1239352 : array::~array ()
517 : {
518 620121 : unsigned i;
519 620121 : value *v;
520 1409847 : FOR_EACH_VEC_ELT (m_elements, i, v)
521 789726 : delete v;
522 1239352 : }
523 :
524 : /* Implementation of json::value::print for json::array. */
525 :
526 : void
527 89072 : array::print (pretty_printer *pp, bool formatted) const
528 : {
529 89072 : pp_character (pp, '[');
530 89072 : if (formatted)
531 3019 : pp_indentation (pp) += 1;
532 : unsigned i;
533 : value *v;
534 286423 : FOR_EACH_VEC_ELT (m_elements, i, v)
535 : {
536 197351 : if (i)
537 : {
538 123334 : pp_string (pp, ",");
539 123334 : if (formatted)
540 : {
541 6668 : pp_newline (pp);
542 6668 : pp_indent (pp);
543 : }
544 : else
545 116666 : pp_space (pp);
546 : }
547 197351 : v->print (pp, formatted);
548 : }
549 89072 : if (formatted)
550 3019 : pp_indentation (pp) -= 1;
551 89072 : pp_character (pp, ']');
552 89072 : }
553 :
554 : std::unique_ptr<value>
555 664 : array::clone () const
556 : {
557 664 : auto result = std::make_unique<array> ();
558 664 : unsigned i;
559 664 : value *v;
560 2301 : FOR_EACH_VEC_ELT (m_elements, i, v)
561 973 : result->append (v->clone ());
562 664 : return result;
563 664 : }
564 :
565 : /* Append non-NULL value V to a json::array, taking ownership of V. */
566 :
567 : void
568 789805 : array::append (value *v)
569 : {
570 789805 : gcc_assert (v);
571 1387895 : v->m_pointer_token = pointer::token (*this, m_elements.length ());
572 789805 : m_elements.safe_push (v);
573 789805 : }
574 :
575 : void
576 173909 : array::append_string (const char *utf8_value)
577 : {
578 173909 : gcc_assert (utf8_value);
579 173909 : append (new json::string (utf8_value));
580 173909 : }
581 :
582 : /* class json::float_number, a subclass of json::value, wrapping a double. */
583 :
584 : /* Implementation of json::value::print for json::float_number. */
585 :
586 : void
587 32 : float_number::print (pretty_printer *pp,
588 : bool formatted ATTRIBUTE_UNUSED) const
589 : {
590 32 : char tmp[1024];
591 32 : snprintf (tmp, sizeof (tmp), "%g", m_value);
592 32 : pp_string (pp, tmp);
593 32 : }
594 :
595 : std::unique_ptr<value>
596 4 : float_number::clone () const
597 : {
598 4 : return std::make_unique<float_number> (m_value);
599 : }
600 :
601 : /* class json::integer_number, a subclass of json::value, wrapping a long. */
602 :
603 : /* Implementation of json::value::print for json::integer_number. */
604 :
605 : void
606 163921 : integer_number::print (pretty_printer *pp,
607 : bool formatted ATTRIBUTE_UNUSED) const
608 : {
609 163921 : char tmp[1024];
610 163921 : snprintf (tmp, sizeof (tmp), "%ld", m_value);
611 163921 : pp_string (pp, tmp);
612 163921 : }
613 :
614 : std::unique_ptr<value>
615 217 : integer_number::clone () const
616 : {
617 217 : return std::make_unique<integer_number> (m_value);
618 : }
619 :
620 : /* class json::string, a subclass of json::value. */
621 :
622 : /* json::string's ctor. */
623 :
624 2061304 : string::string (const char *utf8)
625 : {
626 2061304 : gcc_assert (utf8);
627 2061304 : m_utf8 = xstrdup (utf8);
628 2061304 : m_len = strlen (utf8);
629 2061304 : }
630 :
631 3434 : string::string (const char *utf8, size_t len)
632 : {
633 3434 : gcc_assert (utf8);
634 3434 : m_utf8 = XNEWVEC (char, len + 1);
635 3434 : m_len = len;
636 3434 : memcpy (m_utf8, utf8, len);
637 3434 : m_utf8[len] = '\0';
638 3434 : }
639 :
640 : /* Implementation of json::value::print for json::string. */
641 :
642 : void
643 470402 : string::print (pretty_printer *pp,
644 : bool formatted ATTRIBUTE_UNUSED) const
645 : {
646 470402 : print_escaped_json_string (pp, m_utf8, m_len);
647 470402 : }
648 :
649 : std::unique_ptr<value>
650 3197 : string::clone () const
651 : {
652 3197 : return std::make_unique<string> (m_utf8, m_len);
653 : }
654 :
655 : /* class json::literal, a subclass of json::value. */
656 :
657 : /* Implementation of json::value::print for json::literal. */
658 :
659 : void
660 225 : literal::print (pretty_printer *pp,
661 : bool formatted ATTRIBUTE_UNUSED) const
662 : {
663 225 : switch (m_kind)
664 : {
665 125 : case JSON_TRUE:
666 125 : pp_string (pp, "true");
667 125 : break;
668 92 : case JSON_FALSE:
669 92 : pp_string (pp, "false");
670 92 : break;
671 8 : case JSON_NULL:
672 8 : pp_string (pp, "null");
673 8 : break;
674 0 : default:
675 0 : gcc_unreachable ();
676 : }
677 225 : }
678 :
679 : std::unique_ptr<value>
680 4 : literal::clone () const
681 : {
682 4 : return std::make_unique<literal> (m_kind);
683 : }
684 :
685 :
686 : #if CHECKING_P
687 :
688 : namespace selftest {
689 :
690 : /* Selftests. */
691 :
692 : /* Verify that JV->print () prints EXPECTED_JSON. */
693 :
694 : void
695 160 : assert_print_eq (const location &loc,
696 : const json::value &jv,
697 : bool formatted,
698 : const char *expected_json)
699 : {
700 160 : pretty_printer pp;
701 160 : jv.print (&pp, formatted);
702 160 : ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
703 160 : }
704 :
705 : #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
706 : assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
707 :
708 : /* Verify that object::get works as expected. */
709 :
710 : static void
711 4 : test_object_get ()
712 : {
713 4 : object obj;
714 4 : value *val = new json::string ("value");
715 4 : obj.set ("foo", val);
716 4 : ASSERT_EQ (obj.get ("foo"), val);
717 4 : ASSERT_EQ (obj.get ("not-present"), NULL);
718 4 : }
719 :
720 : /* Verify that JSON objects are written correctly. */
721 :
722 : static void
723 4 : test_writing_objects ()
724 : {
725 4 : object obj;
726 4 : obj.set_string ("foo", "bar");
727 4 : obj.set_string ("baz", "quux");
728 4 : obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
729 :
730 : /* This test relies on json::object writing out key/value pairs
731 : in key-insertion order. */
732 4 : ASSERT_PRINT_EQ (obj, true,
733 : "{\"foo\": \"bar\",\n"
734 : " \"baz\": \"quux\",\n"
735 : " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
736 4 : ASSERT_PRINT_EQ (obj, false,
737 : "{\"foo\": \"bar\", \"baz\": \"quux\""
738 : ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
739 4 : }
740 :
741 : /* Verify that JSON arrays are written correctly. */
742 :
743 : static void
744 4 : test_writing_arrays ()
745 : {
746 4 : array arr;
747 4 : ASSERT_PRINT_EQ (arr, true, "[]");
748 :
749 4 : arr.append (new json::string ("foo"));
750 4 : ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
751 :
752 4 : arr.append_string ("bar");
753 4 : ASSERT_PRINT_EQ (arr, true,
754 : "[\"foo\",\n"
755 : " \"bar\"]");
756 4 : ASSERT_PRINT_EQ (arr, false,
757 : "[\"foo\", \"bar\"]");
758 4 : }
759 :
760 : /* Verify that JSON numbers are written correctly. */
761 :
762 : static void
763 4 : test_writing_float_numbers ()
764 : {
765 4 : ASSERT_PRINT_EQ (float_number (0), true, "0");
766 4 : ASSERT_PRINT_EQ (float_number (42), true, "42");
767 4 : ASSERT_PRINT_EQ (float_number (-100), true, "-100");
768 4 : ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
769 4 : }
770 :
771 : static void
772 4 : test_writing_integer_numbers ()
773 : {
774 4 : ASSERT_PRINT_EQ (integer_number (0), true, "0");
775 4 : ASSERT_PRINT_EQ (integer_number (42), true, "42");
776 4 : ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
777 4 : ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
778 4 : ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
779 4 : }
780 :
781 : /* Verify that JSON strings are written correctly. */
782 :
783 : static void
784 4 : test_writing_strings ()
785 : {
786 4 : string foo ("foo");
787 4 : ASSERT_PRINT_EQ (foo, true, "\"foo\"");
788 :
789 4 : string contains_quotes ("before \"quoted\" after");
790 4 : ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
791 :
792 4 : const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
793 4 : string not_terminated (data, 3);
794 4 : ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
795 4 : string embedded_null (data, sizeof data);
796 4 : ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
797 4 : }
798 :
799 : /* Verify that JSON literals are written correctly. */
800 :
801 : static void
802 4 : test_writing_literals ()
803 : {
804 4 : ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
805 4 : ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
806 4 : ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
807 :
808 4 : ASSERT_PRINT_EQ (literal (true), true, "true");
809 4 : ASSERT_PRINT_EQ (literal (false), true, "false");
810 4 : }
811 :
812 : /* Verify that nested values are formatted correctly when written.
813 :
814 : Also, make use of array::append(std::unique_ptr<value>) and
815 : object::set (const char *key, std::unique_ptr<value> v).*/
816 :
817 : static void
818 4 : test_formatting ()
819 : {
820 4 : object obj;
821 4 : object *child = new object;
822 4 : std::unique_ptr<object> grandchild = std::make_unique<object> ();
823 :
824 4 : obj.set_string ("str", "bar");
825 4 : obj.set ("child", child);
826 4 : obj.set_integer ("int", 42);
827 :
828 4 : array *arr = new array;
829 16 : for (int i = 0; i < 3; i++)
830 12 : arr->append (std::make_unique<integer_number> (i));
831 4 : grandchild->set ("arr", arr);
832 4 : grandchild->set_integer ("int", 1066);
833 :
834 4 : child->set ("grandchild", std::move (grandchild));
835 4 : child->set_integer ("int", 1776);
836 :
837 : /* This test relies on json::object writing out key/value pairs
838 : in key-insertion order. */
839 4 : ASSERT_PRINT_EQ (obj, true,
840 : ("{\"str\": \"bar\",\n"
841 : " \"child\": {\"grandchild\": {\"arr\": [0,\n"
842 : " 1,\n"
843 : " 2],\n"
844 : " \"int\": 1066},\n"
845 : " \"int\": 1776},\n"
846 : " \"int\": 42}"));
847 4 : ASSERT_PRINT_EQ (obj, false,
848 : ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
849 : " {\"arr\": [0, 1, 2], \"int\": 1066},"
850 : " \"int\": 1776}, \"int\": 42}"));
851 4 : }
852 :
853 : /* Helper function for reporting failure of JSON comparisons. */
854 :
855 : static void
856 0 : fail_comparison (const location &loc,
857 : const char *desc,
858 : const value &val_a, const value &val_b,
859 : const char *desc_expected_value,
860 : int actual_value)
861 : {
862 0 : fprintf (stderr, "val_a: ");
863 0 : val_a.dump ();
864 :
865 0 : fprintf (stderr, "val_b: ");
866 0 : val_b.dump ();
867 :
868 0 : selftest::fail_formatted (loc,
869 : "%s: failed JSON comparison:"
870 : " expected: %s got: %i\n",
871 : desc,
872 : desc_expected_value, actual_value);
873 : }
874 :
875 : /* Implementation of ASSERT_JSON_EQ. */
876 :
877 : static void
878 100 : assert_json_equal (const location &loc,
879 : const char *desc,
880 : const value &val_a, const value &val_b)
881 : {
882 : /* Comparison should return zero, both ways, indicating no differences. */
883 100 : const int a_vs_b = value::compare (val_a, val_b);
884 100 : if (a_vs_b != 0)
885 0 : fail_comparison (loc, desc, val_a, val_b, "zero", a_vs_b);
886 :
887 100 : const int b_vs_a = value::compare (val_b, val_a);
888 100 : if (b_vs_a != 0)
889 0 : fail_comparison (loc, desc, val_b, val_a, "zero", b_vs_a);
890 100 : }
891 :
892 : /* Verify that json::value::compare returns 0 ("no differences") on
893 : VAL1 and VAL2, in both orders. */
894 :
895 : #define ASSERT_JSON_EQ(VAL1, VAL2) \
896 : SELFTEST_BEGIN_STMT \
897 : assert_json_equal ((SELFTEST_LOCATION), \
898 : "ASSERT_JSON_EQ", \
899 : (VAL1), (VAL2)); \
900 : SELFTEST_END_STMT
901 :
902 : /* Implementation of ASSERT_JSON_NE. */
903 :
904 : static void
905 48 : assert_json_non_equal (const location &loc,
906 : const char *desc,
907 : const value &val_a, const value &val_b)
908 : {
909 : /* Comparison should be non-zero, indicating differences. */
910 48 : const int a_vs_b = value::compare (val_a, val_b);
911 48 : if (a_vs_b == 0)
912 0 : fail_comparison (loc, desc, val_a, val_b, "non-zero", a_vs_b);
913 :
914 48 : const int b_vs_a = value::compare (val_b, val_a);
915 48 : ASSERT_NE_AT (loc, b_vs_a, 0);
916 48 : if (b_vs_a == 0)
917 : fail_comparison (loc, desc, val_b, val_a, "non-zero", b_vs_a);
918 :
919 : /* Swapping the args should swap the sign of the result
920 : (but isn't necessarily the negation). */
921 48 : if ( (a_vs_b > 0) == (b_vs_a > 0) )
922 0 : fail_comparison (loc, desc, val_b, val_a, "opposite signs", 1);
923 48 : }
924 :
925 : /* Verify that json::value::compare returns non-zero ("different") on
926 : VAL1 and VAL2, in both orders, and that they have opposite
927 : sign. */
928 :
929 : #define ASSERT_JSON_NE(VAL1, VAL2) \
930 : SELFTEST_BEGIN_STMT \
931 : assert_json_non_equal ((SELFTEST_LOCATION), \
932 : "ASSERT_JSON_NE", \
933 : (VAL1), (VAL2)); \
934 : SELFTEST_END_STMT
935 :
936 : /* Verify that json::value::compare works as expected. */
937 :
938 : static void
939 4 : test_comparisons ()
940 : {
941 : /* Literals. */
942 :
943 4 : literal null_lit (JSON_NULL);
944 4 : ASSERT_JSON_EQ (null_lit, null_lit);
945 :
946 4 : literal other_null_lit (JSON_NULL);
947 4 : ASSERT_JSON_EQ (null_lit, other_null_lit);
948 :
949 4 : literal true_lit (JSON_TRUE);
950 4 : ASSERT_JSON_EQ (true_lit, true_lit);
951 4 : ASSERT_JSON_NE (true_lit, null_lit);
952 :
953 4 : literal false_lit (JSON_FALSE);
954 4 : ASSERT_JSON_EQ (false_lit, false_lit);
955 4 : ASSERT_JSON_NE (false_lit, true_lit);
956 4 : ASSERT_JSON_NE (false_lit, null_lit);
957 :
958 : /* Strings. */
959 4 : string str_foo_1 ("foo");
960 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_1);
961 :
962 4 : string str_foo_2 ("foo");
963 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_2);
964 :
965 4 : string str_bar ("bar");
966 4 : ASSERT_JSON_NE (str_bar, str_foo_1);
967 :
968 : /* Numbers. */
969 4 : integer_number i_42 (42);
970 4 : ASSERT_JSON_EQ (i_42, i_42);
971 4 : integer_number i_42_2 (42);
972 4 : ASSERT_JSON_EQ (i_42, i_42_2);
973 4 : integer_number i_43 (43);
974 4 : ASSERT_JSON_NE (i_42, i_43);
975 :
976 4 : float_number f_zero (0.0);
977 4 : ASSERT_JSON_EQ (f_zero, f_zero);
978 4 : float_number f_zero_2 (0.0);
979 4 : ASSERT_JSON_EQ (f_zero, f_zero_2);
980 4 : float_number f_one (1.0);
981 4 : ASSERT_JSON_NE (f_zero, f_one);
982 : /* We don't yet test the more awkward cases e.g. NaN. */
983 :
984 : /* Objects. */
985 :
986 : // Empty object
987 : // Self comparison should be 0
988 4 : object empty_obj_a;
989 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_a);
990 :
991 : // Instances of empty objects should compare equal to each other
992 4 : object empty_obj_b;
993 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_b);
994 :
995 : // Object with one field:
996 4 : object obj_1;
997 4 : obj_1.set_string ("foo", "bar");
998 : // Self comparison should be 0
999 4 : ASSERT_JSON_EQ (obj_1, obj_1);
1000 :
1001 : // but should be different to an empty object:
1002 4 : ASSERT_JSON_NE (obj_1, empty_obj_a);
1003 :
1004 : // Another with one field, with same key/value:
1005 4 : object obj_2;
1006 4 : obj_2.set_string ("foo", "bar");
1007 4 : ASSERT_JSON_EQ (obj_1, obj_2);
1008 :
1009 : // Same key, different value:
1010 4 : object obj_3;
1011 4 : obj_3.set_string ("foo", "baz");
1012 4 : ASSERT_JSON_NE (obj_1, obj_3);
1013 :
1014 : // Adding an extra property:
1015 4 : obj_2.set_integer ("year", 1066);
1016 4 : ASSERT_JSON_NE (obj_1, obj_2);
1017 :
1018 : /* Different insertion order, but same k-v pairs should be equal,
1019 : despite having different serialization. */
1020 4 : object obj_4;
1021 4 : obj_4.set_integer ("year", 1066);
1022 4 : obj_4.set_string ("foo", "bar");
1023 4 : ASSERT_JSON_EQ (obj_2, obj_4);
1024 4 : ASSERT_PRINT_EQ (obj_2, false, "{\"foo\": \"bar\", \"year\": 1066}");
1025 4 : ASSERT_PRINT_EQ (obj_4, false, "{\"year\": 1066, \"foo\": \"bar\"}");
1026 :
1027 : /* Arrays. */
1028 :
1029 : // Empty array
1030 4 : array empty_arr_a;
1031 : // Self comparison should be 0
1032 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_a);
1033 :
1034 : // Objects and arrays are different
1035 4 : ASSERT_JSON_NE (empty_obj_a, empty_arr_a);
1036 :
1037 : // Instances of empty arrays should compare equal to each other
1038 4 : array empty_arr_b;
1039 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_b);
1040 :
1041 : // Array with one element:
1042 4 : array arr_1;
1043 4 : arr_1.append (std::make_unique<string> ("foo"));
1044 : // Self comparison should be 0
1045 4 : ASSERT_JSON_EQ (arr_1, arr_1);
1046 :
1047 : // but should be different to an empty array:
1048 4 : ASSERT_JSON_NE (arr_1, empty_arr_a);
1049 :
1050 : // Another with one element:
1051 4 : array arr_2;
1052 4 : arr_2.append (std::make_unique<string> ("foo"));
1053 4 : ASSERT_JSON_EQ (arr_1, arr_2);
1054 :
1055 : // Adding an extra element:
1056 4 : arr_2.append (std::make_unique<string> ("bar"));
1057 4 : ASSERT_JSON_NE (arr_1, arr_2);
1058 4 : }
1059 :
1060 : /* Ensure that json::string's get_string is usable as a C-style string. */
1061 :
1062 : static void
1063 4 : test_strcmp ()
1064 : {
1065 4 : string str ("foobar", 3);
1066 4 : ASSERT_EQ (strcmp (str.get_string (), "foo"), 0);
1067 4 : }
1068 :
1069 : static void
1070 4 : test_cloning ()
1071 : {
1072 : // Objects
1073 4 : {
1074 4 : object obj;
1075 4 : obj.set_string ("foo", "bar");
1076 :
1077 4 : auto obj_clone = obj.clone ();
1078 4 : ASSERT_JSON_EQ (obj, *obj_clone);
1079 4 : }
1080 :
1081 : // Arrays
1082 4 : {
1083 4 : array arr;
1084 4 : arr.append (std::make_unique<string> ("foo"));
1085 :
1086 4 : auto arr_clone = arr.clone ();
1087 4 : ASSERT_JSON_EQ (arr, *arr_clone);
1088 4 : }
1089 :
1090 : // float_number
1091 4 : {
1092 4 : float_number f_one (1.0);
1093 4 : auto f_clone = f_one.clone ();
1094 4 : ASSERT_JSON_EQ (f_one, *f_clone);
1095 4 : }
1096 :
1097 : // integer_number
1098 4 : {
1099 4 : integer_number num (42);
1100 4 : auto num_clone = num.clone ();
1101 4 : ASSERT_JSON_EQ (num, *num_clone);
1102 4 : }
1103 :
1104 : // string
1105 4 : {
1106 4 : string str ("foo");
1107 4 : auto str_clone = str.clone ();
1108 4 : ASSERT_JSON_EQ (str, *str_clone);
1109 4 : }
1110 :
1111 : // literal
1112 4 : {
1113 4 : literal lit (JSON_TRUE);
1114 4 : auto lit_clone = lit.clone ();
1115 4 : ASSERT_JSON_EQ (lit, *lit_clone);
1116 4 : }
1117 4 : }
1118 :
1119 : /* Run all of the selftests within this file. */
1120 :
1121 : void
1122 4 : json_cc_tests ()
1123 : {
1124 4 : test_object_get ();
1125 4 : test_writing_objects ();
1126 4 : test_writing_arrays ();
1127 4 : test_writing_float_numbers ();
1128 4 : test_writing_integer_numbers ();
1129 4 : test_writing_strings ();
1130 4 : test_writing_literals ();
1131 4 : test_formatting ();
1132 4 : test_comparisons ();
1133 4 : test_strcmp ();
1134 4 : test_cloning ();
1135 4 : }
1136 :
1137 : } // namespace selftest
1138 :
1139 : #endif /* #if CHECKING_P */
|