]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/json.cc
combine: Fix ICE in try_combine on pr112494.c [PR112560]
[thirdparty/gcc.git] / gcc / json.cc
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 print_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
84 void
85 value::dump (FILE *outf, bool formatted) const
86 {
87 pretty_printer pp;
88 pp_buffer (&pp)->stream = outf;
89 print (&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
98 object::~object ()
99 {
100 for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
101 {
102 free (const_cast <char *>((*it).first));
103 delete ((*it).second);
104 }
105 }
106
107 /* Implementation of json::value::print for json::object. */
108
109 void
110 object::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 (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;
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
151 void
152 object::set (const char *key, value *v)
153 {
154 gcc_assert (key);
155 gcc_assert (v);
156
157 value **ptr = m_map.get (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 (owned_key, v);
171 m_keys.safe_push (owned_key);
172 }
173 }
174
175 /* Get the json::value * for KEY.
176
177 The object retains ownership of the value. */
178
179 value *
180 object::get (const char *key) const
181 {
182 gcc_assert (key);
183
184 value **ptr = const_cast <map_t &> (m_map).get (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
194 void
195 object::set_string (const char *key, const char *utf8_value)
196 {
197 set (key, 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
203 void
204 object::set_integer (const char *key, long v)
205 {
206 set (key, 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
212 void
213 object::set_float (const char *key, double v)
214 {
215 set (key, 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
221 void
222 object::set_bool (const char *key, bool v)
223 {
224 set (key, 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
232 array::~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
242 void
243 array::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
272 void
273 array::append (value *v)
274 {
275 gcc_assert (v);
276 m_elements.safe_push (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
283 void
284 float_number::print (pretty_printer *pp,
285 bool formatted ATTRIBUTE_UNUSED) const
286 {
287 char tmp[1024];
288 snprintf (tmp, sizeof (tmp), "%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
296 void
297 integer_number::print (pretty_printer *pp,
298 bool formatted ATTRIBUTE_UNUSED) const
299 {
300 char tmp[1024];
301 snprintf (tmp, sizeof (tmp), "%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
310 string::string (const char *utf8)
311 {
312 gcc_assert (utf8);
313 m_utf8 = xstrdup (utf8);
314 m_len = strlen (utf8);
315 }
316
317 string::string (const char *utf8, size_t len)
318 {
319 gcc_assert (utf8);
320 m_utf8 = XNEWVEC (char, len);
321 m_len = len;
322 memcpy (m_utf8, utf8, len);
323 }
324
325 /* Implementation of json::value::print for json::string. */
326
327 void
328 string::print (pretty_printer *pp,
329 bool formatted ATTRIBUTE_UNUSED) const
330 {
331 print_escaped_json_string (pp, m_utf8, m_len);
332 }
333
334 /* class json::literal, a subclass of json::value. */
335
336 /* Implementation of json::value::print for json::literal. */
337
338 void
339 literal::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 \f
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 assert_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, 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
383 static void
384 test_object_get ()
385 {
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);
391 }
392
393 /* Verify that JSON objects are written correctly. */
394
395 static void
396 test_writing_objects ()
397 {
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");
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
416 static void
417 test_writing_arrays ()
418 {
419 array arr;
420 ASSERT_PRINT_EQ (arr, true, "[]");
421
422 arr.append (new json::string ("foo"));
423 ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
424
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\"]");
431 }
432
433 /* Verify that JSON numbers are written correctly. */
434
435 static void
436 test_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
444 static void
445 test_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
456 static void
457 test_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
474 static void
475 test_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
487 static void
488 test_formatting ()
489 {
490 object obj;
491 object *child = new object;
492 object *grandchild = new object;
493
494 obj.set_string ("str", "bar");
495 obj.set ("child", child);
496 obj.set_integer ("int", 42);
497
498 child->set ("grandchild", grandchild);
499 child->set_integer ("int", 1776);
500
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);
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
525 void
526 json_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 */