Home | History | Annotate | Line # | Download | only in gcc
      1 /* JSON trees
      2    Copyright (C) 2017-2022 Free Software Foundation, Inc.
      3    Contributed by David Malcolm <dmalcolm (at) 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_doublequote (pp);
     74       pp_string (pp, key); // FIXME: escaping?
     75       pp_doublequote (pp);
     76       pp_string (pp, ": ");
     77       value->print (pp);
     78     }
     79   pp_character (pp, '}');
     80 }
     81 
     82 /* Set the json::value * for KEY, taking ownership of V
     83    (and taking a copy of KEY if necessary).  */
     84 
     85 void
     86 object::set (const char *key, value *v)
     87 {
     88   gcc_assert (key);
     89   gcc_assert (v);
     90 
     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 
    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 
    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 
    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 
    160 /* class json::float_number, a subclass of json::value, wrapping a double.  */
    161 
    162 /* Implementation of json::value::print for json::float_number.  */
    163 
    164 void
    165 float_number::print (pretty_printer *pp) const
    166 {
    167   char tmp[1024];
    168   snprintf (tmp, sizeof (tmp), "%g", m_value);
    169   pp_string (pp, tmp);
    170 }
    171 
    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 
    185 /* class json::string, a subclass of json::value.  */
    186 
    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 
    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, "\\\\");
    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 
    259 #if CHECKING_P
    261 
    262 namespace selftest {
    263 
    264 /* Selftests.  */
    265 
    266 /* Verify that JV->print () prints EXPECTED_JSON.  */
    267 
    268 static void
    269 assert_print_eq (const json::value &jv, const char *expected_json)
    270 {
    271   pretty_printer pp;
    272   jv.print (&pp);
    273   ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
    274 }
    275 
    276 /* Verify that object::get works as expected.  */
    277 
    278 static void
    279 test_object_get ()
    280 {
    281   object obj;
    282   value *val = new json::string ("value");
    283   obj.set ("foo", val);
    284   ASSERT_EQ (obj.get ("foo"), val);
    285   ASSERT_EQ (obj.get ("not-present"), NULL);
    286 }
    287 
    288 /* Verify that JSON objects are written correctly.  We can't test more than
    289    one key/value pair, as we don't impose a guaranteed ordering.  */
    290 
    291 static void
    292 test_writing_objects ()
    293 {
    294   object obj;
    295   obj.set ("foo", new json::string ("bar"));
    296   assert_print_eq (obj, "{\"foo\": \"bar\"}");
    297 }
    298 
    299 /* Verify that JSON arrays are written correctly.  */
    300 
    301 static void
    302 test_writing_arrays ()
    303 {
    304   array arr;
    305   assert_print_eq (arr, "[]");
    306 
    307   arr.append (new json::string ("foo"));
    308   assert_print_eq (arr, "[\"foo\"]");
    309 
    310   arr.append (new json::string ("bar"));
    311   assert_print_eq (arr, "[\"foo\", \"bar\"]");
    312 }
    313 
    314 /* Verify that JSON numbers are written correctly.  */
    315 
    316 static void
    317 test_writing_float_numbers ()
    318 {
    319   assert_print_eq (float_number (0), "0");
    320   assert_print_eq (float_number (42), "42");
    321   assert_print_eq (float_number (-100), "-100");
    322   assert_print_eq (float_number (123456789), "1.23457e+08");
    323 }
    324 
    325 static void
    326 test_writing_integer_numbers ()
    327 {
    328   assert_print_eq (integer_number (0), "0");
    329   assert_print_eq (integer_number (42), "42");
    330   assert_print_eq (integer_number (-100), "-100");
    331   assert_print_eq (integer_number (123456789), "123456789");
    332   assert_print_eq (integer_number (-123456789), "-123456789");
    333 }
    334 
    335 /* Verify that JSON strings are written correctly.  */
    336 
    337 static void
    338 test_writing_strings ()
    339 {
    340   string foo ("foo");
    341   assert_print_eq (foo, "\"foo\"");
    342 
    343   string contains_quotes ("before \"quoted\" after");
    344   assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\"");
    345 }
    346 
    347 /* Verify that JSON literals are written correctly.  */
    348 
    349 static void
    350 test_writing_literals ()
    351 {
    352   assert_print_eq (literal (JSON_TRUE), "true");
    353   assert_print_eq (literal (JSON_FALSE), "false");
    354   assert_print_eq (literal (JSON_NULL), "null");
    355 
    356   assert_print_eq (literal (true), "true");
    357   assert_print_eq (literal (false), "false");
    358 }
    359 
    360 /* Run all of the selftests within this file.  */
    361 
    362 void
    363 json_cc_tests ()
    364 {
    365   test_object_get ();
    366   test_writing_objects ();
    367   test_writing_arrays ();
    368   test_writing_float_numbers ();
    369   test_writing_integer_numbers ();
    370   test_writing_strings ();
    371   test_writing_literals ();
    372 }
    373 
    374 } // namespace selftest
    375 
    376 #endif /* #if CHECKING_P */
    377