1/* JSON trees
2 Copyright (C) 2017-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
29using 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
35static void
36print_escaped_json_string (pretty_printer *pp,
37 const char *utf8_str,
38 size_t len)
39{
40 pp_character (pp, '"');
41 for (size_t i = 0; i != len; ++i)
42 {
43 char ch = utf8_str[i];
44 switch (ch)
45 {
46 case '"':
47 pp_string (pp, "\\\"");
48 break;
49 case '\\':
50 pp_string (pp, "\\\\");
51 break;
52 case '\b':
53 pp_string (pp, "\\b");
54 break;
55 case '\f':
56 pp_string (pp, "\\f");
57 break;
58 case '\n':
59 pp_string (pp, "\\n");
60 break;
61 case '\r':
62 pp_string (pp, "\\r");
63 break;
64 case '\t':
65 pp_string (pp, "\\t");
66 break;
67 case '\0':
68 pp_string (pp, "\\0");
69 break;
70 default:
71 pp_character (pp, ch);
72 }
73 }
74 pp_character (pp, '"');
75}
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
84void
85value::dump (FILE *outf, bool formatted) const
86{
87 pretty_printer pp;
88 pp_buffer (&pp)->stream = outf;
89 print (pp: &pp, formatted);
90 pp_flush (&pp);
91}
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
98object::~object ()
99{
100 for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
101 {
102 free (ptr: const_cast <char *>((*it).first));
103 delete ((*it).second);
104 }
105}
106
107/* Implementation of json::value::print for json::object. */
108
109void
110object::print (pretty_printer *pp, bool formatted) const
111{
112 pp_character (pp, '{');
113 if (formatted)
114 pp_indentation (pp) += 1;
115
116 /* Iterate in the order that the keys were inserted. */
117 unsigned i;
118 const char *key;
119 FOR_EACH_VEC_ELT (m_keys, i, key)
120 {
121 if (i > 0)
122 {
123 pp_string (pp, ",");
124 if (formatted)
125 {
126 pp_newline (pp);
127 pp_indent (pp);
128 }
129 else
130 pp_space (pp);
131 }
132 map_t &mut_map = const_cast<map_t &> (m_map);
133 value *value = *mut_map.get (k: key);
134 print_escaped_json_string (pp, utf8_str: key, len: strlen (s: key));
135 pp_string (pp, ": ");
136 const int indent = strlen (s: key) + 4;
137 if (formatted)
138 pp_indentation (pp) += indent;
139 value->print (pp, formatted);
140 if (formatted)
141 pp_indentation (pp) -= indent;
142 }
143 if (formatted)
144 pp_indentation (pp) -= 1;
145 pp_character (pp, '}');
146}
147
148/* Set the json::value * for KEY, taking ownership of V
149 (and taking a copy of KEY if necessary). */
150
151void
152object::set (const char *key, value *v)
153{
154 gcc_assert (key);
155 gcc_assert (v);
156
157 value **ptr = m_map.get (k: key);
158 if (ptr)
159 {
160 /* If the key is already present, delete the existing value
161 and overwrite it. */
162 delete *ptr;
163 *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 char *owned_key = xstrdup (key);
170 m_map.put (k: owned_key, v);
171 m_keys.safe_push (obj: owned_key);
172 }
173}
174
175/* Get the json::value * for KEY.
176
177 The object retains ownership of the value. */
178
179value *
180object::get (const char *key) const
181{
182 gcc_assert (key);
183
184 value **ptr = const_cast <map_t &> (m_map).get (k: key);
185 if (ptr)
186 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
194void
195object::set_string (const char *key, const char *utf8_value)
196{
197 set (key, v: new json::string (utf8_value));
198}
199
200/* Set value of KEY within this object to a JSON
201 integer value based on V. */
202
203void
204object::set_integer (const char *key, long v)
205{
206 set (key, v: new json::integer_number (v));
207}
208
209/* Set value of KEY within this object to a JSON
210 floating point value based on V. */
211
212void
213object::set_float (const char *key, double v)
214{
215 set (key, v: new json::float_number (v));
216}
217
218/* Set value of KEY within this object to the JSON
219 literal true or false, based on V. */
220
221void
222object::set_bool (const char *key, bool v)
223{
224 set (key, v: new json::literal (v));
225}
226
227/* class json::array, a subclass of json::value, representing
228 an ordered collection of values. */
229
230/* json::array's dtor. */
231
232array::~array ()
233{
234 unsigned i;
235 value *v;
236 FOR_EACH_VEC_ELT (m_elements, i, v)
237 delete v;
238}
239
240/* Implementation of json::value::print for json::array. */
241
242void
243array::print (pretty_printer *pp, bool formatted) const
244{
245 pp_character (pp, '[');
246 if (formatted)
247 pp_indentation (pp) += 1;
248 unsigned i;
249 value *v;
250 FOR_EACH_VEC_ELT (m_elements, i, v)
251 {
252 if (i)
253 {
254 pp_string (pp, ",");
255 if (formatted)
256 {
257 pp_newline (pp);
258 pp_indent (pp);
259 }
260 else
261 pp_space (pp);
262 }
263 v->print (pp, formatted);
264 }
265 if (formatted)
266 pp_indentation (pp) -= 1;
267 pp_character (pp, ']');
268}
269
270/* Append non-NULL value V to a json::array, taking ownership of V. */
271
272void
273array::append (value *v)
274{
275 gcc_assert (v);
276 m_elements.safe_push (obj: v);
277}
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
283void
284float_number::print (pretty_printer *pp,
285 bool formatted ATTRIBUTE_UNUSED) const
286{
287 char tmp[1024];
288 snprintf (s: tmp, maxlen: sizeof (tmp), format: "%g", m_value);
289 pp_string (pp, tmp);
290}
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
296void
297integer_number::print (pretty_printer *pp,
298 bool formatted ATTRIBUTE_UNUSED) const
299{
300 char tmp[1024];
301 snprintf (s: tmp, maxlen: sizeof (tmp), format: "%ld", m_value);
302 pp_string (pp, tmp);
303}
304
305
306/* class json::string, a subclass of json::value. */
307
308/* json::string's ctor. */
309
310string::string (const char *utf8)
311{
312 gcc_assert (utf8);
313 m_utf8 = xstrdup (utf8);
314 m_len = strlen (s: utf8);
315}
316
317string::string (const char *utf8, size_t len)
318{
319 gcc_assert (utf8);
320 m_utf8 = XNEWVEC (char, len);
321 m_len = len;
322 memcpy (dest: m_utf8, src: utf8, n: len);
323}
324
325/* Implementation of json::value::print for json::string. */
326
327void
328string::print (pretty_printer *pp,
329 bool formatted ATTRIBUTE_UNUSED) const
330{
331 print_escaped_json_string (pp, utf8_str: m_utf8, len: m_len);
332}
333
334/* class json::literal, a subclass of json::value. */
335
336/* Implementation of json::value::print for json::literal. */
337
338void
339literal::print (pretty_printer *pp,
340 bool formatted ATTRIBUTE_UNUSED) const
341{
342 switch (m_kind)
343 {
344 case JSON_TRUE:
345 pp_string (pp, "true");
346 break;
347 case JSON_FALSE:
348 pp_string (pp, "false");
349 break;
350 case JSON_NULL:
351 pp_string (pp, "null");
352 break;
353 default:
354 gcc_unreachable ();
355 }
356}
357
358
359#if CHECKING_P
360
361namespace selftest {
362
363/* Selftests. */
364
365/* Verify that JV->print () prints EXPECTED_JSON. */
366
367static void
368assert_print_eq (const location &loc,
369 const json::value &jv,
370 bool formatted,
371 const char *expected_json)
372{
373 pretty_printer pp;
374 jv.print (pp: &pp, formatted);
375 ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
376}
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
383static void
384test_object_get ()
385{
386 object obj;
387 value *val = new json::string ("value");
388 obj.set (key: "foo", v: val);
389 ASSERT_EQ (obj.get ("foo"), val);
390 ASSERT_EQ (obj.get ("not-present"), NULL);
391}
392
393/* Verify that JSON objects are written correctly. */
394
395static void
396test_writing_objects ()
397{
398 object obj;
399 obj.set_string (key: "foo", utf8_value: "bar");
400 obj.set_string (key: "baz", utf8_value: "quux");
401 obj.set_string (key: "\"\\\b\f\n\r\t", utf8_value: "value for awkward key");
402
403 /* This test relies on json::object writing out key/value pairs
404 in key-insertion order. */
405 ASSERT_PRINT_EQ (obj, true,
406 "{\"foo\": \"bar\",\n"
407 " \"baz\": \"quux\",\n"
408 " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
409 ASSERT_PRINT_EQ (obj, false,
410 "{\"foo\": \"bar\", \"baz\": \"quux\""
411 ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
412}
413
414/* Verify that JSON arrays are written correctly. */
415
416static void
417test_writing_arrays ()
418{
419 array arr;
420 ASSERT_PRINT_EQ (arr, true, "[]");
421
422 arr.append (v: new json::string ("foo"));
423 ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
424
425 arr.append (v: new json::string ("bar"));
426 ASSERT_PRINT_EQ (arr, true,
427 "[\"foo\",\n"
428 " \"bar\"]");
429 ASSERT_PRINT_EQ (arr, false,
430 "[\"foo\", \"bar\"]");
431}
432
433/* Verify that JSON numbers are written correctly. */
434
435static void
436test_writing_float_numbers ()
437{
438 ASSERT_PRINT_EQ (float_number (0), true, "0");
439 ASSERT_PRINT_EQ (float_number (42), true, "42");
440 ASSERT_PRINT_EQ (float_number (-100), true, "-100");
441 ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
442}
443
444static void
445test_writing_integer_numbers ()
446{
447 ASSERT_PRINT_EQ (integer_number (0), true, "0");
448 ASSERT_PRINT_EQ (integer_number (42), true, "42");
449 ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
450 ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
451 ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
452}
453
454/* Verify that JSON strings are written correctly. */
455
456static void
457test_writing_strings ()
458{
459 string foo ("foo");
460 ASSERT_PRINT_EQ (foo, true, "\"foo\"");
461
462 string contains_quotes ("before \"quoted\" after");
463 ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
464
465 const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
466 string not_terminated (data, 3);
467 ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
468 string embedded_null (data, sizeof data);
469 ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
470}
471
472/* Verify that JSON literals are written correctly. */
473
474static void
475test_writing_literals ()
476{
477 ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
478 ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
479 ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
480
481 ASSERT_PRINT_EQ (literal (true), true, "true");
482 ASSERT_PRINT_EQ (literal (false), true, "false");
483}
484
485/* Verify that nested values are formatted correctly when written. */
486
487static void
488test_formatting ()
489{
490 object obj;
491 object *child = new object;
492 object *grandchild = new object;
493
494 obj.set_string (key: "str", utf8_value: "bar");
495 obj.set (key: "child", v: child);
496 obj.set_integer (key: "int", v: 42);
497
498 child->set (key: "grandchild", v: grandchild);
499 child->set_integer (key: "int", v: 1776);
500
501 array *arr = new array;
502 for (int i = 0; i < 3; i++)
503 arr->append (v: new integer_number (i));
504 grandchild->set (key: "arr", v: arr);
505 grandchild->set_integer (key: "int", v: 1066);
506
507 /* This test relies on json::object writing out key/value pairs
508 in key-insertion order. */
509 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 ASSERT_PRINT_EQ (obj, false,
518 ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
519 " {\"arr\": [0, 1, 2], \"int\": 1066},"
520 " \"int\": 1776}, \"int\": 42}"));
521}
522
523/* Run all of the selftests within this file. */
524
525void
526json_cc_tests ()
527{
528 test_object_get ();
529 test_writing_objects ();
530 test_writing_arrays ();
531 test_writing_float_numbers ();
532 test_writing_integer_numbers ();
533 test_writing_strings ();
534 test_writing_literals ();
535 test_formatting ();
536}
537
538} // namespace selftest
539
540#endif /* #if CHECKING_P */
541

source code of gcc/json.cc