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 : 1379842 : print_escaped_json_string (pretty_printer *pp,
37 : : const char *utf8_str,
38 : : size_t len)
39 : : {
40 : 1379842 : pp_character (pp, '"');
41 : 18726896 : for (size_t i = 0; i != len; ++i)
42 : : {
43 : 17347054 : char ch = utf8_str[i];
44 : 17347054 : switch (ch)
45 : : {
46 : 2552 : case '"':
47 : 2552 : pp_string (pp, "\\\"");
48 : 2552 : 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 : 55142 : case '\n':
59 : 55142 : pp_string (pp, "\\n");
60 : 55142 : break;
61 : 8 : case '\r':
62 : 8 : pp_string (pp, "\\r");
63 : 8 : break;
64 : 3694 : case '\t':
65 : 3694 : pp_string (pp, "\\t");
66 : 3694 : break;
67 : 4 : case '\0':
68 : 4 : pp_string (pp, "\\0");
69 : 4 : break;
70 : 17284155 : default:
71 : 17284155 : pp_character (pp, ch);
72 : : }
73 : : }
74 : 1379842 : pp_character (pp, '"');
75 : 1379842 : }
76 : :
77 : : /* class pointer::token. */
78 : :
79 : 4172350 : pointer::token::token ()
80 : : {
81 : 4172350 : m_parent = nullptr;
82 : 4172350 : m_data.u_member = nullptr;
83 : 4172350 : m_kind = kind::root_value;
84 : 4172350 : }
85 : :
86 : 3362290 : pointer::token::token (json::object &parent, const char *member)
87 : : {
88 : 3362290 : m_parent = &parent;
89 : 3362290 : m_data.u_member = xstrdup (member); // ideally we'd share
90 : 3362290 : m_kind = kind::object_member;
91 : 3362290 : }
92 : :
93 : 806567 : pointer::token::token (json::array &parent, size_t index)
94 : : {
95 : 806567 : m_parent = &parent;
96 : 806567 : m_data.u_index = index;
97 : 806567 : m_kind = kind::array_index;
98 : 806567 : }
99 : :
100 : 8339855 : pointer::token::~token ()
101 : : {
102 : 8339855 : if (m_kind == kind::object_member)
103 : : {
104 : 3361156 : gcc_assert (m_data.u_member);
105 : 3361156 : free (m_data.u_member);
106 : : }
107 : 8339855 : }
108 : :
109 : : pointer::token &
110 : 4168857 : pointer::token::operator= (pointer::token &&other)
111 : : {
112 : 4168857 : m_parent = other.m_parent;
113 : 4168857 : m_data = other.m_data;
114 : 4168857 : m_kind = other.m_kind;
115 : :
116 : 4168857 : other.m_parent = nullptr;
117 : 4168857 : other.m_data.u_member = nullptr;
118 : 4168857 : other.m_kind = kind::root_value;
119 : :
120 : 4168857 : 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 : 139 : value::dump (FILE *outf, bool formatted) const
132 : : {
133 : 139 : pretty_printer pp;
134 : 139 : pp_buffer (&pp)->m_stream = outf;
135 : 139 : print (&pp, formatted);
136 : 139 : pp_flush (&pp);
137 : 139 : }
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 : 14624 : value::compare (const value &val_a, const value &val_b)
164 : : {
165 : 14624 : enum kind kind_a = val_a.get_kind ();
166 : 14624 : enum kind kind_b = val_b.get_kind ();
167 : 14624 : if (kind_a != kind_b)
168 : 32 : return (int)kind_a - (int)kind_b;
169 : :
170 : 14592 : switch (kind_a)
171 : : {
172 : 0 : default:
173 : 0 : gcc_unreachable ();
174 : :
175 : 4843 : case JSON_OBJECT:
176 : 4843 : {
177 : 4843 : const object &obj_a = (const object &)val_a;
178 : 4843 : const object &obj_b = (const object &)val_b;
179 : 4843 : return object::compare (obj_a, obj_b);
180 : : }
181 : 48 : break;
182 : :
183 : 48 : case JSON_ARRAY:
184 : 48 : {
185 : 48 : const array &arr_a = (const array &)val_a;
186 : 48 : const array &arr_b = (const array &)val_b;
187 : 104 : if (int cmp_size = (int)arr_a.size () - (int)arr_b.size ())
188 : : return cmp_size;
189 : 48 : for (size_t idx = 0; idx < arr_a.size (); ++idx)
190 : 16 : if (int cmp_element = compare (*arr_a[idx], *arr_b[idx]))
191 : : return cmp_element;
192 : : return 0;
193 : : }
194 : 61 : break;
195 : :
196 : 61 : case JSON_INTEGER:
197 : 61 : {
198 : 61 : const integer_number &int_a = (const integer_number &)val_a;
199 : 61 : const integer_number &int_b = (const integer_number &)val_b;
200 : 61 : return int_a.get () - int_b.get ();
201 : : }
202 : 24 : break;
203 : :
204 : 24 : case JSON_FLOAT:
205 : 24 : {
206 : 24 : const float_number &float_a = (const float_number &)val_a;
207 : 24 : const float_number &float_b = (const float_number &)val_b;
208 : 24 : union u
209 : : {
210 : : double u_double;
211 : : char u_buf[sizeof(double)];
212 : : };
213 : 24 : union u u_a, u_b;
214 : 24 : u_a.u_double = float_a.get ();
215 : 24 : u_b.u_double = float_b.get ();
216 : 24 : return memcmp (&u_a, &u_b, sizeof(double));
217 : : }
218 : 9584 : break;
219 : :
220 : 9584 : case JSON_STRING:
221 : 9584 : {
222 : 9584 : const string &str_a = (const string &)val_a;
223 : 9584 : const string &str_b = (const string &)val_b;
224 : 9584 : 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 : 1519342 : object::~object ()
243 : : {
244 : 7486900 : for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
245 : : {
246 : 3361156 : free (const_cast <char *>((*it).first));
247 : 3361156 : delete ((*it).second);
248 : : }
249 : 1519342 : }
250 : :
251 : : /* Implementation of json::value::print for json::object. */
252 : :
253 : : void
254 : 259811 : object::print (pretty_printer *pp, bool formatted) const
255 : : {
256 : 259811 : pp_character (pp, '{');
257 : 259811 : if (formatted)
258 : 4328 : pp_indentation (pp) += 1;
259 : :
260 : : /* Iterate in the order that the keys were inserted. */
261 : : unsigned i;
262 : : const char *key;
263 : 1107946 : FOR_EACH_VEC_ELT (m_keys, i, key)
264 : : {
265 : 848135 : if (i > 0)
266 : : {
267 : 588332 : pp_string (pp, ",");
268 : 588332 : if (formatted)
269 : : {
270 : 6668 : pp_newline (pp);
271 : 6668 : pp_indent (pp);
272 : : }
273 : : else
274 : 581664 : pp_space (pp);
275 : : }
276 : 848135 : map_t &mut_map = const_cast<map_t &> (m_map);
277 : 848135 : value *value = *mut_map.get (key);
278 : 848135 : print_escaped_json_string (pp, key, strlen (key));
279 : 848135 : pp_string (pp, ": ");
280 : 848135 : const int indent = strlen (key) + 4;
281 : 848135 : if (formatted)
282 : 10988 : pp_indentation (pp) += indent;
283 : 848135 : value->print (pp, formatted);
284 : 848135 : if (formatted)
285 : 10988 : pp_indentation (pp) -= indent;
286 : : }
287 : 259811 : if (formatted)
288 : 4328 : pp_indentation (pp) -= 1;
289 : 259811 : pp_character (pp, '}');
290 : 259811 : }
291 : :
292 : : /* Set the json::value * for KEY, taking ownership of V
293 : : (and taking a copy of KEY if necessary). */
294 : :
295 : : void
296 : 3362290 : object::set (const char *key, value *v)
297 : : {
298 : 3362290 : gcc_assert (key);
299 : 3362290 : gcc_assert (v);
300 : :
301 : 3362290 : value **ptr = m_map.get (key);
302 : 3362290 : if (ptr)
303 : : {
304 : : /* If the key is already present, delete the existing value
305 : : and overwrite it. */
306 : 0 : delete *ptr;
307 : 0 : *ptr = v;
308 : : }
309 : : else
310 : : {
311 : : /* If the key wasn't already present, take a copy of the key,
312 : : and store the value. */
313 : 3362290 : char *owned_key = xstrdup (key);
314 : 3362290 : m_map.put (owned_key, v);
315 : 3362290 : m_keys.safe_push (owned_key);
316 : : }
317 : :
318 : 3362290 : v->m_pointer_token = pointer::token (*this, key);
319 : 3362290 : }
320 : :
321 : : /* Get the json::value * for KEY.
322 : :
323 : : The object retains ownership of the value. */
324 : :
325 : : value *
326 : 15819 : object::get (const char *key) const
327 : : {
328 : 15819 : gcc_assert (key);
329 : :
330 : 15819 : value **ptr = const_cast <map_t &> (m_map).get (key);
331 : 15819 : if (ptr)
332 : 15441 : return *ptr;
333 : : else
334 : : return NULL;
335 : : }
336 : :
337 : : /* Set value of KEY within this object to a JSON
338 : : string value based on UTF8_VALUE. */
339 : :
340 : : void
341 : 1368221 : object::set_string (const char *key, const char *utf8_value)
342 : : {
343 : 1368221 : set (key, new json::string (utf8_value));
344 : 1368221 : }
345 : :
346 : : /* Set value of KEY within this object to a JSON
347 : : integer value based on V. */
348 : :
349 : : void
350 : 679766 : object::set_integer (const char *key, long v)
351 : : {
352 : 679766 : set (key, new json::integer_number (v));
353 : 679766 : }
354 : :
355 : : /* Set value of KEY within this object to a JSON
356 : : floating point value based on V. */
357 : :
358 : : void
359 : 16 : object::set_float (const char *key, double v)
360 : : {
361 : 16 : set (key, new json::float_number (v));
362 : 16 : }
363 : :
364 : : /* Set value of KEY within this object to the JSON
365 : : literal true or false, based on V. */
366 : :
367 : : void
368 : 534 : object::set_bool (const char *key, bool v)
369 : : {
370 : 1068 : set (key, new json::literal (v));
371 : 534 : }
372 : :
373 : : /* Subroutine of json::compare for comparing a pairs of objects. */
374 : :
375 : : int
376 : 4843 : object::compare (const json::object &obj_a, const json::object &obj_b)
377 : : {
378 : 14453 : if (int cmp_size = (int)obj_a.m_keys.length () - (int)obj_b.m_keys.length ())
379 : : return cmp_size;
380 : :
381 : 14800 : for (auto iter_a : obj_a.m_map)
382 : : {
383 : 9541 : const char *key = iter_a.first;
384 : 9541 : const value *value_a = iter_a.second;
385 : 9541 : gcc_assert (value_a);
386 : :
387 : 9541 : const value *value_b = obj_b.get (key);
388 : 9541 : if (!value_b)
389 : : /* Key is in OBJ_A but not in OBJ_B. */
390 : 4492 : return 1;
391 : : /* If key is OBJ_B but not in OBJ_A, then the
392 : : count of keys will have been different, or
393 : : OBJ_A would have had a key not in OBJ_B. */
394 : 9541 : if (int cmp_value = value::compare (*value_a, *value_b))
395 : : /* Values for key are non-equal. */
396 : : return cmp_value;
397 : : }
398 : :
399 : : /* Objects are equal. */
400 : 210 : return 0;
401 : : }
402 : :
403 : : /* class json::array, a subclass of json::value, representing
404 : : an ordered collection of values. */
405 : :
406 : : /* json::array's dtor. */
407 : :
408 : 1241916 : array::~array ()
409 : : {
410 : 621179 : unsigned i;
411 : 621179 : value *v;
412 : 1427667 : FOR_EACH_VEC_ELT (m_elements, i, v)
413 : 806488 : delete v;
414 : 1241916 : }
415 : :
416 : : /* Implementation of json::value::print for json::array. */
417 : :
418 : : void
419 : 96866 : array::print (pretty_printer *pp, bool formatted) const
420 : : {
421 : 96866 : pp_character (pp, '[');
422 : 96866 : if (formatted)
423 : 1481 : pp_indentation (pp) += 1;
424 : : unsigned i;
425 : : value *v;
426 : 317930 : FOR_EACH_VEC_ELT (m_elements, i, v)
427 : : {
428 : 221064 : if (i)
429 : : {
430 : 138857 : pp_string (pp, ",");
431 : 138857 : if (formatted)
432 : : {
433 : 3550 : pp_newline (pp);
434 : 3550 : pp_indent (pp);
435 : : }
436 : : else
437 : 135307 : pp_space (pp);
438 : : }
439 : 221064 : v->print (pp, formatted);
440 : : }
441 : 96866 : if (formatted)
442 : 1481 : pp_indentation (pp) -= 1;
443 : 96866 : pp_character (pp, ']');
444 : 96866 : }
445 : :
446 : : /* Append non-NULL value V to a json::array, taking ownership of V. */
447 : :
448 : : void
449 : 806567 : array::append (value *v)
450 : : {
451 : 806567 : gcc_assert (v);
452 : 1414012 : v->m_pointer_token = pointer::token (*this, m_elements.length ());
453 : 806567 : m_elements.safe_push (v);
454 : 806567 : }
455 : :
456 : : void
457 : 185138 : array::append_string (const char *utf8_value)
458 : : {
459 : 185138 : gcc_assert (utf8_value);
460 : 185138 : append (new json::string (utf8_value));
461 : 185138 : }
462 : :
463 : : /* class json::float_number, a subclass of json::value, wrapping a double. */
464 : :
465 : : /* Implementation of json::value::print for json::float_number. */
466 : :
467 : : void
468 : 32 : float_number::print (pretty_printer *pp,
469 : : bool formatted ATTRIBUTE_UNUSED) const
470 : : {
471 : 32 : char tmp[1024];
472 : 32 : snprintf (tmp, sizeof (tmp), "%g", m_value);
473 : 32 : pp_string (pp, tmp);
474 : 32 : }
475 : :
476 : : /* class json::integer_number, a subclass of json::value, wrapping a long. */
477 : :
478 : : /* Implementation of json::value::print for json::integer_number. */
479 : :
480 : : void
481 : 180864 : integer_number::print (pretty_printer *pp,
482 : : bool formatted ATTRIBUTE_UNUSED) const
483 : : {
484 : 180864 : char tmp[1024];
485 : 180864 : snprintf (tmp, sizeof (tmp), "%ld", m_value);
486 : 180864 : pp_string (pp, tmp);
487 : 180864 : }
488 : :
489 : :
490 : : /* class json::string, a subclass of json::value. */
491 : :
492 : : /* json::string's ctor. */
493 : :
494 : 2105338 : string::string (const char *utf8)
495 : : {
496 : 2105338 : gcc_assert (utf8);
497 : 2105338 : m_utf8 = xstrdup (utf8);
498 : 2105338 : m_len = strlen (utf8);
499 : 2105338 : }
500 : :
501 : 215 : string::string (const char *utf8, size_t len)
502 : : {
503 : 215 : gcc_assert (utf8);
504 : 215 : m_utf8 = XNEWVEC (char, len);
505 : 215 : m_len = len;
506 : 215 : memcpy (m_utf8, utf8, len);
507 : 215 : }
508 : :
509 : : /* Implementation of json::value::print for json::string. */
510 : :
511 : : void
512 : 531707 : string::print (pretty_printer *pp,
513 : : bool formatted ATTRIBUTE_UNUSED) const
514 : : {
515 : 531707 : print_escaped_json_string (pp, m_utf8, m_len);
516 : 531707 : }
517 : :
518 : : /* class json::literal, a subclass of json::value. */
519 : :
520 : : /* Implementation of json::value::print for json::literal. */
521 : :
522 : : void
523 : 265 : literal::print (pretty_printer *pp,
524 : : bool formatted ATTRIBUTE_UNUSED) const
525 : : {
526 : 265 : switch (m_kind)
527 : : {
528 : 110 : case JSON_TRUE:
529 : 110 : pp_string (pp, "true");
530 : 110 : break;
531 : 147 : case JSON_FALSE:
532 : 147 : pp_string (pp, "false");
533 : 147 : break;
534 : 8 : case JSON_NULL:
535 : 8 : pp_string (pp, "null");
536 : 8 : break;
537 : 0 : default:
538 : 0 : gcc_unreachable ();
539 : : }
540 : 265 : }
541 : :
542 : :
543 : : #if CHECKING_P
544 : :
545 : : namespace selftest {
546 : :
547 : : /* Selftests. */
548 : :
549 : : /* Verify that JV->print () prints EXPECTED_JSON. */
550 : :
551 : : void
552 : 160 : assert_print_eq (const location &loc,
553 : : const json::value &jv,
554 : : bool formatted,
555 : : const char *expected_json)
556 : : {
557 : 160 : pretty_printer pp;
558 : 160 : jv.print (&pp, formatted);
559 : 160 : ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
560 : 160 : }
561 : :
562 : : #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
563 : : assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
564 : :
565 : : /* Verify that object::get works as expected. */
566 : :
567 : : static void
568 : 4 : test_object_get ()
569 : : {
570 : 4 : object obj;
571 : 4 : value *val = new json::string ("value");
572 : 4 : obj.set ("foo", val);
573 : 4 : ASSERT_EQ (obj.get ("foo"), val);
574 : 4 : ASSERT_EQ (obj.get ("not-present"), NULL);
575 : 4 : }
576 : :
577 : : /* Verify that JSON objects are written correctly. */
578 : :
579 : : static void
580 : 4 : test_writing_objects ()
581 : : {
582 : 4 : object obj;
583 : 4 : obj.set_string ("foo", "bar");
584 : 4 : obj.set_string ("baz", "quux");
585 : 4 : obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
586 : :
587 : : /* This test relies on json::object writing out key/value pairs
588 : : in key-insertion order. */
589 : 4 : ASSERT_PRINT_EQ (obj, true,
590 : : "{\"foo\": \"bar\",\n"
591 : : " \"baz\": \"quux\",\n"
592 : : " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
593 : 4 : ASSERT_PRINT_EQ (obj, false,
594 : : "{\"foo\": \"bar\", \"baz\": \"quux\""
595 : : ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
596 : 4 : }
597 : :
598 : : /* Verify that JSON arrays are written correctly. */
599 : :
600 : : static void
601 : 4 : test_writing_arrays ()
602 : : {
603 : 4 : array arr;
604 : 4 : ASSERT_PRINT_EQ (arr, true, "[]");
605 : :
606 : 4 : arr.append (new json::string ("foo"));
607 : 4 : ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
608 : :
609 : 4 : arr.append_string ("bar");
610 : 4 : ASSERT_PRINT_EQ (arr, true,
611 : : "[\"foo\",\n"
612 : : " \"bar\"]");
613 : 4 : ASSERT_PRINT_EQ (arr, false,
614 : : "[\"foo\", \"bar\"]");
615 : 4 : }
616 : :
617 : : /* Verify that JSON numbers are written correctly. */
618 : :
619 : : static void
620 : 4 : test_writing_float_numbers ()
621 : : {
622 : 4 : ASSERT_PRINT_EQ (float_number (0), true, "0");
623 : 4 : ASSERT_PRINT_EQ (float_number (42), true, "42");
624 : 4 : ASSERT_PRINT_EQ (float_number (-100), true, "-100");
625 : 4 : ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
626 : 4 : }
627 : :
628 : : static void
629 : 4 : test_writing_integer_numbers ()
630 : : {
631 : 4 : ASSERT_PRINT_EQ (integer_number (0), true, "0");
632 : 4 : ASSERT_PRINT_EQ (integer_number (42), true, "42");
633 : 4 : ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
634 : 4 : ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
635 : 4 : ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
636 : 4 : }
637 : :
638 : : /* Verify that JSON strings are written correctly. */
639 : :
640 : : static void
641 : 4 : test_writing_strings ()
642 : : {
643 : 4 : string foo ("foo");
644 : 4 : ASSERT_PRINT_EQ (foo, true, "\"foo\"");
645 : :
646 : 4 : string contains_quotes ("before \"quoted\" after");
647 : 4 : ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
648 : :
649 : 4 : const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
650 : 4 : string not_terminated (data, 3);
651 : 4 : ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
652 : 4 : string embedded_null (data, sizeof data);
653 : 4 : ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
654 : 4 : }
655 : :
656 : : /* Verify that JSON literals are written correctly. */
657 : :
658 : : static void
659 : 4 : test_writing_literals ()
660 : : {
661 : 4 : ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
662 : 4 : ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
663 : 4 : ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
664 : :
665 : 4 : ASSERT_PRINT_EQ (literal (true), true, "true");
666 : 4 : ASSERT_PRINT_EQ (literal (false), true, "false");
667 : 4 : }
668 : :
669 : : /* Verify that nested values are formatted correctly when written.
670 : :
671 : : Also, make use of array::append(std::unique_ptr<value>) and
672 : : object::set (const char *key, std::unique_ptr<value> v).*/
673 : :
674 : : static void
675 : 4 : test_formatting ()
676 : : {
677 : 4 : object obj;
678 : 4 : object *child = new object;
679 : 4 : std::unique_ptr<object> grandchild = std::make_unique<object> ();
680 : :
681 : 4 : obj.set_string ("str", "bar");
682 : 4 : obj.set ("child", child);
683 : 4 : obj.set_integer ("int", 42);
684 : :
685 : 4 : array *arr = new array;
686 : 16 : for (int i = 0; i < 3; i++)
687 : 12 : arr->append (std::make_unique<integer_number> (i));
688 : 4 : grandchild->set ("arr", arr);
689 : 4 : grandchild->set_integer ("int", 1066);
690 : :
691 : 4 : child->set ("grandchild", std::move (grandchild));
692 : 4 : child->set_integer ("int", 1776);
693 : :
694 : : /* This test relies on json::object writing out key/value pairs
695 : : in key-insertion order. */
696 : 4 : ASSERT_PRINT_EQ (obj, true,
697 : : ("{\"str\": \"bar\",\n"
698 : : " \"child\": {\"grandchild\": {\"arr\": [0,\n"
699 : : " 1,\n"
700 : : " 2],\n"
701 : : " \"int\": 1066},\n"
702 : : " \"int\": 1776},\n"
703 : : " \"int\": 42}"));
704 : 4 : ASSERT_PRINT_EQ (obj, false,
705 : : ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
706 : : " {\"arr\": [0, 1, 2], \"int\": 1066},"
707 : : " \"int\": 1776}, \"int\": 42}"));
708 : 4 : }
709 : :
710 : : /* Helper function for reporting failure of JSON comparisons. */
711 : :
712 : : static void
713 : 0 : fail_comparison (const location &loc,
714 : : const char *desc,
715 : : const value &val_a, const value &val_b,
716 : : const char *desc_expected_value,
717 : : int actual_value)
718 : : {
719 : 0 : fprintf (stderr, "val_a: ");
720 : 0 : val_a.dump ();
721 : :
722 : 0 : fprintf (stderr, "val_b: ");
723 : 0 : val_b.dump ();
724 : :
725 : 0 : selftest::fail_formatted (loc,
726 : : "%s: failed JSON comparison:"
727 : : " expected: %s got: %i\n",
728 : : desc,
729 : : desc_expected_value, actual_value);
730 : : }
731 : :
732 : : /* Implementation of ASSERT_JSON_EQ. */
733 : :
734 : : static void
735 : 76 : assert_json_equal (const location &loc,
736 : : const char *desc,
737 : : const value &val_a, const value &val_b)
738 : : {
739 : : /* Comparison should return zero, both ways, indicating no differences. */
740 : 76 : const int a_vs_b = value::compare (val_a, val_b);
741 : 76 : if (a_vs_b != 0)
742 : 0 : fail_comparison (loc, desc, val_a, val_b, "zero", a_vs_b);
743 : :
744 : 76 : const int b_vs_a = value::compare (val_b, val_a);
745 : 76 : if (b_vs_a != 0)
746 : 0 : fail_comparison (loc, desc, val_b, val_a, "zero", b_vs_a);
747 : 76 : }
748 : :
749 : : /* Verify that json::value::compare returns 0 ("no differences") on
750 : : VAL1 and VAL2, in both orders. */
751 : :
752 : : #define ASSERT_JSON_EQ(VAL1, VAL2) \
753 : : SELFTEST_BEGIN_STMT \
754 : : assert_json_equal ((SELFTEST_LOCATION), \
755 : : "ASSERT_JSON_EQ", \
756 : : (VAL1), (VAL2)); \
757 : : SELFTEST_END_STMT
758 : :
759 : : /* Implementation of ASSERT_JSON_NE. */
760 : :
761 : : static void
762 : 48 : assert_json_non_equal (const location &loc,
763 : : const char *desc,
764 : : const value &val_a, const value &val_b)
765 : : {
766 : : /* Comparison should be non-zero, indicating differences. */
767 : 48 : const int a_vs_b = value::compare (val_a, val_b);
768 : 48 : if (a_vs_b == 0)
769 : 0 : fail_comparison (loc, desc, val_a, val_b, "non-zero", a_vs_b);
770 : :
771 : 48 : const int b_vs_a = value::compare (val_b, val_a);
772 : 48 : ASSERT_NE_AT (loc, b_vs_a, 0);
773 : 48 : if (b_vs_a == 0)
774 : : fail_comparison (loc, desc, val_b, val_a, "non-zero", b_vs_a);
775 : :
776 : : /* Swapping the args should swap the sign of the result
777 : : (but isn't necessarily the negation). */
778 : 48 : if ( (a_vs_b > 0) == (b_vs_a > 0) )
779 : 0 : fail_comparison (loc, desc, val_b, val_a, "opposite signs", 1);
780 : 48 : }
781 : :
782 : : /* Verify that json::value::compare returns non-zero ("different") on
783 : : VAL1 and VAL2, in both orders, and that they have opposite
784 : : sign. */
785 : :
786 : : #define ASSERT_JSON_NE(VAL1, VAL2) \
787 : : SELFTEST_BEGIN_STMT \
788 : : assert_json_non_equal ((SELFTEST_LOCATION), \
789 : : "ASSERT_JSON_NE", \
790 : : (VAL1), (VAL2)); \
791 : : SELFTEST_END_STMT
792 : :
793 : : /* Verify that json::value::compare works as expected. */
794 : :
795 : : static void
796 : 4 : test_comparisons ()
797 : : {
798 : : /* Literals. */
799 : :
800 : 4 : literal null_lit (JSON_NULL);
801 : 4 : ASSERT_JSON_EQ (null_lit, null_lit);
802 : :
803 : 4 : literal other_null_lit (JSON_NULL);
804 : 4 : ASSERT_JSON_EQ (null_lit, other_null_lit);
805 : :
806 : 4 : literal true_lit (JSON_TRUE);
807 : 4 : ASSERT_JSON_EQ (true_lit, true_lit);
808 : 4 : ASSERT_JSON_NE (true_lit, null_lit);
809 : :
810 : 4 : literal false_lit (JSON_FALSE);
811 : 4 : ASSERT_JSON_EQ (false_lit, false_lit);
812 : 4 : ASSERT_JSON_NE (false_lit, true_lit);
813 : 4 : ASSERT_JSON_NE (false_lit, null_lit);
814 : :
815 : : /* Strings. */
816 : 4 : string str_foo_1 ("foo");
817 : 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_1);
818 : :
819 : 4 : string str_foo_2 ("foo");
820 : 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_2);
821 : :
822 : 4 : string str_bar ("bar");
823 : 4 : ASSERT_JSON_NE (str_bar, str_foo_1);
824 : :
825 : : /* Numbers. */
826 : 4 : integer_number i_42 (42);
827 : 4 : ASSERT_JSON_EQ (i_42, i_42);
828 : 4 : integer_number i_42_2 (42);
829 : 4 : ASSERT_JSON_EQ (i_42, i_42_2);
830 : 4 : integer_number i_43 (43);
831 : 4 : ASSERT_JSON_NE (i_42, i_43);
832 : :
833 : 4 : float_number f_zero (0.0);
834 : 4 : ASSERT_JSON_EQ (f_zero, f_zero);
835 : 4 : float_number f_zero_2 (0.0);
836 : 4 : ASSERT_JSON_EQ (f_zero, f_zero_2);
837 : 4 : float_number f_one (1.0);
838 : 4 : ASSERT_JSON_NE (f_zero, f_one);
839 : : /* We don't yet test the more awkward cases e.g. NaN. */
840 : :
841 : : /* Objects. */
842 : :
843 : : // Empty object
844 : : // Self comparison should be 0
845 : 4 : object empty_obj_a;
846 : 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_a);
847 : :
848 : : // Instances of empty objects should compare equal to each other
849 : 4 : object empty_obj_b;
850 : 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_b);
851 : :
852 : : // Object with one field:
853 : 4 : object obj_1;
854 : 4 : obj_1.set_string ("foo", "bar");
855 : : // Self comparison should be 0
856 : 4 : ASSERT_JSON_EQ (obj_1, obj_1);
857 : :
858 : : // but should be different to an empty object:
859 : 4 : ASSERT_JSON_NE (obj_1, empty_obj_a);
860 : :
861 : : // Another with one field, with same key/value:
862 : 4 : object obj_2;
863 : 4 : obj_2.set_string ("foo", "bar");
864 : 4 : ASSERT_JSON_EQ (obj_1, obj_2);
865 : :
866 : : // Same key, different value:
867 : 4 : object obj_3;
868 : 4 : obj_3.set_string ("foo", "baz");
869 : 4 : ASSERT_JSON_NE (obj_1, obj_3);
870 : :
871 : : // Adding an extra property:
872 : 4 : obj_2.set_integer ("year", 1066);
873 : 4 : ASSERT_JSON_NE (obj_1, obj_2);
874 : :
875 : : /* Different insertion order, but same k-v pairs should be equal,
876 : : despite having different serialization. */
877 : 4 : object obj_4;
878 : 4 : obj_4.set_integer ("year", 1066);
879 : 4 : obj_4.set_string ("foo", "bar");
880 : 4 : ASSERT_JSON_EQ (obj_2, obj_4);
881 : 4 : ASSERT_PRINT_EQ (obj_2, false, "{\"foo\": \"bar\", \"year\": 1066}");
882 : 4 : ASSERT_PRINT_EQ (obj_4, false, "{\"year\": 1066, \"foo\": \"bar\"}");
883 : :
884 : : /* Arrays. */
885 : :
886 : : // Empty array
887 : 4 : array empty_arr_a;
888 : : // Self comparison should be 0
889 : 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_a);
890 : :
891 : : // Objects and arrays are different
892 : 4 : ASSERT_JSON_NE (empty_obj_a, empty_arr_a);
893 : :
894 : : // Instances of empty arrays should compare equal to each other
895 : 4 : array empty_arr_b;
896 : 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_b);
897 : :
898 : : // Array with one element:
899 : 4 : array arr_1;
900 : 4 : arr_1.append (std::make_unique<string> ("foo"));
901 : : // Self comparison should be 0
902 : 4 : ASSERT_JSON_EQ (arr_1, arr_1);
903 : :
904 : : // but should be different to an empty array:
905 : 4 : ASSERT_JSON_NE (arr_1, empty_arr_a);
906 : :
907 : : // Another with one element:
908 : 4 : array arr_2;
909 : 4 : arr_2.append (std::make_unique<string> ("foo"));
910 : 4 : ASSERT_JSON_EQ (arr_1, arr_2);
911 : :
912 : : // Adding an extra element:
913 : 4 : arr_2.append (std::make_unique<string> ("bar"));
914 : 4 : ASSERT_JSON_NE (arr_1, arr_2);
915 : 4 : }
916 : :
917 : : /* Run all of the selftests within this file. */
918 : :
919 : : void
920 : 4 : json_cc_tests ()
921 : : {
922 : 4 : test_object_get ();
923 : 4 : test_writing_objects ();
924 : 4 : test_writing_arrays ();
925 : 4 : test_writing_float_numbers ();
926 : 4 : test_writing_integer_numbers ();
927 : 4 : test_writing_strings ();
928 : 4 : test_writing_literals ();
929 : 4 : test_formatting ();
930 : 4 : test_comparisons ();
931 : 4 : }
932 : :
933 : : } // namespace selftest
934 : :
935 : : #endif /* #if CHECKING_P */
|