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