libcpp, c, middle-end: Optimize initializers using #embed in C
[official-gcc.git] / gcc / json.cc
blob23c8bf1cad396384249ac412999402c1d87b7caf
1 /* JSON trees
2 Copyright (C) 2017-2024 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 #define INCLUDE_MEMORY
23 #include "system.h"
24 #include "coretypes.h"
25 #include "json.h"
26 #include "pretty-print.h"
27 #include "math.h"
28 #include "make-unique.h"
29 #include "selftest.h"
31 using namespace json;
33 /* Print a JSON string to PP, escaping '"', control characters,
34 and embedded null bytes.
35 The string is required to be UTF-8 encoded. */
37 static void
38 print_escaped_json_string (pretty_printer *pp,
39 const char *utf8_str,
40 size_t len)
42 pp_character (pp, '"');
43 for (size_t i = 0; i != len; ++i)
45 char ch = utf8_str[i];
46 switch (ch)
48 case '"':
49 pp_string (pp, "\\\"");
50 break;
51 case '\\':
52 pp_string (pp, "\\\\");
53 break;
54 case '\b':
55 pp_string (pp, "\\b");
56 break;
57 case '\f':
58 pp_string (pp, "\\f");
59 break;
60 case '\n':
61 pp_string (pp, "\\n");
62 break;
63 case '\r':
64 pp_string (pp, "\\r");
65 break;
66 case '\t':
67 pp_string (pp, "\\t");
68 break;
69 case '\0':
70 pp_string (pp, "\\0");
71 break;
72 default:
73 pp_character (pp, ch);
76 pp_character (pp, '"');
79 /* class json::value. */
81 /* Dump this json::value tree to OUTF.
83 The key/value pairs of json::objects are printed in the order
84 in which the keys were originally inserted. */
86 void
87 value::dump (FILE *outf, bool formatted) const
89 pretty_printer pp;
90 pp_buffer (&pp)->m_stream = outf;
91 print (&pp, formatted);
92 pp_flush (&pp);
95 /* A convenience function for debugging.
96 Dump to stderr with formatting, and a trailing newline. */
98 void
99 value::dump () const
101 dump (stderr, true);
102 fprintf (stderr, "\n");
105 /* class json::object, a subclass of json::value, representing
106 an ordered collection of key/value pairs. */
108 /* json:object's dtor. */
110 object::~object ()
112 for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
114 free (const_cast <char *>((*it).first));
115 delete ((*it).second);
119 /* Implementation of json::value::print for json::object. */
121 void
122 object::print (pretty_printer *pp, bool formatted) const
124 pp_character (pp, '{');
125 if (formatted)
126 pp_indentation (pp) += 1;
128 /* Iterate in the order that the keys were inserted. */
129 unsigned i;
130 const char *key;
131 FOR_EACH_VEC_ELT (m_keys, i, key)
133 if (i > 0)
135 pp_string (pp, ",");
136 if (formatted)
138 pp_newline (pp);
139 pp_indent (pp);
141 else
142 pp_space (pp);
144 map_t &mut_map = const_cast<map_t &> (m_map);
145 value *value = *mut_map.get (key);
146 print_escaped_json_string (pp, key, strlen (key));
147 pp_string (pp, ": ");
148 const int indent = strlen (key) + 4;
149 if (formatted)
150 pp_indentation (pp) += indent;
151 value->print (pp, formatted);
152 if (formatted)
153 pp_indentation (pp) -= indent;
155 if (formatted)
156 pp_indentation (pp) -= 1;
157 pp_character (pp, '}');
160 /* Set the json::value * for KEY, taking ownership of V
161 (and taking a copy of KEY if necessary). */
163 void
164 object::set (const char *key, value *v)
166 gcc_assert (key);
167 gcc_assert (v);
169 value **ptr = m_map.get (key);
170 if (ptr)
172 /* If the key is already present, delete the existing value
173 and overwrite it. */
174 delete *ptr;
175 *ptr = v;
177 else
179 /* If the key wasn't already present, take a copy of the key,
180 and store the value. */
181 char *owned_key = xstrdup (key);
182 m_map.put (owned_key, v);
183 m_keys.safe_push (owned_key);
187 /* Get the json::value * for KEY.
189 The object retains ownership of the value. */
191 value *
192 object::get (const char *key) const
194 gcc_assert (key);
196 value **ptr = const_cast <map_t &> (m_map).get (key);
197 if (ptr)
198 return *ptr;
199 else
200 return NULL;
203 /* Set value of KEY within this object to a JSON
204 string value based on UTF8_VALUE. */
206 void
207 object::set_string (const char *key, const char *utf8_value)
209 set (key, new json::string (utf8_value));
212 /* Set value of KEY within this object to a JSON
213 integer value based on V. */
215 void
216 object::set_integer (const char *key, long v)
218 set (key, new json::integer_number (v));
221 /* Set value of KEY within this object to a JSON
222 floating point value based on V. */
224 void
225 object::set_float (const char *key, double v)
227 set (key, new json::float_number (v));
230 /* Set value of KEY within this object to the JSON
231 literal true or false, based on V. */
233 void
234 object::set_bool (const char *key, bool v)
236 set (key, new json::literal (v));
239 /* class json::array, a subclass of json::value, representing
240 an ordered collection of values. */
242 /* json::array's dtor. */
244 array::~array ()
246 unsigned i;
247 value *v;
248 FOR_EACH_VEC_ELT (m_elements, i, v)
249 delete v;
252 /* Implementation of json::value::print for json::array. */
254 void
255 array::print (pretty_printer *pp, bool formatted) const
257 pp_character (pp, '[');
258 if (formatted)
259 pp_indentation (pp) += 1;
260 unsigned i;
261 value *v;
262 FOR_EACH_VEC_ELT (m_elements, i, v)
264 if (i)
266 pp_string (pp, ",");
267 if (formatted)
269 pp_newline (pp);
270 pp_indent (pp);
272 else
273 pp_space (pp);
275 v->print (pp, formatted);
277 if (formatted)
278 pp_indentation (pp) -= 1;
279 pp_character (pp, ']');
282 /* Append non-NULL value V to a json::array, taking ownership of V. */
284 void
285 array::append (value *v)
287 gcc_assert (v);
288 m_elements.safe_push (v);
291 void
292 array::append_string (const char *utf8_value)
294 gcc_assert (utf8_value);
295 append (new json::string (utf8_value));
298 /* class json::float_number, a subclass of json::value, wrapping a double. */
300 /* Implementation of json::value::print for json::float_number. */
302 void
303 float_number::print (pretty_printer *pp,
304 bool formatted ATTRIBUTE_UNUSED) const
306 char tmp[1024];
307 snprintf (tmp, sizeof (tmp), "%g", m_value);
308 pp_string (pp, tmp);
311 /* class json::integer_number, a subclass of json::value, wrapping a long. */
313 /* Implementation of json::value::print for json::integer_number. */
315 void
316 integer_number::print (pretty_printer *pp,
317 bool formatted ATTRIBUTE_UNUSED) const
319 char tmp[1024];
320 snprintf (tmp, sizeof (tmp), "%ld", m_value);
321 pp_string (pp, tmp);
325 /* class json::string, a subclass of json::value. */
327 /* json::string's ctor. */
329 string::string (const char *utf8)
331 gcc_assert (utf8);
332 m_utf8 = xstrdup (utf8);
333 m_len = strlen (utf8);
336 string::string (const char *utf8, size_t len)
338 gcc_assert (utf8);
339 m_utf8 = XNEWVEC (char, len);
340 m_len = len;
341 memcpy (m_utf8, utf8, len);
344 /* Implementation of json::value::print for json::string. */
346 void
347 string::print (pretty_printer *pp,
348 bool formatted ATTRIBUTE_UNUSED) const
350 print_escaped_json_string (pp, m_utf8, m_len);
353 /* class json::literal, a subclass of json::value. */
355 /* Implementation of json::value::print for json::literal. */
357 void
358 literal::print (pretty_printer *pp,
359 bool formatted ATTRIBUTE_UNUSED) const
361 switch (m_kind)
363 case JSON_TRUE:
364 pp_string (pp, "true");
365 break;
366 case JSON_FALSE:
367 pp_string (pp, "false");
368 break;
369 case JSON_NULL:
370 pp_string (pp, "null");
371 break;
372 default:
373 gcc_unreachable ();
378 #if CHECKING_P
380 namespace selftest {
382 /* Selftests. */
384 /* Verify that JV->print () prints EXPECTED_JSON. */
386 static void
387 assert_print_eq (const location &loc,
388 const json::value &jv,
389 bool formatted,
390 const char *expected_json)
392 pretty_printer pp;
393 jv.print (&pp, formatted);
394 ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
397 #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
398 assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
400 /* Verify that object::get works as expected. */
402 static void
403 test_object_get ()
405 object obj;
406 value *val = new json::string ("value");
407 obj.set ("foo", val);
408 ASSERT_EQ (obj.get ("foo"), val);
409 ASSERT_EQ (obj.get ("not-present"), NULL);
412 /* Verify that JSON objects are written correctly. */
414 static void
415 test_writing_objects ()
417 object obj;
418 obj.set_string ("foo", "bar");
419 obj.set_string ("baz", "quux");
420 obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
422 /* This test relies on json::object writing out key/value pairs
423 in key-insertion order. */
424 ASSERT_PRINT_EQ (obj, true,
425 "{\"foo\": \"bar\",\n"
426 " \"baz\": \"quux\",\n"
427 " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
428 ASSERT_PRINT_EQ (obj, false,
429 "{\"foo\": \"bar\", \"baz\": \"quux\""
430 ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
433 /* Verify that JSON arrays are written correctly. */
435 static void
436 test_writing_arrays ()
438 array arr;
439 ASSERT_PRINT_EQ (arr, true, "[]");
441 arr.append (new json::string ("foo"));
442 ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
444 arr.append_string ("bar");
445 ASSERT_PRINT_EQ (arr, true,
446 "[\"foo\",\n"
447 " \"bar\"]");
448 ASSERT_PRINT_EQ (arr, false,
449 "[\"foo\", \"bar\"]");
452 /* Verify that JSON numbers are written correctly. */
454 static void
455 test_writing_float_numbers ()
457 ASSERT_PRINT_EQ (float_number (0), true, "0");
458 ASSERT_PRINT_EQ (float_number (42), true, "42");
459 ASSERT_PRINT_EQ (float_number (-100), true, "-100");
460 ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
463 static void
464 test_writing_integer_numbers ()
466 ASSERT_PRINT_EQ (integer_number (0), true, "0");
467 ASSERT_PRINT_EQ (integer_number (42), true, "42");
468 ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
469 ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
470 ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
473 /* Verify that JSON strings are written correctly. */
475 static void
476 test_writing_strings ()
478 string foo ("foo");
479 ASSERT_PRINT_EQ (foo, true, "\"foo\"");
481 string contains_quotes ("before \"quoted\" after");
482 ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
484 const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
485 string not_terminated (data, 3);
486 ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
487 string embedded_null (data, sizeof data);
488 ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
491 /* Verify that JSON literals are written correctly. */
493 static void
494 test_writing_literals ()
496 ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
497 ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
498 ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
500 ASSERT_PRINT_EQ (literal (true), true, "true");
501 ASSERT_PRINT_EQ (literal (false), true, "false");
504 /* Verify that nested values are formatted correctly when written.
506 Also, make use of array::append(std::unique_ptr<value>) and
507 object::set (const char *key, std::unique_ptr<value> v).*/
509 static void
510 test_formatting ()
512 object obj;
513 object *child = new object;
514 std::unique_ptr<object> grandchild = ::make_unique<object> ();
516 obj.set_string ("str", "bar");
517 obj.set ("child", child);
518 obj.set_integer ("int", 42);
520 array *arr = new array;
521 for (int i = 0; i < 3; i++)
522 arr->append (::make_unique<integer_number> (i));
523 grandchild->set ("arr", arr);
524 grandchild->set_integer ("int", 1066);
526 child->set ("grandchild", std::move (grandchild));
527 child->set_integer ("int", 1776);
529 /* This test relies on json::object writing out key/value pairs
530 in key-insertion order. */
531 ASSERT_PRINT_EQ (obj, true,
532 ("{\"str\": \"bar\",\n"
533 " \"child\": {\"grandchild\": {\"arr\": [0,\n"
534 " 1,\n"
535 " 2],\n"
536 " \"int\": 1066},\n"
537 " \"int\": 1776},\n"
538 " \"int\": 42}"));
539 ASSERT_PRINT_EQ (obj, false,
540 ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
541 " {\"arr\": [0, 1, 2], \"int\": 1066},"
542 " \"int\": 1776}, \"int\": 42}"));
545 /* Run all of the selftests within this file. */
547 void
548 json_cc_tests ()
550 test_object_get ();
551 test_writing_objects ();
552 test_writing_arrays ();
553 test_writing_float_numbers ();
554 test_writing_integer_numbers ();
555 test_writing_strings ();
556 test_writing_literals ();
557 test_formatting ();
560 } // namespace selftest
562 #endif /* #if CHECKING_P */