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 "make-unique.h"
28 : : #include "selftest.h"
29 : :
30 : : using namespace json;
31 : :
32 : : /* Print a JSON string to PP, escaping '"', control characters,
33 : : and embedded null bytes.
34 : : The string is required to be UTF-8 encoded. */
35 : :
36 : : static void
37 : 1366208 : print_escaped_json_string (pretty_printer *pp,
38 : : const char *utf8_str,
39 : : size_t len)
40 : : {
41 : 1366208 : pp_character (pp, '"');
42 : 18452560 : for (size_t i = 0; i != len; ++i)
43 : : {
44 : 17086352 : char ch = utf8_str[i];
45 : 17086352 : switch (ch)
46 : : {
47 : 2534 : case '"':
48 : 2534 : pp_string (pp, "\\\"");
49 : 2534 : break;
50 : 1439 : case '\\':
51 : 1439 : pp_string (pp, "\\\\");
52 : 1439 : break;
53 : 8 : case '\b':
54 : 8 : pp_string (pp, "\\b");
55 : 8 : break;
56 : 8 : case '\f':
57 : 8 : pp_string (pp, "\\f");
58 : 8 : break;
59 : 54099 : case '\n':
60 : 54099 : pp_string (pp, "\\n");
61 : 54099 : break;
62 : 8 : case '\r':
63 : 8 : pp_string (pp, "\\r");
64 : 8 : break;
65 : 3558 : case '\t':
66 : 3558 : pp_string (pp, "\\t");
67 : 3558 : break;
68 : 4 : case '\0':
69 : 4 : pp_string (pp, "\\0");
70 : 4 : break;
71 : 17024694 : default:
72 : 17024694 : pp_character (pp, ch);
73 : : }
74 : : }
75 : 1366208 : pp_character (pp, '"');
76 : 1366208 : }
77 : :
78 : : /* class json::value. */
79 : :
80 : : /* Dump this json::value tree to OUTF.
81 : :
82 : : The key/value pairs of json::objects are printed in the order
83 : : in which the keys were originally inserted. */
84 : :
85 : : void
86 : 136 : value::dump (FILE *outf, bool formatted) const
87 : : {
88 : 136 : pretty_printer pp;
89 : 136 : pp_buffer (&pp)->m_stream = outf;
90 : 136 : print (&pp, formatted);
91 : 136 : pp_flush (&pp);
92 : 136 : }
93 : :
94 : : /* A convenience function for debugging.
95 : : Dump to stderr with formatting, and a trailing newline. */
96 : :
97 : : void
98 : 0 : value::dump () const
99 : : {
100 : 0 : dump (stderr, true);
101 : 0 : fprintf (stderr, "\n");
102 : 0 : }
103 : :
104 : : /* class json::object, a subclass of json::value, representing
105 : : an ordered collection of key/value pairs. */
106 : :
107 : : /* json:object's dtor. */
108 : :
109 : 1508714 : object::~object ()
110 : : {
111 : 7428240 : for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
112 : : {
113 : 3334540 : free (const_cast <char *>((*it).first));
114 : 3334540 : delete ((*it).second);
115 : : }
116 : 1508714 : }
117 : :
118 : : /* Implementation of json::value::print for json::object. */
119 : :
120 : : void
121 : 257977 : object::print (pretty_printer *pp, bool formatted) const
122 : : {
123 : 257977 : pp_character (pp, '{');
124 : 257977 : if (formatted)
125 : 3953 : pp_indentation (pp) += 1;
126 : :
127 : : /* Iterate in the order that the keys were inserted. */
128 : : unsigned i;
129 : : const char *key;
130 : 1099358 : FOR_EACH_VEC_ELT (m_keys, i, key)
131 : : {
132 : 841381 : if (i > 0)
133 : : {
134 : 583412 : pp_string (pp, ",");
135 : 583412 : if (formatted)
136 : : {
137 : 5590 : pp_newline (pp);
138 : 5590 : pp_indent (pp);
139 : : }
140 : : else
141 : 577822 : pp_space (pp);
142 : : }
143 : 841381 : map_t &mut_map = const_cast<map_t &> (m_map);
144 : 841381 : value *value = *mut_map.get (key);
145 : 841381 : print_escaped_json_string (pp, key, strlen (key));
146 : 841381 : pp_string (pp, ": ");
147 : 841381 : const int indent = strlen (key) + 4;
148 : 841381 : if (formatted)
149 : 9535 : pp_indentation (pp) += indent;
150 : 841381 : value->print (pp, formatted);
151 : 841381 : if (formatted)
152 : 9535 : pp_indentation (pp) -= indent;
153 : : }
154 : 257977 : if (formatted)
155 : 3953 : pp_indentation (pp) -= 1;
156 : 257977 : pp_character (pp, '}');
157 : 257977 : }
158 : :
159 : : /* Set the json::value * for KEY, taking ownership of V
160 : : (and taking a copy of KEY if necessary). */
161 : :
162 : : void
163 : 3335597 : object::set (const char *key, value *v)
164 : : {
165 : 3335597 : gcc_assert (key);
166 : 3335597 : gcc_assert (v);
167 : :
168 : 3335597 : value **ptr = m_map.get (key);
169 : 3335597 : if (ptr)
170 : : {
171 : : /* If the key is already present, delete the existing value
172 : : and overwrite it. */
173 : 0 : delete *ptr;
174 : 0 : *ptr = v;
175 : : }
176 : : else
177 : : {
178 : : /* If the key wasn't already present, take a copy of the key,
179 : : and store the value. */
180 : 3335597 : char *owned_key = xstrdup (key);
181 : 3335597 : m_map.put (owned_key, v);
182 : 3335597 : m_keys.safe_push (owned_key);
183 : : }
184 : 3335597 : }
185 : :
186 : : /* Get the json::value * for KEY.
187 : :
188 : : The object retains ownership of the value. */
189 : :
190 : : value *
191 : 6270 : object::get (const char *key) const
192 : : {
193 : 6270 : gcc_assert (key);
194 : :
195 : 6270 : value **ptr = const_cast <map_t &> (m_map).get (key);
196 : 6270 : if (ptr)
197 : 5892 : return *ptr;
198 : : else
199 : : return NULL;
200 : : }
201 : :
202 : : /* Set value of KEY within this object to a JSON
203 : : string value based on UTF8_VALUE. */
204 : :
205 : : void
206 : 1356821 : object::set_string (const char *key, const char *utf8_value)
207 : : {
208 : 1356821 : set (key, new json::string (utf8_value));
209 : 1356821 : }
210 : :
211 : : /* Set value of KEY within this object to a JSON
212 : : integer value based on V. */
213 : :
214 : : void
215 : 675127 : object::set_integer (const char *key, long v)
216 : : {
217 : 675127 : set (key, new json::integer_number (v));
218 : 675127 : }
219 : :
220 : : /* Set value of KEY within this object to a JSON
221 : : floating point value based on V. */
222 : :
223 : : void
224 : 16 : object::set_float (const char *key, double v)
225 : : {
226 : 16 : set (key, new json::float_number (v));
227 : 16 : }
228 : :
229 : : /* Set value of KEY within this object to the JSON
230 : : literal true or false, based on V. */
231 : :
232 : : void
233 : 531 : object::set_bool (const char *key, bool v)
234 : : {
235 : 836 : set (key, new json::literal (v));
236 : 531 : }
237 : :
238 : : /* class json::array, a subclass of json::value, representing
239 : : an ordered collection of values. */
240 : :
241 : : /* json::array's dtor. */
242 : :
243 : 1231320 : array::~array ()
244 : : {
245 : 615662 : unsigned i;
246 : 615662 : value *v;
247 : 1415306 : FOR_EACH_VEC_ELT (m_elements, i, v)
248 : 799644 : delete v;
249 : 1231320 : }
250 : :
251 : : /* Implementation of json::value::print for json::array. */
252 : :
253 : : void
254 : 96720 : array::print (pretty_printer *pp, bool formatted) const
255 : : {
256 : 96720 : pp_character (pp, '[');
257 : 96720 : if (formatted)
258 : 1391 : pp_indentation (pp) += 1;
259 : : unsigned i;
260 : : value *v;
261 : 314806 : FOR_EACH_VEC_ELT (m_elements, i, v)
262 : : {
263 : 218086 : if (i)
264 : : {
265 : 135882 : pp_string (pp, ",");
266 : 135882 : if (formatted)
267 : : {
268 : 3212 : pp_newline (pp);
269 : 3212 : pp_indent (pp);
270 : : }
271 : : else
272 : 132670 : pp_space (pp);
273 : : }
274 : 218086 : v->print (pp, formatted);
275 : : }
276 : 96720 : if (formatted)
277 : 1391 : pp_indentation (pp) -= 1;
278 : 96720 : pp_character (pp, ']');
279 : 96720 : }
280 : :
281 : : /* Append non-NULL value V to a json::array, taking ownership of V. */
282 : :
283 : : void
284 : 799723 : array::append (value *v)
285 : : {
286 : 799723 : gcc_assert (v);
287 : 799723 : m_elements.safe_push (v);
288 : 799723 : }
289 : :
290 : : void
291 : 182943 : array::append_string (const char *utf8_value)
292 : : {
293 : 182943 : gcc_assert (utf8_value);
294 : 182943 : append (new json::string (utf8_value));
295 : 182943 : }
296 : :
297 : : /* class json::float_number, a subclass of json::value, wrapping a double. */
298 : :
299 : : /* Implementation of json::value::print for json::float_number. */
300 : :
301 : : void
302 : 32 : float_number::print (pretty_printer *pp,
303 : : bool formatted ATTRIBUTE_UNUSED) const
304 : : {
305 : 32 : char tmp[1024];
306 : 32 : snprintf (tmp, sizeof (tmp), "%g", m_value);
307 : 32 : pp_string (pp, tmp);
308 : 32 : }
309 : :
310 : : /* class json::integer_number, a subclass of json::value, wrapping a long. */
311 : :
312 : : /* Implementation of json::value::print for json::integer_number. */
313 : :
314 : : void
315 : 179984 : integer_number::print (pretty_printer *pp,
316 : : bool formatted ATTRIBUTE_UNUSED) const
317 : : {
318 : 179984 : char tmp[1024];
319 : 179984 : snprintf (tmp, sizeof (tmp), "%ld", m_value);
320 : 179984 : pp_string (pp, tmp);
321 : 179984 : }
322 : :
323 : :
324 : : /* class json::string, a subclass of json::value. */
325 : :
326 : : /* json::string's ctor. */
327 : :
328 : 2087014 : string::string (const char *utf8)
329 : : {
330 : 2087014 : gcc_assert (utf8);
331 : 2087014 : m_utf8 = xstrdup (utf8);
332 : 2087014 : m_len = strlen (utf8);
333 : 2087014 : }
334 : :
335 : 212 : string::string (const char *utf8, size_t len)
336 : : {
337 : 212 : gcc_assert (utf8);
338 : 212 : m_utf8 = XNEWVEC (char, len);
339 : 212 : m_len = len;
340 : 212 : memcpy (m_utf8, utf8, len);
341 : 212 : }
342 : :
343 : : /* Implementation of json::value::print for json::string. */
344 : :
345 : : void
346 : 524827 : string::print (pretty_printer *pp,
347 : : bool formatted ATTRIBUTE_UNUSED) const
348 : : {
349 : 524827 : print_escaped_json_string (pp, m_utf8, m_len);
350 : 524827 : }
351 : :
352 : : /* class json::literal, a subclass of json::value. */
353 : :
354 : : /* Implementation of json::value::print for json::literal. */
355 : :
356 : : void
357 : 262 : literal::print (pretty_printer *pp,
358 : : bool formatted ATTRIBUTE_UNUSED) const
359 : : {
360 : 262 : switch (m_kind)
361 : : {
362 : 110 : case JSON_TRUE:
363 : 110 : pp_string (pp, "true");
364 : 110 : break;
365 : 144 : case JSON_FALSE:
366 : 144 : pp_string (pp, "false");
367 : 144 : break;
368 : 8 : case JSON_NULL:
369 : 8 : pp_string (pp, "null");
370 : 8 : break;
371 : 0 : default:
372 : 0 : gcc_unreachable ();
373 : : }
374 : 262 : }
375 : :
376 : :
377 : : #if CHECKING_P
378 : :
379 : : namespace selftest {
380 : :
381 : : /* Selftests. */
382 : :
383 : : /* Verify that JV->print () prints EXPECTED_JSON. */
384 : :
385 : : void
386 : 152 : assert_print_eq (const location &loc,
387 : : const json::value &jv,
388 : : bool formatted,
389 : : const char *expected_json)
390 : : {
391 : 152 : pretty_printer pp;
392 : 152 : jv.print (&pp, formatted);
393 : 152 : ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
394 : 152 : }
395 : :
396 : : #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
397 : : assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
398 : :
399 : : /* Verify that object::get works as expected. */
400 : :
401 : : static void
402 : 4 : test_object_get ()
403 : : {
404 : 4 : object obj;
405 : 4 : value *val = new json::string ("value");
406 : 4 : obj.set ("foo", val);
407 : 4 : ASSERT_EQ (obj.get ("foo"), val);
408 : 4 : ASSERT_EQ (obj.get ("not-present"), NULL);
409 : 4 : }
410 : :
411 : : /* Verify that JSON objects are written correctly. */
412 : :
413 : : static void
414 : 4 : test_writing_objects ()
415 : : {
416 : 4 : object obj;
417 : 4 : obj.set_string ("foo", "bar");
418 : 4 : obj.set_string ("baz", "quux");
419 : 4 : obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
420 : :
421 : : /* This test relies on json::object writing out key/value pairs
422 : : in key-insertion order. */
423 : 4 : ASSERT_PRINT_EQ (obj, true,
424 : : "{\"foo\": \"bar\",\n"
425 : : " \"baz\": \"quux\",\n"
426 : : " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
427 : 4 : ASSERT_PRINT_EQ (obj, false,
428 : : "{\"foo\": \"bar\", \"baz\": \"quux\""
429 : : ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
430 : 4 : }
431 : :
432 : : /* Verify that JSON arrays are written correctly. */
433 : :
434 : : static void
435 : 4 : test_writing_arrays ()
436 : : {
437 : 4 : array arr;
438 : 4 : ASSERT_PRINT_EQ (arr, true, "[]");
439 : :
440 : 4 : arr.append (new json::string ("foo"));
441 : 4 : ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
442 : :
443 : 4 : arr.append_string ("bar");
444 : 4 : ASSERT_PRINT_EQ (arr, true,
445 : : "[\"foo\",\n"
446 : : " \"bar\"]");
447 : 4 : ASSERT_PRINT_EQ (arr, false,
448 : : "[\"foo\", \"bar\"]");
449 : 4 : }
450 : :
451 : : /* Verify that JSON numbers are written correctly. */
452 : :
453 : : static void
454 : 4 : test_writing_float_numbers ()
455 : : {
456 : 4 : ASSERT_PRINT_EQ (float_number (0), true, "0");
457 : 4 : ASSERT_PRINT_EQ (float_number (42), true, "42");
458 : 4 : ASSERT_PRINT_EQ (float_number (-100), true, "-100");
459 : 4 : ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
460 : 4 : }
461 : :
462 : : static void
463 : 4 : test_writing_integer_numbers ()
464 : : {
465 : 4 : ASSERT_PRINT_EQ (integer_number (0), true, "0");
466 : 4 : ASSERT_PRINT_EQ (integer_number (42), true, "42");
467 : 4 : ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
468 : 4 : ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
469 : 4 : ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
470 : 4 : }
471 : :
472 : : /* Verify that JSON strings are written correctly. */
473 : :
474 : : static void
475 : 4 : test_writing_strings ()
476 : : {
477 : 4 : string foo ("foo");
478 : 4 : ASSERT_PRINT_EQ (foo, true, "\"foo\"");
479 : :
480 : 4 : string contains_quotes ("before \"quoted\" after");
481 : 4 : ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
482 : :
483 : 4 : const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
484 : 4 : string not_terminated (data, 3);
485 : 4 : ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
486 : 4 : string embedded_null (data, sizeof data);
487 : 4 : ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
488 : 4 : }
489 : :
490 : : /* Verify that JSON literals are written correctly. */
491 : :
492 : : static void
493 : 4 : test_writing_literals ()
494 : : {
495 : 4 : ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
496 : 4 : ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
497 : 4 : ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
498 : :
499 : 4 : ASSERT_PRINT_EQ (literal (true), true, "true");
500 : 4 : ASSERT_PRINT_EQ (literal (false), true, "false");
501 : 4 : }
502 : :
503 : : /* Verify that nested values are formatted correctly when written.
504 : :
505 : : Also, make use of array::append(std::unique_ptr<value>) and
506 : : object::set (const char *key, std::unique_ptr<value> v).*/
507 : :
508 : : static void
509 : 4 : test_formatting ()
510 : : {
511 : 4 : object obj;
512 : 4 : object *child = new object;
513 : 4 : std::unique_ptr<object> grandchild = ::make_unique<object> ();
514 : :
515 : 4 : obj.set_string ("str", "bar");
516 : 4 : obj.set ("child", child);
517 : 4 : obj.set_integer ("int", 42);
518 : :
519 : 4 : array *arr = new array;
520 : 16 : for (int i = 0; i < 3; i++)
521 : 12 : arr->append (::make_unique<integer_number> (i));
522 : 4 : grandchild->set ("arr", arr);
523 : 4 : grandchild->set_integer ("int", 1066);
524 : :
525 : 4 : child->set ("grandchild", std::move (grandchild));
526 : 4 : child->set_integer ("int", 1776);
527 : :
528 : : /* This test relies on json::object writing out key/value pairs
529 : : in key-insertion order. */
530 : 4 : ASSERT_PRINT_EQ (obj, true,
531 : : ("{\"str\": \"bar\",\n"
532 : : " \"child\": {\"grandchild\": {\"arr\": [0,\n"
533 : : " 1,\n"
534 : : " 2],\n"
535 : : " \"int\": 1066},\n"
536 : : " \"int\": 1776},\n"
537 : : " \"int\": 42}"));
538 : 4 : ASSERT_PRINT_EQ (obj, false,
539 : : ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
540 : : " {\"arr\": [0, 1, 2], \"int\": 1066},"
541 : : " \"int\": 1776}, \"int\": 42}"));
542 : 4 : }
543 : :
544 : : /* Run all of the selftests within this file. */
545 : :
546 : : void
547 : 4 : json_cc_tests ()
548 : : {
549 : 4 : test_object_get ();
550 : 4 : test_writing_objects ();
551 : 4 : test_writing_arrays ();
552 : 4 : test_writing_float_numbers ();
553 : 4 : test_writing_integer_numbers ();
554 : 4 : test_writing_strings ();
555 : 4 : test_writing_literals ();
556 : 4 : test_formatting ();
557 : 4 : }
558 : :
559 : : } // namespace selftest
560 : :
561 : : #endif /* #if CHECKING_P */
|