1 | /* JSON trees |
2 | Copyright (C) 2017-2023 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 | /* 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 | |
40 | void |
41 | value::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 | |
54 | object::~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 | |
65 | void |
66 | object::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 | |
91 | void |
92 | object::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 | |
119 | value * |
120 | object::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 | |
136 | array::~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 | |
146 | void |
147 | array::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 | |
163 | void |
164 | array::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 | |
174 | void |
175 | float_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 | |
186 | void |
187 | integer_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 | |
199 | string::string (const char *utf8) |
200 | { |
201 | gcc_assert (utf8); |
202 | m_utf8 = xstrdup (utf8); |
203 | m_len = strlen (s: utf8); |
204 | } |
205 | |
206 | string::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 | |
216 | void |
217 | string::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 | |
260 | void |
261 | literal::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 | |
282 | namespace selftest { |
283 | |
284 | /* Selftests. */ |
285 | |
286 | /* Verify that JV->print () prints EXPECTED_JSON. */ |
287 | |
288 | static void |
289 | assert_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 | |
298 | static void |
299 | test_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 | |
310 | static void |
311 | test_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 | |
323 | static void |
324 | test_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 | |
338 | static void |
339 | test_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 | |
347 | static void |
348 | test_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 | |
359 | static void |
360 | test_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 | |
377 | static void |
378 | test_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 | |
390 | void |
391 | json_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 | |