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