1/* JSON trees
2 Copyright (C) 2017-2023 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/* class json::value. */
32
33/* Dump this json::value tree to OUTF.
34
35 No formatting is done.
36
37 The key/value pairs of json::objects are printed in the order
38 in which the keys were originally inserted. */
39
40void
41value::dump (FILE *outf) const
42{
43 pretty_printer pp;
44 pp_buffer (&pp)->stream = outf;
45 print (pp: &pp);
46 pp_flush (&pp);
47}
48
49/* class json::object, a subclass of json::value, representing
50 an ordered collection of key/value pairs. */
51
52/* json:object's dtor. */
53
54object::~object ()
55{
56 for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
57 {
58 free (ptr: const_cast <char *>((*it).first));
59 delete ((*it).second);
60 }
61}
62
63/* Implementation of json::value::print for json::object. */
64
65void
66object::print (pretty_printer *pp) const
67{
68 pp_character (pp, '{');
69
70 /* Iterate in the order that the keys were inserted. */
71 unsigned i;
72 const char *key;
73 FOR_EACH_VEC_ELT (m_keys, i, key)
74 {
75 if (i > 0)
76 pp_string (pp, ", ");
77 map_t &mut_map = const_cast<map_t &> (m_map);
78 value *value = *mut_map.get (k: key);
79 pp_doublequote (pp);
80 pp_string (pp, key); // FIXME: escaping?
81 pp_doublequote (pp);
82 pp_string (pp, ": ");
83 value->print (pp);
84 }
85 pp_character (pp, '}');
86}
87
88/* Set the json::value * for KEY, taking ownership of V
89 (and taking a copy of KEY if necessary). */
90
91void
92object::set (const char *key, value *v)
93{
94 gcc_assert (key);
95 gcc_assert (v);
96
97 value **ptr = m_map.get (k: key);
98 if (ptr)
99 {
100 /* If the key is already present, delete the existing value
101 and overwrite it. */
102 delete *ptr;
103 *ptr = v;
104 }
105 else
106 {
107 /* If the key wasn't already present, take a copy of the key,
108 and store the value. */
109 char *owned_key = xstrdup (key);
110 m_map.put (k: owned_key, v);
111 m_keys.safe_push (obj: owned_key);
112 }
113}
114
115/* Get the json::value * for KEY.
116
117 The object retains ownership of the value. */
118
119value *
120object::get (const char *key) const
121{
122 gcc_assert (key);
123
124 value **ptr = const_cast <map_t &> (m_map).get (k: key);
125 if (ptr)
126 return *ptr;
127 else
128 return NULL;
129}
130
131/* class json::array, a subclass of json::value, representing
132 an ordered collection of values. */
133
134/* json::array's dtor. */
135
136array::~array ()
137{
138 unsigned i;
139 value *v;
140 FOR_EACH_VEC_ELT (m_elements, i, v)
141 delete v;
142}
143
144/* Implementation of json::value::print for json::array. */
145
146void
147array::print (pretty_printer *pp) const
148{
149 pp_character (pp, '[');
150 unsigned i;
151 value *v;
152 FOR_EACH_VEC_ELT (m_elements, i, v)
153 {
154 if (i)
155 pp_string (pp, ", ");
156 v->print (pp);
157 }
158 pp_character (pp, ']');
159}
160
161/* Append non-NULL value V to a json::array, taking ownership of V. */
162
163void
164array::append (value *v)
165{
166 gcc_assert (v);
167 m_elements.safe_push (obj: v);
168}
169
170/* class json::float_number, a subclass of json::value, wrapping a double. */
171
172/* Implementation of json::value::print for json::float_number. */
173
174void
175float_number::print (pretty_printer *pp) const
176{
177 char tmp[1024];
178 snprintf (s: tmp, maxlen: sizeof (tmp), format: "%g", m_value);
179 pp_string (pp, tmp);
180}
181
182/* class json::integer_number, a subclass of json::value, wrapping a long. */
183
184/* Implementation of json::value::print for json::integer_number. */
185
186void
187integer_number::print (pretty_printer *pp) const
188{
189 char tmp[1024];
190 snprintf (s: tmp, maxlen: sizeof (tmp), format: "%ld", m_value);
191 pp_string (pp, tmp);
192}
193
194
195/* class json::string, a subclass of json::value. */
196
197/* json::string's ctor. */
198
199string::string (const char *utf8)
200{
201 gcc_assert (utf8);
202 m_utf8 = xstrdup (utf8);
203 m_len = strlen (s: utf8);
204}
205
206string::string (const char *utf8, size_t len)
207{
208 gcc_assert (utf8);
209 m_utf8 = XNEWVEC (char, len);
210 m_len = len;
211 memcpy (dest: m_utf8, src: utf8, n: len);
212}
213
214/* Implementation of json::value::print for json::string. */
215
216void
217string::print (pretty_printer *pp) const
218{
219 pp_character (pp, '"');
220 for (size_t i = 0; i != m_len; ++i)
221 {
222 char ch = m_utf8[i];
223 switch (ch)
224 {
225 case '"':
226 pp_string (pp, "\\\"");
227 break;
228 case '\\':
229 pp_string (pp, "\\\\");
230 break;
231 case '\b':
232 pp_string (pp, "\\b");
233 break;
234 case '\f':
235 pp_string (pp, "\\f");
236 break;
237 case '\n':
238 pp_string (pp, "\\n");
239 break;
240 case '\r':
241 pp_string (pp, "\\r");
242 break;
243 case '\t':
244 pp_string (pp, "\\t");
245 break;
246 case '\0':
247 pp_string (pp, "\\0");
248 break;
249 default:
250 pp_character (pp, ch);
251 }
252 }
253 pp_character (pp, '"');
254}
255
256/* class json::literal, a subclass of json::value. */
257
258/* Implementation of json::value::print for json::literal. */
259
260void
261literal::print (pretty_printer *pp) const
262{
263 switch (m_kind)
264 {
265 case JSON_TRUE:
266 pp_string (pp, "true");
267 break;
268 case JSON_FALSE:
269 pp_string (pp, "false");
270 break;
271 case JSON_NULL:
272 pp_string (pp, "null");
273 break;
274 default:
275 gcc_unreachable ();
276 }
277}
278
279
280#if CHECKING_P
281
282namespace selftest {
283
284/* Selftests. */
285
286/* Verify that JV->print () prints EXPECTED_JSON. */
287
288static void
289assert_print_eq (const json::value &jv, const char *expected_json)
290{
291 pretty_printer pp;
292 jv.print (pp: &pp);
293 ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
294}
295
296/* Verify that object::get works as expected. */
297
298static void
299test_object_get ()
300{
301 object obj;
302 value *val = new json::string ("value");
303 obj.set (key: "foo", v: val);
304 ASSERT_EQ (obj.get ("foo"), val);
305 ASSERT_EQ (obj.get ("not-present"), NULL);
306}
307
308/* Verify that JSON objects are written correctly. */
309
310static void
311test_writing_objects ()
312{
313 object obj;
314 obj.set (key: "foo", v: new json::string ("bar"));
315 obj.set (key: "baz", v: new json::string ("quux"));
316 /* This test relies on json::object writing out key/value pairs
317 in key-insertion order. */
318 assert_print_eq (jv: obj, expected_json: "{\"foo\": \"bar\", \"baz\": \"quux\"}");
319}
320
321/* Verify that JSON arrays are written correctly. */
322
323static void
324test_writing_arrays ()
325{
326 array arr;
327 assert_print_eq (jv: arr, expected_json: "[]");
328
329 arr.append (v: new json::string ("foo"));
330 assert_print_eq (jv: arr, expected_json: "[\"foo\"]");
331
332 arr.append (v: new json::string ("bar"));
333 assert_print_eq (jv: arr, expected_json: "[\"foo\", \"bar\"]");
334}
335
336/* Verify that JSON numbers are written correctly. */
337
338static void
339test_writing_float_numbers ()
340{
341 assert_print_eq (jv: float_number (0), expected_json: "0");
342 assert_print_eq (jv: float_number (42), expected_json: "42");
343 assert_print_eq (jv: float_number (-100), expected_json: "-100");
344 assert_print_eq (jv: float_number (123456789), expected_json: "1.23457e+08");
345}
346
347static void
348test_writing_integer_numbers ()
349{
350 assert_print_eq (jv: integer_number (0), expected_json: "0");
351 assert_print_eq (jv: integer_number (42), expected_json: "42");
352 assert_print_eq (jv: integer_number (-100), expected_json: "-100");
353 assert_print_eq (jv: integer_number (123456789), expected_json: "123456789");
354 assert_print_eq (jv: integer_number (-123456789), expected_json: "-123456789");
355}
356
357/* Verify that JSON strings are written correctly. */
358
359static void
360test_writing_strings ()
361{
362 string foo ("foo");
363 assert_print_eq (jv: foo, expected_json: "\"foo\"");
364
365 string contains_quotes ("before \"quoted\" after");
366 assert_print_eq (jv: contains_quotes, expected_json: "\"before \\\"quoted\\\" after\"");
367
368 const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
369 string not_terminated (data, 3);
370 assert_print_eq (jv: not_terminated, expected_json: "\"abc\"");
371 string embedded_null (data, sizeof data);
372 assert_print_eq (jv: embedded_null, expected_json: "\"abcd\\0ef\"");
373}
374
375/* Verify that JSON literals are written correctly. */
376
377static void
378test_writing_literals ()
379{
380 assert_print_eq (jv: literal (JSON_TRUE), expected_json: "true");
381 assert_print_eq (jv: literal (JSON_FALSE), expected_json: "false");
382 assert_print_eq (jv: literal (JSON_NULL), expected_json: "null");
383
384 assert_print_eq (jv: literal (true), expected_json: "true");
385 assert_print_eq (jv: literal (false), expected_json: "false");
386}
387
388/* Run all of the selftests within this file. */
389
390void
391json_cc_tests ()
392{
393 test_object_get ();
394 test_writing_objects ();
395 test_writing_arrays ();
396 test_writing_float_numbers ();
397 test_writing_integer_numbers ();
398 test_writing_strings ();
399 test_writing_literals ();
400}
401
402} // namespace selftest
403
404#endif /* #if CHECKING_P */
405

source code of gcc/json.cc