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 1288802 : print_escaped_json_string (pretty_printer *pp,
37 : const char *utf8_str,
38 : size_t len)
39 : {
40 1288802 : pp_character (pp, '"');
41 17693822 : for (size_t i = 0; i != len; ++i)
42 : {
43 16405020 : char ch = utf8_str[i];
44 16405020 : 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 50377 : case '\n':
59 50377 : pp_string (pp, "\\n");
60 50377 : break;
61 8 : case '\r':
62 8 : pp_string (pp, "\\r");
63 8 : break;
64 2800 : case '\t':
65 2800 : pp_string (pp, "\\t");
66 2800 : break;
67 4 : case '\0':
68 4 : pp_string (pp, "\\0");
69 4 : break;
70 16347188 : default:
71 16347188 : pp_character (pp, ch);
72 : }
73 : }
74 1288802 : pp_character (pp, '"');
75 1288802 : }
76 :
77 : /* class pointer::token. */
78 :
79 4145917 : pointer::token::token ()
80 : {
81 4145917 : m_parent = nullptr;
82 4145917 : m_data.u_member = nullptr;
83 4145917 : m_kind = kind::root_value;
84 4145917 : }
85 :
86 3342122 : pointer::token::token (json::object &parent, const char *member)
87 : {
88 3342122 : m_parent = &parent;
89 3342122 : m_data.u_member = xstrdup (member); // ideally we'd share
90 3342122 : m_kind = kind::object_member;
91 3342122 : }
92 :
93 797821 : pointer::token::token (json::array &parent, size_t index)
94 : {
95 797821 : m_parent = &parent;
96 797821 : m_data.u_index = index;
97 797821 : m_kind = kind::array_index;
98 797821 : }
99 :
100 8284484 : pointer::token::~token ()
101 : {
102 8284484 : if (m_kind == kind::object_member)
103 : {
104 3340968 : gcc_assert (m_data.u_member);
105 3340968 : free (m_data.u_member);
106 : }
107 8284484 : }
108 :
109 : pointer::token &
110 4139943 : pointer::token::operator= (pointer::token &&other)
111 : {
112 4139943 : m_parent = other.m_parent;
113 4139943 : m_data = other.m_data;
114 4139943 : m_kind = other.m_kind;
115 :
116 4139943 : other.m_parent = nullptr;
117 4139943 : other.m_data.u_member = nullptr;
118 4139943 : other.m_kind = kind::root_value;
119 :
120 4139943 : return *this;
121 : }
122 :
123 : /* Print this to PP as an RFC 6901 section 3 reference-token. */
124 :
125 : void
126 92 : pointer::token::print (pretty_printer *pp) const
127 : {
128 92 : 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 92 : }
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 60 : value::print_pointer (pretty_printer *pp) const
277 : {
278 : /* Get path from this value to root. */
279 60 : auto_vec<const pointer::token *> ancestry;
280 152 : for (auto *iter = this; iter; iter = iter->m_pointer_token.m_parent)
281 92 : ancestry.safe_push (&iter->m_pointer_token);
282 :
283 : /* Walk backward, going from root to this value. */
284 60 : ancestry.reverse ();
285 60 : bool first = true;
286 272 : for (auto iter : ancestry)
287 : {
288 92 : if (first)
289 : first = false;
290 : else
291 32 : pp_character (pp, '/');
292 92 : iter->print (pp);
293 : }
294 60 : }
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 1497420 : object::~object ()
302 : {
303 10780496 : for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
304 : {
305 3340890 : free (const_cast <char *>((*it).first));
306 3340890 : delete ((*it).second);
307 : }
308 1497420 : }
309 :
310 : /* Implementation of json::value::print for json::object. */
311 :
312 : void
313 246070 : object::print (pretty_printer *pp, bool formatted) const
314 : {
315 246070 : pp_character (pp, '{');
316 246070 : 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 1043786 : FOR_EACH_VEC_ELT (m_keys, i, key)
323 : {
324 797716 : if (i > 0)
325 : {
326 551654 : pp_string (pp, ",");
327 551654 : if (formatted)
328 : {
329 14613 : pp_newline (pp);
330 14613 : pp_indent (pp);
331 : }
332 : else
333 537041 : pp_space (pp);
334 : }
335 797716 : map_t &mut_map = const_cast<map_t &> (m_map);
336 797716 : value *value = *mut_map.get (key);
337 797716 : print_escaped_json_string (pp, key, strlen (key));
338 797716 : pp_string (pp, ": ");
339 797716 : const int indent = strlen (key) + 4;
340 797716 : if (formatted)
341 25157 : pp_indentation (pp) += indent;
342 797716 : value->print (pp, formatted);
343 797716 : if (formatted)
344 25157 : pp_indentation (pp) -= indent;
345 : }
346 246070 : if (formatted)
347 10552 : pp_indentation (pp) -= 1;
348 246070 : pp_character (pp, '}');
349 246070 : }
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 3342122 : object::set (const char *key, value *v)
380 : {
381 3342122 : gcc_assert (key);
382 3342122 : gcc_assert (v);
383 :
384 3342122 : value **ptr = m_map.get (key);
385 3342122 : 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 3342044 : char *owned_key = xstrdup (key);
397 3342044 : m_map.put (owned_key, v);
398 3342044 : m_keys.safe_push (owned_key);
399 : }
400 :
401 3342122 : v->m_pointer_token = pointer::token (*this, key);
402 3342122 : }
403 :
404 : /* Get the json::value * for KEY.
405 :
406 : The object retains ownership of the value. */
407 :
408 : value *
409 25840 : object::get (const char *key) const
410 : {
411 25840 : gcc_assert (key);
412 :
413 25840 : value **ptr = const_cast <map_t &> (m_map).get (key);
414 25840 : if (ptr)
415 23277 : 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 : Return a borrowed ptr to the new json::string. */
423 :
424 : const json::string *
425 1354030 : object::set_string (const char *key, const char *utf8_value)
426 : {
427 1354030 : json::string *str = new json::string (utf8_value);
428 1354030 : set (key, str);
429 1354030 : return str;
430 : }
431 :
432 : /* Set value of KEY within this object to a JSON
433 : integer value based on V.
434 : Return a borrowed ptr to the new json::integer_number. */
435 :
436 : const json::integer_number *
437 677110 : object::set_integer (const char *key, long v)
438 : {
439 677110 : json::integer_number *js_int = new json::integer_number (v);
440 677110 : set (key, js_int);
441 677110 : return js_int;
442 : }
443 :
444 : /* Set value of KEY within this object to a JSON
445 : floating point value based on V. */
446 :
447 : void
448 16 : object::set_float (const char *key, double v)
449 : {
450 16 : set (key, new json::float_number (v));
451 16 : }
452 :
453 : /* Set value of KEY within this object to the JSON
454 : literal true or false, based on V. */
455 :
456 : void
457 489 : object::set_bool (const char *key, bool v)
458 : {
459 978 : set (key, new json::literal (v));
460 489 : }
461 :
462 : void
463 180 : object::set_string (const string_property &property, const char *utf8_value)
464 : {
465 180 : set_string (property.m_key.get (), utf8_value);
466 180 : }
467 :
468 : void
469 240 : object::set_integer (const integer_property &property, long value)
470 : {
471 240 : set_integer (property.m_key.get (), value);
472 240 : }
473 :
474 : void
475 0 : object::set_bool (const bool_property &property, bool value)
476 : {
477 0 : set_bool (property.m_key.get (), value);
478 0 : }
479 :
480 : void
481 780 : object::set_array_of_string (const array_of_string_property &property,
482 : std::unique_ptr<json::array> value)
483 : {
484 780 : set<array> (property.m_key.get (), std::move (value));
485 780 : }
486 :
487 : /* Subroutine of json::compare for comparing a pairs of objects. */
488 :
489 : int
490 5402 : object::compare (const json::object &obj_a, const json::object &obj_b)
491 : {
492 16130 : if (int cmp_size = (int)obj_a.m_keys.length () - (int)obj_b.m_keys.length ())
493 : return cmp_size;
494 :
495 18171 : for (auto iter_a : obj_a.m_map)
496 : {
497 11108 : const char *key = iter_a.first;
498 11108 : const value *value_a = iter_a.second;
499 11108 : gcc_assert (value_a);
500 :
501 11108 : const value *value_b = obj_b.get (key);
502 11108 : if (!value_b)
503 : /* Key is in OBJ_A but not in OBJ_B. */
504 4623 : return 1;
505 : /* If key is OBJ_B but not in OBJ_A, then the
506 : count of keys will have been different, or
507 : OBJ_A would have had a key not in OBJ_B. */
508 11108 : if (int cmp_value = value::compare (*value_a, *value_b))
509 : /* Values for key are non-equal. */
510 : return cmp_value;
511 : }
512 :
513 : /* Objects are equal. */
514 578 : return 0;
515 : }
516 :
517 : /* class json::array, a subclass of json::value, representing
518 : an ordered collection of values. */
519 :
520 : /* json::array's dtor. */
521 :
522 1247136 : array::~array ()
523 : {
524 624015 : unsigned i;
525 624015 : value *v;
526 1421757 : FOR_EACH_VEC_ELT (m_elements, i, v)
527 797742 : delete v;
528 1247136 : }
529 :
530 : /* Implementation of json::value::print for json::array. */
531 :
532 : void
533 92958 : array::print (pretty_printer *pp, bool formatted) const
534 : {
535 92958 : pp_character (pp, '[');
536 92958 : if (formatted)
537 3019 : pp_indentation (pp) += 1;
538 : unsigned i;
539 : value *v;
540 298317 : FOR_EACH_VEC_ELT (m_elements, i, v)
541 : {
542 205359 : if (i)
543 : {
544 127480 : pp_string (pp, ",");
545 127480 : if (formatted)
546 : {
547 6668 : pp_newline (pp);
548 6668 : pp_indent (pp);
549 : }
550 : else
551 120812 : pp_space (pp);
552 : }
553 205359 : v->print (pp, formatted);
554 : }
555 92958 : if (formatted)
556 3019 : pp_indentation (pp) -= 1;
557 92958 : pp_character (pp, ']');
558 92958 : }
559 :
560 : std::unique_ptr<value>
561 664 : array::clone () const
562 : {
563 664 : auto result = std::make_unique<array> ();
564 664 : unsigned i;
565 664 : value *v;
566 2301 : FOR_EACH_VEC_ELT (m_elements, i, v)
567 973 : result->append (v->clone ());
568 664 : return result;
569 664 : }
570 :
571 : /* Append non-NULL value V to a json::array, taking ownership of V. */
572 :
573 : void
574 797821 : array::append (value *v)
575 : {
576 797821 : gcc_assert (v);
577 1400061 : v->m_pointer_token = pointer::token (*this, m_elements.length ());
578 797821 : m_elements.safe_push (v);
579 797821 : }
580 :
581 : /* Append UTF8_VALUE to this array, returning a borrowed pointer to the
582 : new json::string. */
583 :
584 : const json::string *
585 176935 : array::append_string (const char *utf8_value)
586 : {
587 176935 : gcc_assert (utf8_value);
588 176935 : auto js_str = new json::string (utf8_value);
589 176935 : append (js_str);
590 176935 : return js_str;
591 : }
592 :
593 : /* class json::float_number, a subclass of json::value, wrapping a double. */
594 :
595 : /* Implementation of json::value::print for json::float_number. */
596 :
597 : void
598 32 : float_number::print (pretty_printer *pp,
599 : bool formatted ATTRIBUTE_UNUSED) const
600 : {
601 32 : char tmp[1024];
602 32 : snprintf (tmp, sizeof (tmp), "%g", m_value);
603 32 : pp_string (pp, tmp);
604 32 : }
605 :
606 : std::unique_ptr<value>
607 4 : float_number::clone () const
608 : {
609 4 : return std::make_unique<float_number> (m_value);
610 : }
611 :
612 : /* class json::integer_number, a subclass of json::value, wrapping a long. */
613 :
614 : /* Implementation of json::value::print for json::integer_number. */
615 :
616 : void
617 173077 : integer_number::print (pretty_printer *pp,
618 : bool formatted ATTRIBUTE_UNUSED) const
619 : {
620 173077 : char tmp[1024];
621 173077 : snprintf (tmp, sizeof (tmp), "%ld", m_value);
622 173077 : pp_string (pp, tmp);
623 173077 : }
624 :
625 : std::unique_ptr<value>
626 217 : integer_number::clone () const
627 : {
628 217 : return std::make_unique<integer_number> (m_value);
629 : }
630 :
631 : /* class json::string, a subclass of json::value. */
632 :
633 : /* json::string's ctor. */
634 :
635 2082012 : string::string (const char *utf8)
636 : {
637 2082012 : gcc_assert (utf8);
638 2082012 : m_utf8 = xstrdup (utf8);
639 2082012 : m_len = strlen (utf8);
640 2082012 : }
641 :
642 3434 : string::string (const char *utf8, size_t len)
643 : {
644 3434 : gcc_assert (utf8);
645 3434 : m_utf8 = XNEWVEC (char, len + 1);
646 3434 : m_len = len;
647 3434 : memcpy (m_utf8, utf8, len);
648 3434 : m_utf8[len] = '\0';
649 3434 : }
650 :
651 : /* Implementation of json::value::print for json::string. */
652 :
653 : void
654 491086 : string::print (pretty_printer *pp,
655 : bool formatted ATTRIBUTE_UNUSED) const
656 : {
657 491086 : print_escaped_json_string (pp, m_utf8, m_len);
658 491086 : }
659 :
660 : std::unique_ptr<value>
661 3197 : string::clone () const
662 : {
663 3197 : return std::make_unique<string> (m_utf8, m_len);
664 : }
665 :
666 : /* class json::literal, a subclass of json::value. */
667 :
668 : /* Implementation of json::value::print for json::literal. */
669 :
670 : void
671 225 : literal::print (pretty_printer *pp,
672 : bool formatted ATTRIBUTE_UNUSED) const
673 : {
674 225 : switch (m_kind)
675 : {
676 125 : case JSON_TRUE:
677 125 : pp_string (pp, "true");
678 125 : break;
679 92 : case JSON_FALSE:
680 92 : pp_string (pp, "false");
681 92 : break;
682 8 : case JSON_NULL:
683 8 : pp_string (pp, "null");
684 8 : break;
685 0 : default:
686 0 : gcc_unreachable ();
687 : }
688 225 : }
689 :
690 : std::unique_ptr<value>
691 4 : literal::clone () const
692 : {
693 4 : return std::make_unique<literal> (m_kind);
694 : }
695 :
696 :
697 : #if CHECKING_P
698 :
699 : namespace selftest {
700 :
701 : /* Selftests. */
702 :
703 : /* Verify that JV->print () prints EXPECTED_JSON. */
704 :
705 : void
706 160 : assert_print_eq (const location &loc,
707 : const json::value &jv,
708 : bool formatted,
709 : const char *expected_json)
710 : {
711 160 : pretty_printer pp;
712 160 : jv.print (&pp, formatted);
713 160 : ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
714 160 : }
715 :
716 : #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
717 : assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
718 :
719 : /* Verify that object::get works as expected. */
720 :
721 : static void
722 4 : test_object_get ()
723 : {
724 4 : object obj;
725 4 : value *val = new json::string ("value");
726 4 : obj.set ("foo", val);
727 4 : ASSERT_EQ (obj.get ("foo"), val);
728 4 : ASSERT_EQ (obj.get ("not-present"), NULL);
729 4 : }
730 :
731 : /* Verify that JSON objects are written correctly. */
732 :
733 : static void
734 4 : test_writing_objects ()
735 : {
736 4 : object obj;
737 4 : obj.set_string ("foo", "bar");
738 4 : obj.set_string ("baz", "quux");
739 4 : obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
740 :
741 : /* This test relies on json::object writing out key/value pairs
742 : in key-insertion order. */
743 4 : ASSERT_PRINT_EQ (obj, true,
744 : "{\"foo\": \"bar\",\n"
745 : " \"baz\": \"quux\",\n"
746 : " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
747 4 : ASSERT_PRINT_EQ (obj, false,
748 : "{\"foo\": \"bar\", \"baz\": \"quux\""
749 : ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
750 4 : }
751 :
752 : /* Verify that JSON arrays are written correctly. */
753 :
754 : static void
755 4 : test_writing_arrays ()
756 : {
757 4 : array arr;
758 4 : ASSERT_PRINT_EQ (arr, true, "[]");
759 :
760 4 : arr.append (new json::string ("foo"));
761 4 : ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
762 :
763 4 : arr.append_string ("bar");
764 4 : ASSERT_PRINT_EQ (arr, true,
765 : "[\"foo\",\n"
766 : " \"bar\"]");
767 4 : ASSERT_PRINT_EQ (arr, false,
768 : "[\"foo\", \"bar\"]");
769 4 : }
770 :
771 : /* Verify that JSON numbers are written correctly. */
772 :
773 : static void
774 4 : test_writing_float_numbers ()
775 : {
776 4 : ASSERT_PRINT_EQ (float_number (0), true, "0");
777 4 : ASSERT_PRINT_EQ (float_number (42), true, "42");
778 4 : ASSERT_PRINT_EQ (float_number (-100), true, "-100");
779 4 : ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
780 4 : }
781 :
782 : static void
783 4 : test_writing_integer_numbers ()
784 : {
785 4 : ASSERT_PRINT_EQ (integer_number (0), true, "0");
786 4 : ASSERT_PRINT_EQ (integer_number (42), true, "42");
787 4 : ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
788 4 : ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
789 4 : ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
790 4 : }
791 :
792 : /* Verify that JSON strings are written correctly. */
793 :
794 : static void
795 4 : test_writing_strings ()
796 : {
797 4 : string foo ("foo");
798 4 : ASSERT_PRINT_EQ (foo, true, "\"foo\"");
799 :
800 4 : string contains_quotes ("before \"quoted\" after");
801 4 : ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
802 :
803 4 : const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
804 4 : string not_terminated (data, 3);
805 4 : ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
806 4 : string embedded_null (data, sizeof data);
807 4 : ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
808 4 : }
809 :
810 : /* Verify that JSON literals are written correctly. */
811 :
812 : static void
813 4 : test_writing_literals ()
814 : {
815 4 : ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
816 4 : ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
817 4 : ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
818 :
819 4 : ASSERT_PRINT_EQ (literal (true), true, "true");
820 4 : ASSERT_PRINT_EQ (literal (false), true, "false");
821 4 : }
822 :
823 : /* Verify that nested values are formatted correctly when written.
824 :
825 : Also, make use of array::append(std::unique_ptr<value>) and
826 : object::set (const char *key, std::unique_ptr<value> v).*/
827 :
828 : static void
829 4 : test_formatting ()
830 : {
831 4 : object obj;
832 4 : object *child = new object;
833 4 : std::unique_ptr<object> grandchild = std::make_unique<object> ();
834 :
835 4 : obj.set_string ("str", "bar");
836 4 : obj.set ("child", child);
837 4 : obj.set_integer ("int", 42);
838 :
839 4 : array *arr = new array;
840 16 : for (int i = 0; i < 3; i++)
841 12 : arr->append (std::make_unique<integer_number> (i));
842 4 : grandchild->set ("arr", arr);
843 4 : grandchild->set_integer ("int", 1066);
844 :
845 4 : child->set ("grandchild", std::move (grandchild));
846 4 : child->set_integer ("int", 1776);
847 :
848 : /* This test relies on json::object writing out key/value pairs
849 : in key-insertion order. */
850 4 : ASSERT_PRINT_EQ (obj, true,
851 : ("{\"str\": \"bar\",\n"
852 : " \"child\": {\"grandchild\": {\"arr\": [0,\n"
853 : " 1,\n"
854 : " 2],\n"
855 : " \"int\": 1066},\n"
856 : " \"int\": 1776},\n"
857 : " \"int\": 42}"));
858 4 : ASSERT_PRINT_EQ (obj, false,
859 : ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
860 : " {\"arr\": [0, 1, 2], \"int\": 1066},"
861 : " \"int\": 1776}, \"int\": 42}"));
862 4 : }
863 :
864 : /* Helper function for reporting failure of JSON comparisons. */
865 :
866 : static void
867 0 : fail_comparison (const location &loc,
868 : const char *desc,
869 : const value &val_a, const value &val_b,
870 : const char *desc_expected_value,
871 : int actual_value)
872 : {
873 0 : fprintf (stderr, "val_a: ");
874 0 : val_a.dump ();
875 :
876 0 : fprintf (stderr, "val_b: ");
877 0 : val_b.dump ();
878 :
879 0 : selftest::fail_formatted (loc,
880 : "%s: failed JSON comparison:"
881 : " expected: %s got: %i\n",
882 : desc,
883 : desc_expected_value, actual_value);
884 : }
885 :
886 : /* Implementation of ASSERT_JSON_EQ. */
887 :
888 : static void
889 100 : assert_json_equal (const location &loc,
890 : const char *desc,
891 : const value &val_a, const value &val_b)
892 : {
893 : /* Comparison should return zero, both ways, indicating no differences. */
894 100 : const int a_vs_b = value::compare (val_a, val_b);
895 100 : if (a_vs_b != 0)
896 0 : fail_comparison (loc, desc, val_a, val_b, "zero", a_vs_b);
897 :
898 100 : const int b_vs_a = value::compare (val_b, val_a);
899 100 : if (b_vs_a != 0)
900 0 : fail_comparison (loc, desc, val_b, val_a, "zero", b_vs_a);
901 100 : }
902 :
903 : /* Verify that json::value::compare returns 0 ("no differences") on
904 : VAL1 and VAL2, in both orders. */
905 :
906 : #define ASSERT_JSON_EQ(VAL1, VAL2) \
907 : SELFTEST_BEGIN_STMT \
908 : assert_json_equal ((SELFTEST_LOCATION), \
909 : "ASSERT_JSON_EQ", \
910 : (VAL1), (VAL2)); \
911 : SELFTEST_END_STMT
912 :
913 : /* Implementation of ASSERT_JSON_NE. */
914 :
915 : static void
916 48 : assert_json_non_equal (const location &loc,
917 : const char *desc,
918 : const value &val_a, const value &val_b)
919 : {
920 : /* Comparison should be non-zero, indicating differences. */
921 48 : const int a_vs_b = value::compare (val_a, val_b);
922 48 : if (a_vs_b == 0)
923 0 : fail_comparison (loc, desc, val_a, val_b, "non-zero", a_vs_b);
924 :
925 48 : const int b_vs_a = value::compare (val_b, val_a);
926 48 : ASSERT_NE_AT (loc, b_vs_a, 0);
927 48 : if (b_vs_a == 0)
928 : fail_comparison (loc, desc, val_b, val_a, "non-zero", b_vs_a);
929 :
930 : /* Swapping the args should swap the sign of the result
931 : (but isn't necessarily the negation). */
932 48 : if ( (a_vs_b > 0) == (b_vs_a > 0) )
933 0 : fail_comparison (loc, desc, val_b, val_a, "opposite signs", 1);
934 48 : }
935 :
936 : /* Verify that json::value::compare returns non-zero ("different") on
937 : VAL1 and VAL2, in both orders, and that they have opposite
938 : sign. */
939 :
940 : #define ASSERT_JSON_NE(VAL1, VAL2) \
941 : SELFTEST_BEGIN_STMT \
942 : assert_json_non_equal ((SELFTEST_LOCATION), \
943 : "ASSERT_JSON_NE", \
944 : (VAL1), (VAL2)); \
945 : SELFTEST_END_STMT
946 :
947 : /* Verify that json::value::compare works as expected. */
948 :
949 : static void
950 4 : test_comparisons ()
951 : {
952 : /* Literals. */
953 :
954 4 : literal null_lit (JSON_NULL);
955 4 : ASSERT_JSON_EQ (null_lit, null_lit);
956 :
957 4 : literal other_null_lit (JSON_NULL);
958 4 : ASSERT_JSON_EQ (null_lit, other_null_lit);
959 :
960 4 : literal true_lit (JSON_TRUE);
961 4 : ASSERT_JSON_EQ (true_lit, true_lit);
962 4 : ASSERT_JSON_NE (true_lit, null_lit);
963 :
964 4 : literal false_lit (JSON_FALSE);
965 4 : ASSERT_JSON_EQ (false_lit, false_lit);
966 4 : ASSERT_JSON_NE (false_lit, true_lit);
967 4 : ASSERT_JSON_NE (false_lit, null_lit);
968 :
969 : /* Strings. */
970 4 : string str_foo_1 ("foo");
971 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_1);
972 :
973 4 : string str_foo_2 ("foo");
974 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_2);
975 :
976 4 : string str_bar ("bar");
977 4 : ASSERT_JSON_NE (str_bar, str_foo_1);
978 :
979 : /* Numbers. */
980 4 : integer_number i_42 (42);
981 4 : ASSERT_JSON_EQ (i_42, i_42);
982 4 : integer_number i_42_2 (42);
983 4 : ASSERT_JSON_EQ (i_42, i_42_2);
984 4 : integer_number i_43 (43);
985 4 : ASSERT_JSON_NE (i_42, i_43);
986 :
987 4 : float_number f_zero (0.0);
988 4 : ASSERT_JSON_EQ (f_zero, f_zero);
989 4 : float_number f_zero_2 (0.0);
990 4 : ASSERT_JSON_EQ (f_zero, f_zero_2);
991 4 : float_number f_one (1.0);
992 4 : ASSERT_JSON_NE (f_zero, f_one);
993 : /* We don't yet test the more awkward cases e.g. NaN. */
994 :
995 : /* Objects. */
996 :
997 : // Empty object
998 : // Self comparison should be 0
999 4 : object empty_obj_a;
1000 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_a);
1001 :
1002 : // Instances of empty objects should compare equal to each other
1003 4 : object empty_obj_b;
1004 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_b);
1005 :
1006 : // Object with one field:
1007 4 : object obj_1;
1008 4 : obj_1.set_string ("foo", "bar");
1009 : // Self comparison should be 0
1010 4 : ASSERT_JSON_EQ (obj_1, obj_1);
1011 :
1012 : // but should be different to an empty object:
1013 4 : ASSERT_JSON_NE (obj_1, empty_obj_a);
1014 :
1015 : // Another with one field, with same key/value:
1016 4 : object obj_2;
1017 4 : obj_2.set_string ("foo", "bar");
1018 4 : ASSERT_JSON_EQ (obj_1, obj_2);
1019 :
1020 : // Same key, different value:
1021 4 : object obj_3;
1022 4 : obj_3.set_string ("foo", "baz");
1023 4 : ASSERT_JSON_NE (obj_1, obj_3);
1024 :
1025 : // Adding an extra property:
1026 4 : obj_2.set_integer ("year", 1066);
1027 4 : ASSERT_JSON_NE (obj_1, obj_2);
1028 :
1029 : /* Different insertion order, but same k-v pairs should be equal,
1030 : despite having different serialization. */
1031 4 : object obj_4;
1032 4 : obj_4.set_integer ("year", 1066);
1033 4 : obj_4.set_string ("foo", "bar");
1034 4 : ASSERT_JSON_EQ (obj_2, obj_4);
1035 4 : ASSERT_PRINT_EQ (obj_2, false, "{\"foo\": \"bar\", \"year\": 1066}");
1036 4 : ASSERT_PRINT_EQ (obj_4, false, "{\"year\": 1066, \"foo\": \"bar\"}");
1037 :
1038 : /* Arrays. */
1039 :
1040 : // Empty array
1041 4 : array empty_arr_a;
1042 : // Self comparison should be 0
1043 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_a);
1044 :
1045 : // Objects and arrays are different
1046 4 : ASSERT_JSON_NE (empty_obj_a, empty_arr_a);
1047 :
1048 : // Instances of empty arrays should compare equal to each other
1049 4 : array empty_arr_b;
1050 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_b);
1051 :
1052 : // Array with one element:
1053 4 : array arr_1;
1054 4 : arr_1.append (std::make_unique<string> ("foo"));
1055 : // Self comparison should be 0
1056 4 : ASSERT_JSON_EQ (arr_1, arr_1);
1057 :
1058 : // but should be different to an empty array:
1059 4 : ASSERT_JSON_NE (arr_1, empty_arr_a);
1060 :
1061 : // Another with one element:
1062 4 : array arr_2;
1063 4 : arr_2.append (std::make_unique<string> ("foo"));
1064 4 : ASSERT_JSON_EQ (arr_1, arr_2);
1065 :
1066 : // Adding an extra element:
1067 4 : arr_2.append (std::make_unique<string> ("bar"));
1068 4 : ASSERT_JSON_NE (arr_1, arr_2);
1069 4 : }
1070 :
1071 : /* Ensure that json::string's get_string is usable as a C-style string. */
1072 :
1073 : static void
1074 4 : test_strcmp ()
1075 : {
1076 4 : string str ("foobar", 3);
1077 4 : ASSERT_EQ (strcmp (str.get_string (), "foo"), 0);
1078 4 : }
1079 :
1080 : static void
1081 4 : test_cloning ()
1082 : {
1083 : // Objects
1084 4 : {
1085 4 : object obj;
1086 4 : obj.set_string ("foo", "bar");
1087 :
1088 4 : auto obj_clone = obj.clone ();
1089 4 : ASSERT_JSON_EQ (obj, *obj_clone);
1090 4 : }
1091 :
1092 : // Arrays
1093 4 : {
1094 4 : array arr;
1095 4 : arr.append (std::make_unique<string> ("foo"));
1096 :
1097 4 : auto arr_clone = arr.clone ();
1098 4 : ASSERT_JSON_EQ (arr, *arr_clone);
1099 4 : }
1100 :
1101 : // float_number
1102 4 : {
1103 4 : float_number f_one (1.0);
1104 4 : auto f_clone = f_one.clone ();
1105 4 : ASSERT_JSON_EQ (f_one, *f_clone);
1106 4 : }
1107 :
1108 : // integer_number
1109 4 : {
1110 4 : integer_number num (42);
1111 4 : auto num_clone = num.clone ();
1112 4 : ASSERT_JSON_EQ (num, *num_clone);
1113 4 : }
1114 :
1115 : // string
1116 4 : {
1117 4 : string str ("foo");
1118 4 : auto str_clone = str.clone ();
1119 4 : ASSERT_JSON_EQ (str, *str_clone);
1120 4 : }
1121 :
1122 : // literal
1123 4 : {
1124 4 : literal lit (JSON_TRUE);
1125 4 : auto lit_clone = lit.clone ();
1126 4 : ASSERT_JSON_EQ (lit, *lit_clone);
1127 4 : }
1128 4 : }
1129 :
1130 : /* Run all of the selftests within this file. */
1131 :
1132 : void
1133 4 : json_cc_tests ()
1134 : {
1135 4 : test_object_get ();
1136 4 : test_writing_objects ();
1137 4 : test_writing_arrays ();
1138 4 : test_writing_float_numbers ();
1139 4 : test_writing_integer_numbers ();
1140 4 : test_writing_strings ();
1141 4 : test_writing_literals ();
1142 4 : test_formatting ();
1143 4 : test_comparisons ();
1144 4 : test_strcmp ();
1145 4 : test_cloning ();
1146 4 : }
1147 :
1148 : } // namespace selftest
1149 :
1150 : #endif /* #if CHECKING_P */
|