| /* JSON trees |
| Copyright (C) 2017-2022 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "json.h" |
| #include "pretty-print.h" |
| #include "math.h" |
| #include "selftest.h" |
| |
| using namespace json; |
| |
| /* class json::value. */ |
| |
| /* Dump this json::value tree to OUTF. |
| No formatting is done. There are no guarantees about the order |
| in which the key/value pairs of json::objects are printed. */ |
| |
| void |
| value::dump (FILE *outf) const |
| { |
| pretty_printer pp; |
| pp_buffer (&pp)->stream = outf; |
| print (&pp); |
| pp_flush (&pp); |
| } |
| |
| /* class json::object, a subclass of json::value, representing |
| an unordered collection of key/value pairs. */ |
| |
| /* json:object's dtor. */ |
| |
| object::~object () |
| { |
| for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) |
| { |
| free (const_cast <char *>((*it).first)); |
| delete ((*it).second); |
| } |
| } |
| |
| /* Implementation of json::value::print for json::object. */ |
| |
| void |
| object::print (pretty_printer *pp) const |
| { |
| /* Note that the order is not guaranteed. */ |
| pp_character (pp, '{'); |
| for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) |
| { |
| if (it != m_map.begin ()) |
| pp_string (pp, ", "); |
| const char *key = const_cast <char *>((*it).first); |
| value *value = (*it).second; |
| pp_doublequote (pp); |
| pp_string (pp, key); // FIXME: escaping? |
| pp_doublequote (pp); |
| pp_string (pp, ": "); |
| value->print (pp); |
| } |
| pp_character (pp, '}'); |
| } |
| |
| /* Set the json::value * for KEY, taking ownership of V |
| (and taking a copy of KEY if necessary). */ |
| |
| void |
| object::set (const char *key, value *v) |
| { |
| gcc_assert (key); |
| gcc_assert (v); |
| |
| value **ptr = m_map.get (key); |
| if (ptr) |
| { |
| /* If the key is already present, delete the existing value |
| and overwrite it. */ |
| delete *ptr; |
| *ptr = v; |
| } |
| else |
| /* If the key wasn't already present, take a copy of the key, |
| and store the value. */ |
| m_map.put (xstrdup (key), v); |
| } |
| |
| /* Get the json::value * for KEY. |
| |
| The object retains ownership of the value. */ |
| |
| value * |
| object::get (const char *key) const |
| { |
| gcc_assert (key); |
| |
| value **ptr = const_cast <map_t &> (m_map).get (key); |
| if (ptr) |
| return *ptr; |
| else |
| return NULL; |
| } |
| |
| /* class json::array, a subclass of json::value, representing |
| an ordered collection of values. */ |
| |
| /* json::array's dtor. */ |
| |
| array::~array () |
| { |
| unsigned i; |
| value *v; |
| FOR_EACH_VEC_ELT (m_elements, i, v) |
| delete v; |
| } |
| |
| /* Implementation of json::value::print for json::array. */ |
| |
| void |
| array::print (pretty_printer *pp) const |
| { |
| pp_character (pp, '['); |
| unsigned i; |
| value *v; |
| FOR_EACH_VEC_ELT (m_elements, i, v) |
| { |
| if (i) |
| pp_string (pp, ", "); |
| v->print (pp); |
| } |
| pp_character (pp, ']'); |
| } |
| |
| /* Append non-NULL value V to a json::array, taking ownership of V. */ |
| |
| void |
| array::append (value *v) |
| { |
| gcc_assert (v); |
| m_elements.safe_push (v); |
| } |
| |
| /* class json::float_number, a subclass of json::value, wrapping a double. */ |
| |
| /* Implementation of json::value::print for json::float_number. */ |
| |
| void |
| float_number::print (pretty_printer *pp) const |
| { |
| char tmp[1024]; |
| snprintf (tmp, sizeof (tmp), "%g", m_value); |
| pp_string (pp, tmp); |
| } |
| |
| /* class json::integer_number, a subclass of json::value, wrapping a long. */ |
| |
| /* Implementation of json::value::print for json::integer_number. */ |
| |
| void |
| integer_number::print (pretty_printer *pp) const |
| { |
| char tmp[1024]; |
| snprintf (tmp, sizeof (tmp), "%ld", m_value); |
| pp_string (pp, tmp); |
| } |
| |
| |
| /* class json::string, a subclass of json::value. */ |
| |
| /* json::string's ctor. */ |
| |
| string::string (const char *utf8) |
| { |
| gcc_assert (utf8); |
| m_utf8 = xstrdup (utf8); |
| m_len = strlen (utf8); |
| } |
| |
| string::string (const char *utf8, size_t len) |
| { |
| gcc_assert (utf8); |
| m_utf8 = XNEWVEC (char, len); |
| m_len = len; |
| memcpy (m_utf8, utf8, len); |
| } |
| |
| /* Implementation of json::value::print for json::string. */ |
| |
| void |
| string::print (pretty_printer *pp) const |
| { |
| pp_character (pp, '"'); |
| for (size_t i = 0; i != m_len; ++i) |
| { |
| char ch = m_utf8[i]; |
| switch (ch) |
| { |
| case '"': |
| pp_string (pp, "\\\""); |
| break; |
| case '\\': |
| pp_string (pp, "\\\\"); |
| break; |
| case '\b': |
| pp_string (pp, "\\b"); |
| break; |
| case '\f': |
| pp_string (pp, "\\f"); |
| break; |
| case '\n': |
| pp_string (pp, "\\n"); |
| break; |
| case '\r': |
| pp_string (pp, "\\r"); |
| break; |
| case '\t': |
| pp_string (pp, "\\t"); |
| break; |
| case '\0': |
| pp_string (pp, "\\0"); |
| break; |
| default: |
| pp_character (pp, ch); |
| } |
| } |
| pp_character (pp, '"'); |
| } |
| |
| /* class json::literal, a subclass of json::value. */ |
| |
| /* Implementation of json::value::print for json::literal. */ |
| |
| void |
| literal::print (pretty_printer *pp) const |
| { |
| switch (m_kind) |
| { |
| case JSON_TRUE: |
| pp_string (pp, "true"); |
| break; |
| case JSON_FALSE: |
| pp_string (pp, "false"); |
| break; |
| case JSON_NULL: |
| pp_string (pp, "null"); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| |
| #if CHECKING_P |
| |
| namespace selftest { |
| |
| /* Selftests. */ |
| |
| /* Verify that JV->print () prints EXPECTED_JSON. */ |
| |
| static void |
| assert_print_eq (const json::value &jv, const char *expected_json) |
| { |
| pretty_printer pp; |
| jv.print (&pp); |
| ASSERT_STREQ (expected_json, pp_formatted_text (&pp)); |
| } |
| |
| /* Verify that object::get works as expected. */ |
| |
| static void |
| test_object_get () |
| { |
| object obj; |
| value *val = new json::string ("value"); |
| obj.set ("foo", val); |
| ASSERT_EQ (obj.get ("foo"), val); |
| ASSERT_EQ (obj.get ("not-present"), NULL); |
| } |
| |
| /* Verify that JSON objects are written correctly. We can't test more than |
| one key/value pair, as we don't impose a guaranteed ordering. */ |
| |
| static void |
| test_writing_objects () |
| { |
| object obj; |
| obj.set ("foo", new json::string ("bar")); |
| assert_print_eq (obj, "{\"foo\": \"bar\"}"); |
| } |
| |
| /* Verify that JSON arrays are written correctly. */ |
| |
| static void |
| test_writing_arrays () |
| { |
| array arr; |
| assert_print_eq (arr, "[]"); |
| |
| arr.append (new json::string ("foo")); |
| assert_print_eq (arr, "[\"foo\"]"); |
| |
| arr.append (new json::string ("bar")); |
| assert_print_eq (arr, "[\"foo\", \"bar\"]"); |
| } |
| |
| /* Verify that JSON numbers are written correctly. */ |
| |
| static void |
| test_writing_float_numbers () |
| { |
| assert_print_eq (float_number (0), "0"); |
| assert_print_eq (float_number (42), "42"); |
| assert_print_eq (float_number (-100), "-100"); |
| assert_print_eq (float_number (123456789), "1.23457e+08"); |
| } |
| |
| static void |
| test_writing_integer_numbers () |
| { |
| assert_print_eq (integer_number (0), "0"); |
| assert_print_eq (integer_number (42), "42"); |
| assert_print_eq (integer_number (-100), "-100"); |
| assert_print_eq (integer_number (123456789), "123456789"); |
| assert_print_eq (integer_number (-123456789), "-123456789"); |
| } |
| |
| /* Verify that JSON strings are written correctly. */ |
| |
| static void |
| test_writing_strings () |
| { |
| string foo ("foo"); |
| assert_print_eq (foo, "\"foo\""); |
| |
| string contains_quotes ("before \"quoted\" after"); |
| assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\""); |
| |
| const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'}; |
| string not_terminated (data, 3); |
| assert_print_eq (not_terminated, "\"abc\""); |
| string embedded_null (data, sizeof data); |
| assert_print_eq (embedded_null, "\"abcd\\0ef\""); |
| } |
| |
| /* Verify that JSON literals are written correctly. */ |
| |
| static void |
| test_writing_literals () |
| { |
| assert_print_eq (literal (JSON_TRUE), "true"); |
| assert_print_eq (literal (JSON_FALSE), "false"); |
| assert_print_eq (literal (JSON_NULL), "null"); |
| |
| assert_print_eq (literal (true), "true"); |
| assert_print_eq (literal (false), "false"); |
| } |
| |
| /* Run all of the selftests within this file. */ |
| |
| void |
| json_cc_tests () |
| { |
| test_object_get (); |
| test_writing_objects (); |
| test_writing_arrays (); |
| test_writing_float_numbers (); |
| test_writing_integer_numbers (); |
| test_writing_strings (); |
| test_writing_literals (); |
| } |
| |
| } // namespace selftest |
| |
| #endif /* #if CHECKING_P */ |