]>
Commit | Line | Data |
---|---|---|
9dcf2a11 | 1 | /* JSON trees |
fbd26352 | 2 | Copyright (C) 2017-2019 Free Software Foundation, Inc. |
9dcf2a11 | 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 | ||
e4aaf333 | 79 | /* Set the json::value * for KEY, taking ownership of V |
9dcf2a11 | 80 | (and taking a copy of KEY if necessary). */ |
81 | ||
82 | void | |
83 | object::set (const char *key, value *v) | |
84 | { | |
e4aaf333 | 85 | gcc_assert (key); |
86 | gcc_assert (v); | |
87 | ||
9dcf2a11 | 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 | ||
2593ab36 | 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 | ||
9dcf2a11 | 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 | ||
e4aaf333 | 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 | ||
9dcf2a11 | 157 | /* class json::number, a subclass of json::value, wrapping a double. */ |
158 | ||
159 | /* Implementation of json::value::print for json::number. */ | |
160 | ||
161 | void | |
162 | number::print (pretty_printer *pp) const | |
163 | { | |
164 | char tmp[1024]; | |
165 | snprintf (tmp, sizeof (tmp), "%g", m_value); | |
166 | pp_string (pp, tmp); | |
167 | } | |
168 | ||
169 | /* class json::string, a subclass of json::value. */ | |
170 | ||
e4aaf333 | 171 | /* json::string's ctor. */ |
172 | ||
173 | string::string (const char *utf8) | |
174 | { | |
175 | gcc_assert (utf8); | |
176 | m_utf8 = xstrdup (utf8); | |
177 | } | |
178 | ||
179 | /* Implementation of json::value::print for json::string. */ | |
180 | ||
9dcf2a11 | 181 | void |
182 | string::print (pretty_printer *pp) const | |
183 | { | |
184 | pp_character (pp, '"'); | |
185 | for (const char *ptr = m_utf8; *ptr; ptr++) | |
186 | { | |
187 | char ch = *ptr; | |
188 | switch (ch) | |
189 | { | |
190 | case '"': | |
191 | pp_string (pp, "\\\""); | |
192 | break; | |
193 | case '\\': | |
194 | pp_string (pp, "\\n"); | |
195 | break; | |
196 | case '\b': | |
197 | pp_string (pp, "\\b"); | |
198 | break; | |
199 | case '\f': | |
200 | pp_string (pp, "\\f"); | |
201 | break; | |
202 | case '\n': | |
203 | pp_string (pp, "\\n"); | |
204 | break; | |
205 | case '\r': | |
206 | pp_string (pp, "\\r"); | |
207 | break; | |
208 | case '\t': | |
209 | pp_string (pp, "\\t"); | |
210 | break; | |
211 | ||
212 | default: | |
213 | pp_character (pp, ch); | |
214 | } | |
215 | } | |
216 | pp_character (pp, '"'); | |
217 | } | |
218 | ||
219 | /* class json::literal, a subclass of json::value. */ | |
220 | ||
221 | /* Implementation of json::value::print for json::literal. */ | |
222 | ||
223 | void | |
224 | literal::print (pretty_printer *pp) const | |
225 | { | |
226 | switch (m_kind) | |
227 | { | |
228 | case JSON_TRUE: | |
229 | pp_string (pp, "true"); | |
230 | break; | |
231 | case JSON_FALSE: | |
232 | pp_string (pp, "false"); | |
233 | break; | |
234 | case JSON_NULL: | |
235 | pp_string (pp, "null"); | |
236 | break; | |
237 | default: | |
238 | gcc_unreachable (); | |
239 | } | |
240 | } | |
241 | ||
242 | \f | |
243 | #if CHECKING_P | |
244 | ||
245 | namespace selftest { | |
246 | ||
247 | /* Selftests. */ | |
248 | ||
249 | /* Verify that JV->print () prints EXPECTED_JSON. */ | |
250 | ||
251 | static void | |
252 | assert_print_eq (const json::value &jv, const char *expected_json) | |
253 | { | |
254 | pretty_printer pp; | |
255 | jv.print (&pp); | |
256 | ASSERT_STREQ (expected_json, pp_formatted_text (&pp)); | |
257 | } | |
258 | ||
2593ab36 | 259 | /* Verify that object::get works as expected. */ |
260 | ||
261 | static void | |
262 | test_object_get () | |
263 | { | |
264 | object obj; | |
265 | value *val = new json::string ("value"); | |
266 | obj.set ("foo", val); | |
267 | ASSERT_EQ (obj.get ("foo"), val); | |
268 | ASSERT_EQ (obj.get ("not-present"), NULL); | |
269 | } | |
270 | ||
9dcf2a11 | 271 | /* Verify that JSON objects are written correctly. We can't test more than |
272 | one key/value pair, as we don't impose a guaranteed ordering. */ | |
273 | ||
274 | static void | |
275 | test_writing_objects () | |
276 | { | |
277 | object obj; | |
278 | obj.set ("foo", new json::string ("bar")); | |
279 | assert_print_eq (obj, "{\"foo\": \"bar\"}"); | |
280 | } | |
281 | ||
282 | /* Verify that JSON arrays are written correctly. */ | |
283 | ||
284 | static void | |
285 | test_writing_arrays () | |
286 | { | |
287 | array arr; | |
288 | assert_print_eq (arr, "[]"); | |
289 | ||
290 | arr.append (new json::string ("foo")); | |
291 | assert_print_eq (arr, "[\"foo\"]"); | |
292 | ||
293 | arr.append (new json::string ("bar")); | |
294 | assert_print_eq (arr, "[\"foo\", \"bar\"]"); | |
295 | } | |
296 | ||
297 | /* Verify that JSON numbers are written correctly. */ | |
298 | ||
299 | static void | |
300 | test_writing_numbers () | |
301 | { | |
302 | assert_print_eq (number (0), "0"); | |
303 | assert_print_eq (number (42), "42"); | |
304 | assert_print_eq (number (-100), "-100"); | |
305 | } | |
306 | ||
307 | /* Verify that JSON strings are written correctly. */ | |
308 | ||
309 | static void | |
310 | test_writing_strings () | |
311 | { | |
312 | string foo ("foo"); | |
313 | assert_print_eq (foo, "\"foo\""); | |
314 | ||
315 | string contains_quotes ("before \"quoted\" after"); | |
316 | assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\""); | |
317 | } | |
318 | ||
4cd96a00 | 319 | /* Verify that JSON literals are written correctly. */ |
9dcf2a11 | 320 | |
321 | static void | |
322 | test_writing_literals () | |
323 | { | |
324 | assert_print_eq (literal (JSON_TRUE), "true"); | |
325 | assert_print_eq (literal (JSON_FALSE), "false"); | |
326 | assert_print_eq (literal (JSON_NULL), "null"); | |
8d11df62 | 327 | |
328 | assert_print_eq (literal (true), "true"); | |
329 | assert_print_eq (literal (false), "false"); | |
9dcf2a11 | 330 | } |
331 | ||
332 | /* Run all of the selftests within this file. */ | |
333 | ||
334 | void | |
335 | json_cc_tests () | |
336 | { | |
2593ab36 | 337 | test_object_get (); |
9dcf2a11 | 338 | test_writing_objects (); |
339 | test_writing_arrays (); | |
340 | test_writing_numbers (); | |
341 | test_writing_strings (); | |
342 | test_writing_literals (); | |
343 | } | |
344 | ||
345 | } // namespace selftest | |
346 | ||
347 | #endif /* #if CHECKING_P */ |