Home | History | Annotate | Line # | Download | only in gcc
      1 /* JSON trees
      2    Copyright (C) 2017-2024 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 /* Print a JSON string to PP, escaping '"', control characters,
     32    and embedded null bytes.
     33    The string is required to be UTF-8 encoded.  */
     34 
     35 static void
     36 print_escaped_json_string (pretty_printer *pp,
     37 			   const char *utf8_str,
     38 			   size_t len)
     39 {
     40   pp_character (pp, '"');
     41   for (size_t i = 0; i != len; ++i)
     42     {
     43       char ch = utf8_str[i];
     44       switch (ch)
     45 	{
     46 	case '"':
     47 	  pp_string (pp, "\\\"");
     48 	  break;
     49 	case '\\':
     50 	  pp_string (pp, "\\\\");
     51 	  break;
     52 	case '\b':
     53 	  pp_string (pp, "\\b");
     54 	  break;
     55 	case '\f':
     56 	  pp_string (pp, "\\f");
     57 	  break;
     58 	case '\n':
     59 	  pp_string (pp, "\\n");
     60 	  break;
     61 	case '\r':
     62 	  pp_string (pp, "\\r");
     63 	  break;
     64 	case '\t':
     65 	  pp_string (pp, "\\t");
     66 	  break;
     67 	case '\0':
     68 	  pp_string (pp, "\\0");
     69 	  break;
     70 	default:
     71 	  pp_character (pp, ch);
     72 	}
     73     }
     74   pp_character (pp, '"');
     75 }
     76 
     77 /* class json::value.  */
     78 
     79 /* Dump this json::value tree to OUTF.
     80 
     81    The key/value pairs of json::objects are printed in the order
     82    in which the keys were originally inserted.  */
     83 
     84 void
     85 value::dump (FILE *outf, bool formatted) const
     86 {
     87   pretty_printer pp;
     88   pp_buffer (&pp)->stream = outf;
     89   print (&pp, formatted);
     90   pp_flush (&pp);
     91 }
     92 
     93 /* class json::object, a subclass of json::value, representing
     94    an ordered collection of key/value pairs.  */
     95 
     96 /* json:object's dtor.  */
     97 
     98 object::~object ()
     99 {
    100   for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
    101     {
    102       free (const_cast <char *>((*it).first));
    103       delete ((*it).second);
    104     }
    105 }
    106 
    107 /* Implementation of json::value::print for json::object.  */
    108 
    109 void
    110 object::print (pretty_printer *pp, bool formatted) const
    111 {
    112   pp_character (pp, '{');
    113   if (formatted)
    114     pp_indentation (pp) += 1;
    115 
    116   /* Iterate in the order that the keys were inserted.  */
    117   unsigned i;
    118   const char *key;
    119   FOR_EACH_VEC_ELT (m_keys, i, key)
    120     {
    121       if (i > 0)
    122 	{
    123 	  pp_string (pp, ",");
    124 	  if (formatted)
    125 	    {
    126 	      pp_newline (pp);
    127 	      pp_indent (pp);
    128 	    }
    129 	  else
    130 	    pp_space (pp);
    131 	}
    132       map_t &mut_map = const_cast<map_t &> (m_map);
    133       value *value = *mut_map.get (key);
    134       print_escaped_json_string (pp, key, strlen (key));
    135       pp_string (pp, ": ");
    136       const int indent = strlen (key) + 4;
    137       if (formatted)
    138 	pp_indentation (pp) += indent;
    139       value->print (pp, formatted);
    140       if (formatted)
    141 	pp_indentation (pp) -= indent;
    142     }
    143   if (formatted)
    144     pp_indentation (pp) -= 1;
    145   pp_character (pp, '}');
    146 }
    147 
    148 /* Set the json::value * for KEY, taking ownership of V
    149    (and taking a copy of KEY if necessary).  */
    150 
    151 void
    152 object::set (const char *key, value *v)
    153 {
    154   gcc_assert (key);
    155   gcc_assert (v);
    156 
    157   value **ptr = m_map.get (key);
    158   if (ptr)
    159     {
    160       /* If the key is already present, delete the existing value
    161 	 and overwrite it.  */
    162       delete *ptr;
    163       *ptr = v;
    164     }
    165   else
    166     {
    167       /* If the key wasn't already present, take a copy of the key,
    168 	 and store the value.  */
    169       char *owned_key = xstrdup (key);
    170       m_map.put (owned_key, v);
    171       m_keys.safe_push (owned_key);
    172     }
    173 }
    174 
    175 /* Get the json::value * for KEY.
    176 
    177    The object retains ownership of the value.  */
    178 
    179 value *
    180 object::get (const char *key) const
    181 {
    182   gcc_assert (key);
    183 
    184   value **ptr = const_cast <map_t &> (m_map).get (key);
    185   if (ptr)
    186     return *ptr;
    187   else
    188     return NULL;
    189 }
    190 
    191 /* Set value of KEY within this object to a JSON
    192    string value based on UTF8_VALUE.  */
    193 
    194 void
    195 object::set_string (const char *key, const char *utf8_value)
    196 {
    197   set (key, new json::string (utf8_value));
    198 }
    199 
    200 /* Set value of KEY within this object to a JSON
    201    integer value based on V.  */
    202 
    203 void
    204 object::set_integer (const char *key, long v)
    205 {
    206   set (key, new json::integer_number (v));
    207 }
    208 
    209 /* Set value of KEY within this object to a JSON
    210    floating point value based on V.  */
    211 
    212 void
    213 object::set_float (const char *key, double v)
    214 {
    215   set (key, new json::float_number (v));
    216 }
    217 
    218 /* Set value of KEY within this object to the JSON
    219    literal true or false, based on V.  */
    220 
    221 void
    222 object::set_bool (const char *key, bool v)
    223 {
    224   set (key, new json::literal (v));
    225 }
    226 
    227 /* class json::array, a subclass of json::value, representing
    228    an ordered collection of values.  */
    229 
    230 /* json::array's dtor.  */
    231 
    232 array::~array ()
    233 {
    234   unsigned i;
    235   value *v;
    236   FOR_EACH_VEC_ELT (m_elements, i, v)
    237     delete v;
    238 }
    239 
    240 /* Implementation of json::value::print for json::array.  */
    241 
    242 void
    243 array::print (pretty_printer *pp, bool formatted) const
    244 {
    245   pp_character (pp, '[');
    246   if (formatted)
    247     pp_indentation (pp) += 1;
    248   unsigned i;
    249   value *v;
    250   FOR_EACH_VEC_ELT (m_elements, i, v)
    251     {
    252       if (i)
    253 	{
    254 	  pp_string (pp, ",");
    255 	  if (formatted)
    256 	    {
    257 	      pp_newline (pp);
    258 	      pp_indent (pp);
    259 	    }
    260 	  else
    261 	    pp_space (pp);
    262 	}
    263       v->print (pp, formatted);
    264     }
    265   if (formatted)
    266     pp_indentation (pp) -= 1;
    267   pp_character (pp, ']');
    268 }
    269 
    270 /* Append non-NULL value V to a json::array, taking ownership of V.  */
    271 
    272 void
    273 array::append (value *v)
    274 {
    275   gcc_assert (v);
    276   m_elements.safe_push (v);
    277 }
    278 
    279 /* class json::float_number, a subclass of json::value, wrapping a double.  */
    280 
    281 /* Implementation of json::value::print for json::float_number.  */
    282 
    283 void
    284 float_number::print (pretty_printer *pp,
    285 		     bool formatted ATTRIBUTE_UNUSED) const
    286 {
    287   char tmp[1024];
    288   snprintf (tmp, sizeof (tmp), "%g", m_value);
    289   pp_string (pp, tmp);
    290 }
    291 
    292 /* class json::integer_number, a subclass of json::value, wrapping a long.  */
    293 
    294 /* Implementation of json::value::print for json::integer_number.  */
    295 
    296 void
    297 integer_number::print (pretty_printer *pp,
    298 		       bool formatted ATTRIBUTE_UNUSED) const
    299 {
    300   char tmp[1024];
    301   snprintf (tmp, sizeof (tmp), "%ld", m_value);
    302   pp_string (pp, tmp);
    303 }
    304 
    305 
    306 /* class json::string, a subclass of json::value.  */
    307 
    308 /* json::string's ctor.  */
    309 
    310 string::string (const char *utf8)
    311 {
    312   gcc_assert (utf8);
    313   m_utf8 = xstrdup (utf8);
    314   m_len = strlen (utf8);
    315 }
    316 
    317 string::string (const char *utf8, size_t len)
    318 {
    319   gcc_assert (utf8);
    320   m_utf8 = XNEWVEC (char, len);
    321   m_len = len;
    322   memcpy (m_utf8, utf8, len);
    323 }
    324 
    325 /* Implementation of json::value::print for json::string.  */
    326 
    327 void
    328 string::print (pretty_printer *pp,
    329 	       bool formatted ATTRIBUTE_UNUSED) const
    330 {
    331   print_escaped_json_string (pp, m_utf8, m_len);
    332 }
    333 
    334 /* class json::literal, a subclass of json::value.  */
    335 
    336 /* Implementation of json::value::print for json::literal.  */
    337 
    338 void
    339 literal::print (pretty_printer *pp,
    340 		bool formatted ATTRIBUTE_UNUSED) const
    341 {
    342   switch (m_kind)
    343     {
    344     case JSON_TRUE:
    345       pp_string (pp, "true");
    346       break;
    347     case JSON_FALSE:
    348       pp_string (pp, "false");
    349       break;
    350     case JSON_NULL:
    351       pp_string (pp, "null");
    352       break;
    353     default:
    354       gcc_unreachable ();
    355     }
    356 }
    357 
    358 
    359 #if CHECKING_P
    361 
    362 namespace selftest {
    363 
    364 /* Selftests.  */
    365 
    366 /* Verify that JV->print () prints EXPECTED_JSON.  */
    367 
    368 static void
    369 assert_print_eq (const location &loc,
    370 		 const json::value &jv,
    371 		 bool formatted,
    372 		 const char *expected_json)
    373 {
    374   pretty_printer pp;
    375   jv.print (&pp, formatted);
    376   ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
    377 }
    378 
    379 #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON)	\
    380   assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
    381 
    382 /* Verify that object::get works as expected.  */
    383 
    384 static void
    385 test_object_get ()
    386 {
    387   object obj;
    388   value *val = new json::string ("value");
    389   obj.set ("foo", val);
    390   ASSERT_EQ (obj.get ("foo"), val);
    391   ASSERT_EQ (obj.get ("not-present"), NULL);
    392 }
    393 
    394 /* Verify that JSON objects are written correctly.  */
    395 
    396 static void
    397 test_writing_objects ()
    398 {
    399   object obj;
    400   obj.set_string ("foo", "bar");
    401   obj.set_string ("baz", "quux");
    402   obj.set_string ("\"\\\b\f\n\r\t", "value for awkward key");
    403 
    404   /* This test relies on json::object writing out key/value pairs
    405      in key-insertion order.  */
    406   ASSERT_PRINT_EQ (obj, true,
    407 		   "{\"foo\": \"bar\",\n"
    408 		   " \"baz\": \"quux\",\n"
    409 		   " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
    410   ASSERT_PRINT_EQ (obj, false,
    411 		   "{\"foo\": \"bar\", \"baz\": \"quux\""
    412 		   ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
    413 }
    414 
    415 /* Verify that JSON arrays are written correctly.  */
    416 
    417 static void
    418 test_writing_arrays ()
    419 {
    420   array arr;
    421   ASSERT_PRINT_EQ (arr, true, "[]");
    422 
    423   arr.append (new json::string ("foo"));
    424   ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
    425 
    426   arr.append (new json::string ("bar"));
    427   ASSERT_PRINT_EQ (arr, true,
    428 		   "[\"foo\",\n"
    429 		   " \"bar\"]");
    430   ASSERT_PRINT_EQ (arr, false,
    431 		   "[\"foo\", \"bar\"]");
    432 }
    433 
    434 /* Verify that JSON numbers are written correctly.  */
    435 
    436 static void
    437 test_writing_float_numbers ()
    438 {
    439   ASSERT_PRINT_EQ (float_number (0), true, "0");
    440   ASSERT_PRINT_EQ (float_number (42), true, "42");
    441   ASSERT_PRINT_EQ (float_number (-100), true, "-100");
    442   ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
    443 }
    444 
    445 static void
    446 test_writing_integer_numbers ()
    447 {
    448   ASSERT_PRINT_EQ (integer_number (0), true, "0");
    449   ASSERT_PRINT_EQ (integer_number (42), true, "42");
    450   ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
    451   ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
    452   ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
    453 }
    454 
    455 /* Verify that JSON strings are written correctly.  */
    456 
    457 static void
    458 test_writing_strings ()
    459 {
    460   string foo ("foo");
    461   ASSERT_PRINT_EQ (foo, true, "\"foo\"");
    462 
    463   string contains_quotes ("before \"quoted\" after");
    464   ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
    465 
    466   const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
    467   string not_terminated (data, 3);
    468   ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
    469   string embedded_null (data, sizeof data);
    470   ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
    471 }
    472 
    473 /* Verify that JSON literals are written correctly.  */
    474 
    475 static void
    476 test_writing_literals ()
    477 {
    478   ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
    479   ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
    480   ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
    481 
    482   ASSERT_PRINT_EQ (literal (true), true, "true");
    483   ASSERT_PRINT_EQ (literal (false), true, "false");
    484 }
    485 
    486 /* Verify that nested values are formatted correctly when written.  */
    487 
    488 static void
    489 test_formatting ()
    490 {
    491   object obj;
    492   object *child = new object;
    493   object *grandchild = new object;
    494 
    495   obj.set_string ("str", "bar");
    496   obj.set ("child", child);
    497   obj.set_integer ("int", 42);
    498 
    499   child->set ("grandchild", grandchild);
    500   child->set_integer ("int", 1776);
    501 
    502   array *arr = new array;
    503   for (int i = 0; i < 3; i++)
    504     arr->append (new integer_number (i));
    505   grandchild->set ("arr", arr);
    506   grandchild->set_integer ("int", 1066);
    507 
    508   /* This test relies on json::object writing out key/value pairs
    509      in key-insertion order.  */
    510   ASSERT_PRINT_EQ (obj, true,
    511 		   ("{\"str\": \"bar\",\n"
    512 		    " \"child\": {\"grandchild\": {\"arr\": [0,\n"
    513 		    "                                  1,\n"
    514 		    "                                  2],\n"
    515 		    "                          \"int\": 1066},\n"
    516 		    "           \"int\": 1776},\n"
    517 		    " \"int\": 42}"));
    518   ASSERT_PRINT_EQ (obj, false,
    519 		   ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
    520 		    " {\"arr\": [0, 1, 2], \"int\": 1066},"
    521 		    " \"int\": 1776}, \"int\": 42}"));
    522 }
    523 
    524 /* Run all of the selftests within this file.  */
    525 
    526 void
    527 json_cc_tests ()
    528 {
    529   test_object_get ();
    530   test_writing_objects ();
    531   test_writing_arrays ();
    532   test_writing_float_numbers ();
    533   test_writing_integer_numbers ();
    534   test_writing_strings ();
    535   test_writing_literals ();
    536   test_formatting ();
    537 }
    538 
    539 } // namespace selftest
    540 
    541 #endif /* #if CHECKING_P */
    542