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 : 1290235 : print_escaped_json_string (pretty_printer *pp,
37 : : const char *utf8_str,
38 : : size_t len)
39 : : {
40 : 1290235 : pp_character (pp, '"');
41 : 17443146 : for (size_t i = 0; i != len; ++i)
42 : : {
43 : 16152911 : char ch = utf8_str[i];
44 : 16152911 : switch (ch)
45 : : {
46 : 2656 : case '"':
47 : 2656 : pp_string (pp, "\\\"");
48 : 2656 : 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 : 51368 : case '\n':
59 : 51368 : pp_string (pp, "\\n");
60 : 51368 : break;
61 : 8 : case '\r':
62 : 8 : pp_string (pp, "\\r");
63 : 8 : break;
64 : 3848 : case '\t':
65 : 3848 : pp_string (pp, "\\t");
66 : 3848 : break;
67 : 4 : case '\0':
68 : 4 : pp_string (pp, "\\0");
69 : 4 : break;
70 : 16093528 : default:
71 : 16093528 : pp_character (pp, ch);
72 : : }
73 : : }
74 : 1290235 : pp_character (pp, '"');
75 : 1290235 : }
76 : :
77 : : /* class pointer::token. */
78 : :
79 : 4120432 : pointer::token::token ()
80 : : {
81 : 4120432 : m_parent = nullptr;
82 : 4120432 : m_data.u_member = nullptr;
83 : 4120432 : m_kind = kind::root_value;
84 : 4120432 : }
85 : :
86 : 3319268 : pointer::token::token (json::object &parent, const char *member)
87 : : {
88 : 3319268 : m_parent = &parent;
89 : 3319268 : m_data.u_member = xstrdup (member); // ideally we'd share
90 : 3319268 : m_kind = kind::object_member;
91 : 3319268 : }
92 : :
93 : 796593 : pointer::token::token (json::array &parent, size_t index)
94 : : {
95 : 796593 : m_parent = &parent;
96 : 796593 : m_data.u_index = index;
97 : 796593 : m_kind = kind::array_index;
98 : 796593 : }
99 : :
100 : 8234941 : pointer::token::~token ()
101 : : {
102 : 8234941 : if (m_kind == kind::object_member)
103 : : {
104 : 3318134 : gcc_assert (m_data.u_member);
105 : 3318134 : free (m_data.u_member);
106 : : }
107 : 8234941 : }
108 : :
109 : : pointer::token &
110 : 4115861 : pointer::token::operator= (pointer::token &&other)
111 : : {
112 : 4115861 : m_parent = other.m_parent;
113 : 4115861 : m_data = other.m_data;
114 : 4115861 : m_kind = other.m_kind;
115 : :
116 : 4115861 : other.m_parent = nullptr;
117 : 4115861 : other.m_data.u_member = nullptr;
118 : 4115861 : other.m_kind = kind::root_value;
119 : :
120 : 4115861 : 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 : 102 : value::dump (FILE *outf, bool formatted) const
132 : : {
133 : 102 : pretty_printer pp;
134 : 102 : pp_buffer (&pp)->m_stream = outf;
135 : 102 : print (&pp, formatted);
136 : 102 : pp_flush (&pp);
137 : 102 : }
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 : 15472 : value::compare (const value &val_a, const value &val_b)
164 : : {
165 : 15472 : enum kind kind_a = val_a.get_kind ();
166 : 15472 : enum kind kind_b = val_b.get_kind ();
167 : 15472 : if (kind_a != kind_b)
168 : 32 : return (int)kind_a - (int)kind_b;
169 : :
170 : 15440 : switch (kind_a)
171 : : {
172 : 0 : default:
173 : 0 : gcc_unreachable ();
174 : :
175 : 5045 : case JSON_OBJECT:
176 : 5045 : {
177 : 5045 : const object &obj_a = (const object &)val_a;
178 : 5045 : const object &obj_b = (const object &)val_b;
179 : 5045 : 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 : 10198 : break;
219 : :
220 : 10198 : case JSON_STRING:
221 : 10198 : {
222 : 10198 : const string &str_a = (const string &)val_a;
223 : 10198 : const string &str_b = (const string &)val_b;
224 : 10198 : 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 : 1487439 : object::~object ()
243 : : {
244 : 7386818 : for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
245 : : {
246 : 3318043 : free (const_cast <char *>((*it).first));
247 : 3318043 : delete ((*it).second);
248 : : }
249 : 1487439 : }
250 : :
251 : : /* Implementation of json::value::print for json::object. */
252 : :
253 : : void
254 : 242825 : object::print (pretty_printer *pp, bool formatted) const
255 : : {
256 : 242825 : pp_character (pp, '{');
257 : 242825 : if (formatted)
258 : 7641 : pp_indentation (pp) += 1;
259 : :
260 : : /* Iterate in the order that the keys were inserted. */
261 : : unsigned i;
262 : : const char *key;
263 : 1033275 : FOR_EACH_VEC_ELT (m_keys, i, key)
264 : : {
265 : 790450 : if (i > 0)
266 : : {
267 : 547633 : pp_string (pp, ",");
268 : 547633 : if (formatted)
269 : : {
270 : 10671 : pp_newline (pp);
271 : 10671 : pp_indent (pp);
272 : : }
273 : : else
274 : 536962 : pp_space (pp);
275 : : }
276 : 790450 : map_t &mut_map = const_cast<map_t &> (m_map);
277 : 790450 : value *value = *mut_map.get (key);
278 : 790450 : print_escaped_json_string (pp, key, strlen (key));
279 : 790450 : pp_string (pp, ": ");
280 : 790450 : const int indent = strlen (key) + 4;
281 : 790450 : if (formatted)
282 : 18304 : pp_indentation (pp) += indent;
283 : 790450 : value->print (pp, formatted);
284 : 790450 : if (formatted)
285 : 18304 : pp_indentation (pp) -= indent;
286 : : }
287 : 242825 : if (formatted)
288 : 7641 : pp_indentation (pp) -= 1;
289 : 242825 : pp_character (pp, '}');
290 : 242825 : }
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 : 3319268 : object::set (const char *key, value *v)
321 : : {
322 : 3319268 : gcc_assert (key);
323 : 3319268 : gcc_assert (v);
324 : :
325 : 3319268 : value **ptr = m_map.get (key);
326 : 3319268 : 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 : 3319177 : char *owned_key = xstrdup (key);
338 : 3319177 : m_map.put (owned_key, v);
339 : 3319177 : m_keys.safe_push (owned_key);
340 : : }
341 : :
342 : 3319268 : v->m_pointer_token = pointer::token (*this, key);
343 : 3319268 : }
344 : :
345 : : /* Get the json::value * for KEY.
346 : :
347 : : The object retains ownership of the value. */
348 : :
349 : : value *
350 : 18999 : object::get (const char *key) const
351 : : {
352 : 18999 : gcc_assert (key);
353 : :
354 : 18999 : value **ptr = const_cast <map_t &> (m_map).get (key);
355 : 18999 : if (ptr)
356 : 17985 : 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 : 1350239 : object::set_string (const char *key, const char *utf8_value)
366 : : {
367 : 1350239 : set (key, new json::string (utf8_value));
368 : 1350239 : }
369 : :
370 : : /* Set value of KEY within this object to a JSON
371 : : integer value based on V. */
372 : :
373 : : void
374 : 667595 : object::set_integer (const char *key, long v)
375 : : {
376 : 667595 : set (key, new json::integer_number (v));
377 : 667595 : }
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 : 477 : object::set_bool (const char *key, bool v)
393 : : {
394 : 954 : set (key, new json::literal (v));
395 : 477 : }
396 : :
397 : : void
398 : 0 : object::set_string (const string_property &property, const char *utf8_value)
399 : : {
400 : 0 : set_string (property.m_key.get (), utf8_value);
401 : 0 : }
402 : :
403 : : void
404 : 0 : object::set_integer (const integer_property &property, long value)
405 : : {
406 : 0 : set_integer (property.m_key.get (), value);
407 : 0 : }
408 : :
409 : : void
410 : 0 : object::set_bool (const bool_property &property, bool value)
411 : : {
412 : 0 : set_bool (property.m_key.get (), value);
413 : 0 : }
414 : :
415 : : void
416 : 0 : object::set_array_of_string (const array_of_string_property &property,
417 : : std::unique_ptr<json::array> value)
418 : : {
419 : 0 : set<array> (property.m_key.get (), std::move (value));
420 : 0 : }
421 : :
422 : : /* Subroutine of json::compare for comparing a pairs of objects. */
423 : :
424 : : int
425 : 5045 : object::compare (const json::object &obj_a, const json::object &obj_b)
426 : : {
427 : 15059 : if (int cmp_size = (int)obj_a.m_keys.length () - (int)obj_b.m_keys.length ())
428 : : return cmp_size;
429 : :
430 : 16074 : for (auto iter_a : obj_a.m_map)
431 : : {
432 : 10139 : const char *key = iter_a.first;
433 : 10139 : const value *value_a = iter_a.second;
434 : 10139 : gcc_assert (value_a);
435 : :
436 : 10139 : const value *value_b = obj_b.get (key);
437 : 10139 : if (!value_b)
438 : : /* Key is in OBJ_A but not in OBJ_B. */
439 : 4554 : return 1;
440 : : /* If key is OBJ_B but not in OBJ_A, then the
441 : : count of keys will have been different, or
442 : : OBJ_A would have had a key not in OBJ_B. */
443 : 10139 : if (int cmp_value = value::compare (*value_a, *value_b))
444 : : /* Values for key are non-equal. */
445 : : return cmp_value;
446 : : }
447 : :
448 : : /* Objects are equal. */
449 : 350 : return 0;
450 : : }
451 : :
452 : : /* class json::array, a subclass of json::value, representing
453 : : an ordered collection of values. */
454 : :
455 : : /* json::array's dtor. */
456 : :
457 : 1234045 : array::~array ()
458 : : {
459 : 617455 : unsigned i;
460 : 617455 : value *v;
461 : 1413969 : FOR_EACH_VEC_ELT (m_elements, i, v)
462 : 796514 : delete v;
463 : 1234045 : }
464 : :
465 : : /* Implementation of json::value::print for json::array. */
466 : :
467 : : void
468 : 90232 : array::print (pretty_printer *pp, bool formatted) const
469 : : {
470 : 90232 : pp_character (pp, '[');
471 : 90232 : if (formatted)
472 : 1907 : pp_indentation (pp) += 1;
473 : : unsigned i;
474 : : value *v;
475 : 298803 : FOR_EACH_VEC_ELT (m_elements, i, v)
476 : : {
477 : 208571 : if (i)
478 : : {
479 : 132960 : pp_string (pp, ",");
480 : 132960 : if (formatted)
481 : : {
482 : 4947 : pp_newline (pp);
483 : 4947 : pp_indent (pp);
484 : : }
485 : : else
486 : 128013 : pp_space (pp);
487 : : }
488 : 208571 : v->print (pp, formatted);
489 : : }
490 : 90232 : if (formatted)
491 : 1907 : pp_indentation (pp) -= 1;
492 : 90232 : pp_character (pp, ']');
493 : 90232 : }
494 : :
495 : : std::unique_ptr<value>
496 : 4 : array::clone () const
497 : : {
498 : 4 : auto result = std::make_unique<array> ();
499 : 4 : unsigned i;
500 : 4 : value *v;
501 : 12 : FOR_EACH_VEC_ELT (m_elements, i, v)
502 : 4 : result->append (v->clone ());
503 : 4 : return result;
504 : 4 : }
505 : :
506 : : /* Append non-NULL value V to a json::array, taking ownership of V. */
507 : :
508 : : void
509 : 796593 : array::append (value *v)
510 : : {
511 : 796593 : gcc_assert (v);
512 : 1400651 : v->m_pointer_token = pointer::token (*this, m_elements.length ());
513 : 796593 : m_elements.safe_push (v);
514 : 796593 : }
515 : :
516 : : void
517 : 180222 : array::append_string (const char *utf8_value)
518 : : {
519 : 180222 : gcc_assert (utf8_value);
520 : 180222 : append (new json::string (utf8_value));
521 : 180222 : }
522 : :
523 : : /* class json::float_number, a subclass of json::value, wrapping a double. */
524 : :
525 : : /* Implementation of json::value::print for json::float_number. */
526 : :
527 : : void
528 : 32 : float_number::print (pretty_printer *pp,
529 : : bool formatted ATTRIBUTE_UNUSED) const
530 : : {
531 : 32 : char tmp[1024];
532 : 32 : snprintf (tmp, sizeof (tmp), "%g", m_value);
533 : 32 : pp_string (pp, tmp);
534 : 32 : }
535 : :
536 : : std::unique_ptr<value>
537 : 4 : float_number::clone () const
538 : : {
539 : 4 : return std::make_unique<float_number> (m_value);
540 : : }
541 : :
542 : : /* class json::integer_number, a subclass of json::value, wrapping a long. */
543 : :
544 : : /* Implementation of json::value::print for json::integer_number. */
545 : :
546 : : void
547 : 166251 : integer_number::print (pretty_printer *pp,
548 : : bool formatted ATTRIBUTE_UNUSED) const
549 : : {
550 : 166251 : char tmp[1024];
551 : 166251 : snprintf (tmp, sizeof (tmp), "%ld", m_value);
552 : 166251 : pp_string (pp, tmp);
553 : 166251 : }
554 : :
555 : : std::unique_ptr<value>
556 : 4 : integer_number::clone () const
557 : : {
558 : 4 : return std::make_unique<integer_number> (m_value);
559 : : }
560 : :
561 : : /* class json::string, a subclass of json::value. */
562 : :
563 : : /* json::string's ctor. */
564 : :
565 : 2081317 : string::string (const char *utf8)
566 : : {
567 : 2081317 : gcc_assert (utf8);
568 : 2081317 : m_utf8 = xstrdup (utf8);
569 : 2081317 : m_len = strlen (utf8);
570 : 2081317 : }
571 : :
572 : 2102 : string::string (const char *utf8, size_t len)
573 : : {
574 : 2102 : gcc_assert (utf8);
575 : 2102 : m_utf8 = XNEWVEC (char, len + 1);
576 : 2102 : m_len = len;
577 : 2102 : memcpy (m_utf8, utf8, len);
578 : 2102 : m_utf8[len] = '\0';
579 : 2102 : }
580 : :
581 : : /* Implementation of json::value::print for json::string. */
582 : :
583 : : void
584 : 499785 : string::print (pretty_printer *pp,
585 : : bool formatted ATTRIBUTE_UNUSED) const
586 : : {
587 : 499785 : print_escaped_json_string (pp, m_utf8, m_len);
588 : 499785 : }
589 : :
590 : : std::unique_ptr<value>
591 : 1876 : string::clone () const
592 : : {
593 : 1876 : return std::make_unique<string> (m_utf8, m_len);
594 : : }
595 : :
596 : : /* class json::literal, a subclass of json::value. */
597 : :
598 : : /* Implementation of json::value::print for json::literal. */
599 : :
600 : : void
601 : 213 : literal::print (pretty_printer *pp,
602 : : bool formatted ATTRIBUTE_UNUSED) const
603 : : {
604 : 213 : switch (m_kind)
605 : : {
606 : 113 : case JSON_TRUE:
607 : 113 : pp_string (pp, "true");
608 : 113 : break;
609 : 92 : case JSON_FALSE:
610 : 92 : pp_string (pp, "false");
611 : 92 : break;
612 : 8 : case JSON_NULL:
613 : 8 : pp_string (pp, "null");
614 : 8 : break;
615 : 0 : default:
616 : 0 : gcc_unreachable ();
617 : : }
618 : 213 : }
619 : :
620 : : std::unique_ptr<value>
621 : 4 : literal::clone () const
622 : : {
623 : 4 : return std::make_unique<literal> (m_kind);
624 : : }
625 : :
626 : :
627 : : #if CHECKING_P
628 : :
629 : : namespace selftest {
630 : :
631 : : /* Selftests. */
632 : :
633 : : /* Verify that JV->print () prints EXPECTED_JSON. */
634 : :
635 : : void
636 : 160 : assert_print_eq (const location &loc,
637 : : const json::value &jv,
638 : : bool formatted,
639 : : const char *expected_json)
640 : : {
641 : 160 : pretty_printer pp;
642 : 160 : jv.print (&pp, formatted);
643 : 160 : ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
644 : 160 : }
645 : :
646 : : #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
647 : : assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
648 : :
649 : : /* Verify that object::get works as expected. */
650 : :
651 : : static void
652 : 4 : test_object_get ()
653 : : {
654 : 4 : object obj;
655 : 4 : value *val = new json::string ("value");
656 : 4 : obj.set ("foo", val);
657 : 4 : ASSERT_EQ (obj.get ("foo"), val);
658 : 4 : ASSERT_EQ (obj.get ("not-present"), NULL);
659 : 4 : }
660 : :
661 : : /* Verify that JSON objects are written correctly. */
662 : :
663 : : static void
664 : 4 : test_writing_objects ()
665 : : {
666 : 4 : object obj;
667 : 4 : obj.set_string ("foo", "bar");
668 : 4 : obj.set_string ("baz", "quux");
669 : 4 : obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
670 : :
671 : : /* This test relies on json::object writing out key/value pairs
672 : : in key-insertion order. */
673 : 4 : ASSERT_PRINT_EQ (obj, true,
674 : : "{\"foo\": \"bar\",\n"
675 : : " \"baz\": \"quux\",\n"
676 : : " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
677 : 4 : ASSERT_PRINT_EQ (obj, false,
678 : : "{\"foo\": \"bar\", \"baz\": \"quux\""
679 : : ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
680 : 4 : }
681 : :
682 : : /* Verify that JSON arrays are written correctly. */
683 : :
684 : : static void
685 : 4 : test_writing_arrays ()
686 : : {
687 : 4 : array arr;
688 : 4 : ASSERT_PRINT_EQ (arr, true, "[]");
689 : :
690 : 4 : arr.append (new json::string ("foo"));
691 : 4 : ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
692 : :
693 : 4 : arr.append_string ("bar");
694 : 4 : ASSERT_PRINT_EQ (arr, true,
695 : : "[\"foo\",\n"
696 : : " \"bar\"]");
697 : 4 : ASSERT_PRINT_EQ (arr, false,
698 : : "[\"foo\", \"bar\"]");
699 : 4 : }
700 : :
701 : : /* Verify that JSON numbers are written correctly. */
702 : :
703 : : static void
704 : 4 : test_writing_float_numbers ()
705 : : {
706 : 4 : ASSERT_PRINT_EQ (float_number (0), true, "0");
707 : 4 : ASSERT_PRINT_EQ (float_number (42), true, "42");
708 : 4 : ASSERT_PRINT_EQ (float_number (-100), true, "-100");
709 : 4 : ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
710 : 4 : }
711 : :
712 : : static void
713 : 4 : test_writing_integer_numbers ()
714 : : {
715 : 4 : ASSERT_PRINT_EQ (integer_number (0), true, "0");
716 : 4 : ASSERT_PRINT_EQ (integer_number (42), true, "42");
717 : 4 : ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
718 : 4 : ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
719 : 4 : ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
720 : 4 : }
721 : :
722 : : /* Verify that JSON strings are written correctly. */
723 : :
724 : : static void
725 : 4 : test_writing_strings ()
726 : : {
727 : 4 : string foo ("foo");
728 : 4 : ASSERT_PRINT_EQ (foo, true, "\"foo\"");
729 : :
730 : 4 : string contains_quotes ("before \"quoted\" after");
731 : 4 : ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
732 : :
733 : 4 : const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
734 : 4 : string not_terminated (data, 3);
735 : 4 : ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
736 : 4 : string embedded_null (data, sizeof data);
737 : 4 : ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
738 : 4 : }
739 : :
740 : : /* Verify that JSON literals are written correctly. */
741 : :
742 : : static void
743 : 4 : test_writing_literals ()
744 : : {
745 : 4 : ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
746 : 4 : ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
747 : 4 : ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
748 : :
749 : 4 : ASSERT_PRINT_EQ (literal (true), true, "true");
750 : 4 : ASSERT_PRINT_EQ (literal (false), true, "false");
751 : 4 : }
752 : :
753 : : /* Verify that nested values are formatted correctly when written.
754 : :
755 : : Also, make use of array::append(std::unique_ptr<value>) and
756 : : object::set (const char *key, std::unique_ptr<value> v).*/
757 : :
758 : : static void
759 : 4 : test_formatting ()
760 : : {
761 : 4 : object obj;
762 : 4 : object *child = new object;
763 : 4 : std::unique_ptr<object> grandchild = std::make_unique<object> ();
764 : :
765 : 4 : obj.set_string ("str", "bar");
766 : 4 : obj.set ("child", child);
767 : 4 : obj.set_integer ("int", 42);
768 : :
769 : 4 : array *arr = new array;
770 : 16 : for (int i = 0; i < 3; i++)
771 : 12 : arr->append (std::make_unique<integer_number> (i));
772 : 4 : grandchild->set ("arr", arr);
773 : 4 : grandchild->set_integer ("int", 1066);
774 : :
775 : 4 : child->set ("grandchild", std::move (grandchild));
776 : 4 : child->set_integer ("int", 1776);
777 : :
778 : : /* This test relies on json::object writing out key/value pairs
779 : : in key-insertion order. */
780 : 4 : ASSERT_PRINT_EQ (obj, true,
781 : : ("{\"str\": \"bar\",\n"
782 : : " \"child\": {\"grandchild\": {\"arr\": [0,\n"
783 : : " 1,\n"
784 : : " 2],\n"
785 : : " \"int\": 1066},\n"
786 : : " \"int\": 1776},\n"
787 : : " \"int\": 42}"));
788 : 4 : ASSERT_PRINT_EQ (obj, false,
789 : : ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
790 : : " {\"arr\": [0, 1, 2], \"int\": 1066},"
791 : : " \"int\": 1776}, \"int\": 42}"));
792 : 4 : }
793 : :
794 : : /* Helper function for reporting failure of JSON comparisons. */
795 : :
796 : : static void
797 : 0 : fail_comparison (const location &loc,
798 : : const char *desc,
799 : : const value &val_a, const value &val_b,
800 : : const char *desc_expected_value,
801 : : int actual_value)
802 : : {
803 : 0 : fprintf (stderr, "val_a: ");
804 : 0 : val_a.dump ();
805 : :
806 : 0 : fprintf (stderr, "val_b: ");
807 : 0 : val_b.dump ();
808 : :
809 : 0 : selftest::fail_formatted (loc,
810 : : "%s: failed JSON comparison:"
811 : : " expected: %s got: %i\n",
812 : : desc,
813 : : desc_expected_value, actual_value);
814 : : }
815 : :
816 : : /* Implementation of ASSERT_JSON_EQ. */
817 : :
818 : : static void
819 : 100 : assert_json_equal (const location &loc,
820 : : const char *desc,
821 : : const value &val_a, const value &val_b)
822 : : {
823 : : /* Comparison should return zero, both ways, indicating no differences. */
824 : 100 : const int a_vs_b = value::compare (val_a, val_b);
825 : 100 : if (a_vs_b != 0)
826 : 0 : fail_comparison (loc, desc, val_a, val_b, "zero", a_vs_b);
827 : :
828 : 100 : const int b_vs_a = value::compare (val_b, val_a);
829 : 100 : if (b_vs_a != 0)
830 : 0 : fail_comparison (loc, desc, val_b, val_a, "zero", b_vs_a);
831 : 100 : }
832 : :
833 : : /* Verify that json::value::compare returns 0 ("no differences") on
834 : : VAL1 and VAL2, in both orders. */
835 : :
836 : : #define ASSERT_JSON_EQ(VAL1, VAL2) \
837 : : SELFTEST_BEGIN_STMT \
838 : : assert_json_equal ((SELFTEST_LOCATION), \
839 : : "ASSERT_JSON_EQ", \
840 : : (VAL1), (VAL2)); \
841 : : SELFTEST_END_STMT
842 : :
843 : : /* Implementation of ASSERT_JSON_NE. */
844 : :
845 : : static void
846 : 48 : assert_json_non_equal (const location &loc,
847 : : const char *desc,
848 : : const value &val_a, const value &val_b)
849 : : {
850 : : /* Comparison should be non-zero, indicating differences. */
851 : 48 : const int a_vs_b = value::compare (val_a, val_b);
852 : 48 : if (a_vs_b == 0)
853 : 0 : fail_comparison (loc, desc, val_a, val_b, "non-zero", a_vs_b);
854 : :
855 : 48 : const int b_vs_a = value::compare (val_b, val_a);
856 : 48 : ASSERT_NE_AT (loc, b_vs_a, 0);
857 : 48 : if (b_vs_a == 0)
858 : : fail_comparison (loc, desc, val_b, val_a, "non-zero", b_vs_a);
859 : :
860 : : /* Swapping the args should swap the sign of the result
861 : : (but isn't necessarily the negation). */
862 : 48 : if ( (a_vs_b > 0) == (b_vs_a > 0) )
863 : 0 : fail_comparison (loc, desc, val_b, val_a, "opposite signs", 1);
864 : 48 : }
865 : :
866 : : /* Verify that json::value::compare returns non-zero ("different") on
867 : : VAL1 and VAL2, in both orders, and that they have opposite
868 : : sign. */
869 : :
870 : : #define ASSERT_JSON_NE(VAL1, VAL2) \
871 : : SELFTEST_BEGIN_STMT \
872 : : assert_json_non_equal ((SELFTEST_LOCATION), \
873 : : "ASSERT_JSON_NE", \
874 : : (VAL1), (VAL2)); \
875 : : SELFTEST_END_STMT
876 : :
877 : : /* Verify that json::value::compare works as expected. */
878 : :
879 : : static void
880 : 4 : test_comparisons ()
881 : : {
882 : : /* Literals. */
883 : :
884 : 4 : literal null_lit (JSON_NULL);
885 : 4 : ASSERT_JSON_EQ (null_lit, null_lit);
886 : :
887 : 4 : literal other_null_lit (JSON_NULL);
888 : 4 : ASSERT_JSON_EQ (null_lit, other_null_lit);
889 : :
890 : 4 : literal true_lit (JSON_TRUE);
891 : 4 : ASSERT_JSON_EQ (true_lit, true_lit);
892 : 4 : ASSERT_JSON_NE (true_lit, null_lit);
893 : :
894 : 4 : literal false_lit (JSON_FALSE);
895 : 4 : ASSERT_JSON_EQ (false_lit, false_lit);
896 : 4 : ASSERT_JSON_NE (false_lit, true_lit);
897 : 4 : ASSERT_JSON_NE (false_lit, null_lit);
898 : :
899 : : /* Strings. */
900 : 4 : string str_foo_1 ("foo");
901 : 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_1);
902 : :
903 : 4 : string str_foo_2 ("foo");
904 : 4 : ASSERT_JSON_EQ (str_foo_1, str_foo_2);
905 : :
906 : 4 : string str_bar ("bar");
907 : 4 : ASSERT_JSON_NE (str_bar, str_foo_1);
908 : :
909 : : /* Numbers. */
910 : 4 : integer_number i_42 (42);
911 : 4 : ASSERT_JSON_EQ (i_42, i_42);
912 : 4 : integer_number i_42_2 (42);
913 : 4 : ASSERT_JSON_EQ (i_42, i_42_2);
914 : 4 : integer_number i_43 (43);
915 : 4 : ASSERT_JSON_NE (i_42, i_43);
916 : :
917 : 4 : float_number f_zero (0.0);
918 : 4 : ASSERT_JSON_EQ (f_zero, f_zero);
919 : 4 : float_number f_zero_2 (0.0);
920 : 4 : ASSERT_JSON_EQ (f_zero, f_zero_2);
921 : 4 : float_number f_one (1.0);
922 : 4 : ASSERT_JSON_NE (f_zero, f_one);
923 : : /* We don't yet test the more awkward cases e.g. NaN. */
924 : :
925 : : /* Objects. */
926 : :
927 : : // Empty object
928 : : // Self comparison should be 0
929 : 4 : object empty_obj_a;
930 : 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_a);
931 : :
932 : : // Instances of empty objects should compare equal to each other
933 : 4 : object empty_obj_b;
934 : 4 : ASSERT_JSON_EQ (empty_obj_a, empty_obj_b);
935 : :
936 : : // Object with one field:
937 : 4 : object obj_1;
938 : 4 : obj_1.set_string ("foo", "bar");
939 : : // Self comparison should be 0
940 : 4 : ASSERT_JSON_EQ (obj_1, obj_1);
941 : :
942 : : // but should be different to an empty object:
943 : 4 : ASSERT_JSON_NE (obj_1, empty_obj_a);
944 : :
945 : : // Another with one field, with same key/value:
946 : 4 : object obj_2;
947 : 4 : obj_2.set_string ("foo", "bar");
948 : 4 : ASSERT_JSON_EQ (obj_1, obj_2);
949 : :
950 : : // Same key, different value:
951 : 4 : object obj_3;
952 : 4 : obj_3.set_string ("foo", "baz");
953 : 4 : ASSERT_JSON_NE (obj_1, obj_3);
954 : :
955 : : // Adding an extra property:
956 : 4 : obj_2.set_integer ("year", 1066);
957 : 4 : ASSERT_JSON_NE (obj_1, obj_2);
958 : :
959 : : /* Different insertion order, but same k-v pairs should be equal,
960 : : despite having different serialization. */
961 : 4 : object obj_4;
962 : 4 : obj_4.set_integer ("year", 1066);
963 : 4 : obj_4.set_string ("foo", "bar");
964 : 4 : ASSERT_JSON_EQ (obj_2, obj_4);
965 : 4 : ASSERT_PRINT_EQ (obj_2, false, "{\"foo\": \"bar\", \"year\": 1066}");
966 : 4 : ASSERT_PRINT_EQ (obj_4, false, "{\"year\": 1066, \"foo\": \"bar\"}");
967 : :
968 : : /* Arrays. */
969 : :
970 : : // Empty array
971 : 4 : array empty_arr_a;
972 : : // Self comparison should be 0
973 : 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_a);
974 : :
975 : : // Objects and arrays are different
976 : 4 : ASSERT_JSON_NE (empty_obj_a, empty_arr_a);
977 : :
978 : : // Instances of empty arrays should compare equal to each other
979 : 4 : array empty_arr_b;
980 : 4 : ASSERT_JSON_EQ (empty_arr_a, empty_arr_b);
981 : :
982 : : // Array with one element:
983 : 4 : array arr_1;
984 : 4 : arr_1.append (std::make_unique<string> ("foo"));
985 : : // Self comparison should be 0
986 : 4 : ASSERT_JSON_EQ (arr_1, arr_1);
987 : :
988 : : // but should be different to an empty array:
989 : 4 : ASSERT_JSON_NE (arr_1, empty_arr_a);
990 : :
991 : : // Another with one element:
992 : 4 : array arr_2;
993 : 4 : arr_2.append (std::make_unique<string> ("foo"));
994 : 4 : ASSERT_JSON_EQ (arr_1, arr_2);
995 : :
996 : : // Adding an extra element:
997 : 4 : arr_2.append (std::make_unique<string> ("bar"));
998 : 4 : ASSERT_JSON_NE (arr_1, arr_2);
999 : 4 : }
1000 : :
1001 : : /* Ensure that json::string's get_string is usable as a C-style string. */
1002 : :
1003 : : static void
1004 : 4 : test_strcmp ()
1005 : : {
1006 : 4 : string str ("foobar", 3);
1007 : 4 : ASSERT_EQ (strcmp (str.get_string (), "foo"), 0);
1008 : 4 : }
1009 : :
1010 : : static void
1011 : 4 : test_cloning ()
1012 : : {
1013 : : // Objects
1014 : 4 : {
1015 : 4 : object obj;
1016 : 4 : obj.set_string ("foo", "bar");
1017 : :
1018 : 4 : auto obj_clone = obj.clone ();
1019 : 4 : ASSERT_JSON_EQ (obj, *obj_clone);
1020 : 4 : }
1021 : :
1022 : : // Arrays
1023 : 4 : {
1024 : 4 : array arr;
1025 : 4 : arr.append (std::make_unique<string> ("foo"));
1026 : :
1027 : 4 : auto arr_clone = arr.clone ();
1028 : 4 : ASSERT_JSON_EQ (arr, *arr_clone);
1029 : 4 : }
1030 : :
1031 : : // float_number
1032 : 4 : {
1033 : 4 : float_number f_one (1.0);
1034 : 4 : auto f_clone = f_one.clone ();
1035 : 4 : ASSERT_JSON_EQ (f_one, *f_clone);
1036 : 4 : }
1037 : :
1038 : : // integer_number
1039 : 4 : {
1040 : 4 : integer_number num (42);
1041 : 4 : auto num_clone = num.clone ();
1042 : 4 : ASSERT_JSON_EQ (num, *num_clone);
1043 : 4 : }
1044 : :
1045 : : // string
1046 : 4 : {
1047 : 4 : string str ("foo");
1048 : 4 : auto str_clone = str.clone ();
1049 : 4 : ASSERT_JSON_EQ (str, *str_clone);
1050 : 4 : }
1051 : :
1052 : : // literal
1053 : 4 : {
1054 : 4 : literal lit (JSON_TRUE);
1055 : 4 : auto lit_clone = lit.clone ();
1056 : 4 : ASSERT_JSON_EQ (lit, *lit_clone);
1057 : 4 : }
1058 : 4 : }
1059 : :
1060 : : /* Run all of the selftests within this file. */
1061 : :
1062 : : void
1063 : 4 : json_cc_tests ()
1064 : : {
1065 : 4 : test_object_get ();
1066 : 4 : test_writing_objects ();
1067 : 4 : test_writing_arrays ();
1068 : 4 : test_writing_float_numbers ();
1069 : 4 : test_writing_integer_numbers ();
1070 : 4 : test_writing_strings ();
1071 : 4 : test_writing_literals ();
1072 : 4 : test_formatting ();
1073 : 4 : test_comparisons ();
1074 : 4 : test_strcmp ();
1075 : 4 : test_cloning ();
1076 : 4 : }
1077 : :
1078 : : } // namespace selftest
1079 : :
1080 : : #endif /* #if CHECKING_P */
|