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