testsuite: AIX csect section name.
[official-gcc.git] / gcc / json.cc
blob350917af5df1dcb0c5bdd3b2accffe9e738ccfb3
1 /* JSON trees
2 Copyright (C) 2017-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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.
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.
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/>. */
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"
29 using namespace json;
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. */
35 static void
36 print_escaped_json_string (pretty_printer *pp,
37 const char *utf8_str,
38 size_t len)
40 pp_character (pp, '"');
41 for (size_t i = 0; i != len; ++i)
43 char ch = utf8_str[i];
44 switch (ch)
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);
74 pp_character (pp, '"');
77 /* class json::value. */
79 /* Dump this json::value tree to OUTF.
81 The key/value pairs of json::objects are printed in the order
82 in which the keys were originally inserted. */
84 void
85 value::dump (FILE *outf, bool formatted) const
87 pretty_printer pp;
88 pp_buffer (&pp)->stream = outf;
89 print (&pp, formatted);
90 pp_flush (&pp);
93 /* class json::object, a subclass of json::value, representing
94 an ordered collection of key/value pairs. */
96 /* json:object's dtor. */
98 object::~object ()
100 for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
102 free (const_cast <char *>((*it).first));
103 delete ((*it).second);
107 /* Implementation of json::value::print for json::object. */
109 void
110 object::print (pretty_printer *pp, bool formatted) const
112 pp_character (pp, '{');
113 if (formatted)
114 pp_indentation (pp) += 1;
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)
121 if (i > 0)
123 pp_string (pp, ",");
124 if (formatted)
126 pp_newline (pp);
127 pp_indent (pp);
129 else
130 pp_space (pp);
132 map_t &mut_map = const_cast<map_t &> (m_map);
133 value *value = *mut_map.get (key);
134 print_escaped_json_string (pp, key, strlen (key));
135 pp_string (pp, ": ");
136 const int indent = strlen (key) + 4;
137 if (formatted)
138 pp_indentation (pp) += indent;
139 value->print (pp, formatted);
140 if (formatted)
141 pp_indentation (pp) -= indent;
143 if (formatted)
144 pp_indentation (pp) -= 1;
145 pp_character (pp, '}');
148 /* Set the json::value * for KEY, taking ownership of V
149 (and taking a copy of KEY if necessary). */
151 void
152 object::set (const char *key, value *v)
154 gcc_assert (key);
155 gcc_assert (v);
157 value **ptr = m_map.get (key);
158 if (ptr)
160 /* If the key is already present, delete the existing value
161 and overwrite it. */
162 delete *ptr;
163 *ptr = v;
165 else
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 (owned_key, v);
171 m_keys.safe_push (owned_key);
175 /* Get the json::value * for KEY.
177 The object retains ownership of the value. */
179 value *
180 object::get (const char *key) const
182 gcc_assert (key);
184 value **ptr = const_cast <map_t &> (m_map).get (key);
185 if (ptr)
186 return *ptr;
187 else
188 return NULL;
191 /* Set value of KEY within this object to a JSON
192 string value based on UTF8_VALUE. */
194 void
195 object::set_string (const char *key, const char *utf8_value)
197 set (key, new json::string (utf8_value));
200 /* Set value of KEY within this object to a JSON
201 integer value based on V. */
203 void
204 object::set_integer (const char *key, long v)
206 set (key, new json::integer_number (v));
209 /* Set value of KEY within this object to a JSON
210 floating point value based on V. */
212 void
213 object::set_float (const char *key, double v)
215 set (key, new json::float_number (v));
218 /* Set value of KEY within this object to the JSON
219 literal true or false, based on V. */
221 void
222 object::set_bool (const char *key, bool v)
224 set (key, new json::literal (v));
227 /* class json::array, a subclass of json::value, representing
228 an ordered collection of values. */
230 /* json::array's dtor. */
232 array::~array ()
234 unsigned i;
235 value *v;
236 FOR_EACH_VEC_ELT (m_elements, i, v)
237 delete v;
240 /* Implementation of json::value::print for json::array. */
242 void
243 array::print (pretty_printer *pp, bool formatted) const
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)
252 if (i)
254 pp_string (pp, ",");
255 if (formatted)
257 pp_newline (pp);
258 pp_indent (pp);
260 else
261 pp_space (pp);
263 v->print (pp, formatted);
265 if (formatted)
266 pp_indentation (pp) -= 1;
267 pp_character (pp, ']');
270 /* Append non-NULL value V to a json::array, taking ownership of V. */
272 void
273 array::append (value *v)
275 gcc_assert (v);
276 m_elements.safe_push (v);
279 /* class json::float_number, a subclass of json::value, wrapping a double. */
281 /* Implementation of json::value::print for json::float_number. */
283 void
284 float_number::print (pretty_printer *pp,
285 bool formatted ATTRIBUTE_UNUSED) const
287 char tmp[1024];
288 snprintf (tmp, sizeof (tmp), "%g", m_value);
289 pp_string (pp, tmp);
292 /* class json::integer_number, a subclass of json::value, wrapping a long. */
294 /* Implementation of json::value::print for json::integer_number. */
296 void
297 integer_number::print (pretty_printer *pp,
298 bool formatted ATTRIBUTE_UNUSED) const
300 char tmp[1024];
301 snprintf (tmp, sizeof (tmp), "%ld", m_value);
302 pp_string (pp, tmp);
306 /* class json::string, a subclass of json::value. */
308 /* json::string's ctor. */
310 string::string (const char *utf8)
312 gcc_assert (utf8);
313 m_utf8 = xstrdup (utf8);
314 m_len = strlen (utf8);
317 string::string (const char *utf8, size_t len)
319 gcc_assert (utf8);
320 m_utf8 = XNEWVEC (char, len);
321 m_len = len;
322 memcpy (m_utf8, utf8, len);
325 /* Implementation of json::value::print for json::string. */
327 void
328 string::print (pretty_printer *pp,
329 bool formatted ATTRIBUTE_UNUSED) const
331 print_escaped_json_string (pp, m_utf8, m_len);
334 /* class json::literal, a subclass of json::value. */
336 /* Implementation of json::value::print for json::literal. */
338 void
339 literal::print (pretty_printer *pp,
340 bool formatted ATTRIBUTE_UNUSED) const
342 switch (m_kind)
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 ();
359 #if CHECKING_P
361 namespace selftest {
363 /* Selftests. */
365 /* Verify that JV->print () prints EXPECTED_JSON. */
367 static void
368 assert_print_eq (const location &loc,
369 const json::value &jv,
370 bool formatted,
371 const char *expected_json)
373 pretty_printer pp;
374 jv.print (&pp, formatted);
375 ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
378 #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
379 assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
381 /* Verify that object::get works as expected. */
383 static void
384 test_object_get ()
386 object obj;
387 value *val = new json::string ("value");
388 obj.set ("foo", val);
389 ASSERT_EQ (obj.get ("foo"), val);
390 ASSERT_EQ (obj.get ("not-present"), NULL);
393 /* Verify that JSON objects are written correctly. */
395 static void
396 test_writing_objects ()
398 object obj;
399 obj.set_string ("foo", "bar");
400 obj.set_string ("baz", "quux");
401 obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
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\"}");
414 /* Verify that JSON arrays are written correctly. */
416 static void
417 test_writing_arrays ()
419 array arr;
420 ASSERT_PRINT_EQ (arr, true, "[]");
422 arr.append (new json::string ("foo"));
423 ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
425 arr.append (new json::string ("bar"));
426 ASSERT_PRINT_EQ (arr, true,
427 "[\"foo\",\n"
428 " \"bar\"]");
429 ASSERT_PRINT_EQ (arr, false,
430 "[\"foo\", \"bar\"]");
433 /* Verify that JSON numbers are written correctly. */
435 static void
436 test_writing_float_numbers ()
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");
444 static void
445 test_writing_integer_numbers ()
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");
454 /* Verify that JSON strings are written correctly. */
456 static void
457 test_writing_strings ()
459 string foo ("foo");
460 ASSERT_PRINT_EQ (foo, true, "\"foo\"");
462 string contains_quotes ("before \"quoted\" after");
463 ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
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\"");
472 /* Verify that JSON literals are written correctly. */
474 static void
475 test_writing_literals ()
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");
481 ASSERT_PRINT_EQ (literal (true), true, "true");
482 ASSERT_PRINT_EQ (literal (false), true, "false");
485 /* Verify that nested values are formatted correctly when written. */
487 static void
488 test_formatting ()
490 object obj;
491 object *child = new object;
492 object *grandchild = new object;
494 obj.set_string ("str", "bar");
495 obj.set ("child", child);
496 obj.set_integer ("int", 42);
498 child->set ("grandchild", grandchild);
499 child->set_integer ("int", 1776);
501 array *arr = new array;
502 for (int i = 0; i < 3; i++)
503 arr->append (new integer_number (i));
504 grandchild->set ("arr", arr);
505 grandchild->set_integer ("int", 1066);
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}"));
523 /* Run all of the selftests within this file. */
525 void
526 json_cc_tests ()
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 ();
538 } // namespace selftest
540 #endif /* #if CHECKING_P */