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