| 1 | /* JSON trees |
| 2 | Copyright (C) 2017-2025 Free Software Foundation, Inc. |
| 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 | /* 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 pointer::token. */ |
| 78 | |
| 79 | pointer::token::token () |
| 80 | { |
| 81 | m_parent = nullptr; |
| 82 | m_data.u_member = nullptr; |
| 83 | m_kind = kind::root_value; |
| 84 | } |
| 85 | |
| 86 | pointer::token::token (json::object &parent, const char *member) |
| 87 | { |
| 88 | m_parent = &parent; |
| 89 | m_data.u_member = xstrdup (member); // ideally we'd share |
| 90 | m_kind = kind::object_member; |
| 91 | } |
| 92 | |
| 93 | pointer::token::token (json::array &parent, size_t index) |
| 94 | { |
| 95 | m_parent = &parent; |
| 96 | m_data.u_index = index; |
| 97 | m_kind = kind::array_index; |
| 98 | } |
| 99 | |
| 100 | pointer::token::~token () |
| 101 | { |
| 102 | if (m_kind == kind::object_member) |
| 103 | { |
| 104 | gcc_assert (m_data.u_member); |
| 105 | free (ptr: m_data.u_member); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | pointer::token & |
| 110 | pointer::token::operator= (pointer::token &&other) |
| 111 | { |
| 112 | m_parent = other.m_parent; |
| 113 | m_data = other.m_data; |
| 114 | m_kind = other.m_kind; |
| 115 | |
| 116 | other.m_parent = nullptr; |
| 117 | other.m_data.u_member = nullptr; |
| 118 | other.m_kind = kind::root_value; |
| 119 | |
| 120 | return *this; |
| 121 | } |
| 122 | |
| 123 | /* class json::value. */ |
| 124 | |
| 125 | /* Dump this json::value tree to OUTF. |
| 126 | |
| 127 | The key/value pairs of json::objects are printed in the order |
| 128 | in which the keys were originally inserted. */ |
| 129 | |
| 130 | void |
| 131 | value::dump (FILE *outf, bool formatted) const |
| 132 | { |
| 133 | pretty_printer pp; |
| 134 | pp_buffer (pp: &pp)->m_stream = outf; |
| 135 | print (pp: &pp, formatted); |
| 136 | pp_flush (&pp); |
| 137 | } |
| 138 | |
| 139 | /* A convenience function for debugging. |
| 140 | Dump to stderr with formatting, and a trailing newline. */ |
| 141 | |
| 142 | void |
| 143 | value::dump () const |
| 144 | { |
| 145 | dump (stderr, formatted: true); |
| 146 | fprintf (stderr, format: "\n" ); |
| 147 | } |
| 148 | |
| 149 | /* A deterministic total ordering for comparing json values, so that we |
| 150 | can e.g. put them in std::map. |
| 151 | |
| 152 | This is intended to follow the condition for equality described in |
| 153 | the JSON Schema standard (§4.3, “Instance equality”), as referenced |
| 154 | by SARIF v2.1.0 (§3.7.3 "Array properties with unique values"), but has |
| 155 | the following limitations: |
| 156 | - numbers are supposed to be checked for "the same mathematical value", |
| 157 | but in this implementation int vs float numbers won't compare as equal, |
| 158 | and float number comparison is bitwise |
| 159 | - strings are supposed to be "the same codepoint-for-codepoint", but |
| 160 | this implementation doesn't take into account canonicalization issues. */ |
| 161 | |
| 162 | int |
| 163 | value::compare (const value &val_a, const value &val_b) |
| 164 | { |
| 165 | enum kind kind_a = val_a.get_kind (); |
| 166 | enum kind kind_b = val_b.get_kind (); |
| 167 | if (kind_a != kind_b) |
| 168 | return (int)kind_a - (int)kind_b; |
| 169 | |
| 170 | switch (kind_a) |
| 171 | { |
| 172 | default: |
| 173 | gcc_unreachable (); |
| 174 | |
| 175 | case JSON_OBJECT: |
| 176 | { |
| 177 | const object &obj_a = (const object &)val_a; |
| 178 | const object &obj_b = (const object &)val_b; |
| 179 | return object::compare (obj_a, obj_b); |
| 180 | } |
| 181 | break; |
| 182 | |
| 183 | case JSON_ARRAY: |
| 184 | { |
| 185 | const array &arr_a = (const array &)val_a; |
| 186 | const array &arr_b = (const array &)val_b; |
| 187 | if (int cmp_size = (int)arr_a.size () - (int)arr_b.size ()) |
| 188 | return cmp_size; |
| 189 | for (size_t idx = 0; idx < arr_a.size (); ++idx) |
| 190 | if (int cmp_element = compare (val_a: *arr_a[idx], val_b: *arr_b[idx])) |
| 191 | return cmp_element; |
| 192 | return 0; |
| 193 | } |
| 194 | break; |
| 195 | |
| 196 | case JSON_INTEGER: |
| 197 | { |
| 198 | const integer_number &int_a = (const integer_number &)val_a; |
| 199 | const integer_number &int_b = (const integer_number &)val_b; |
| 200 | return int_a.get () - int_b.get (); |
| 201 | } |
| 202 | break; |
| 203 | |
| 204 | case JSON_FLOAT: |
| 205 | { |
| 206 | const float_number &float_a = (const float_number &)val_a; |
| 207 | const float_number &float_b = (const float_number &)val_b; |
| 208 | union u |
| 209 | { |
| 210 | double u_double; |
| 211 | char u_buf[sizeof(double)]; |
| 212 | }; |
| 213 | union u u_a, u_b; |
| 214 | u_a.u_double = float_a.get (); |
| 215 | u_b.u_double = float_b.get (); |
| 216 | return memcmp (s1: &u_a, s2: &u_b, n: sizeof(double)); |
| 217 | } |
| 218 | break; |
| 219 | |
| 220 | case JSON_STRING: |
| 221 | { |
| 222 | const string &str_a = (const string &)val_a; |
| 223 | const string &str_b = (const string &)val_b; |
| 224 | return strcmp (s1: str_a.get_string (), s2: str_b.get_string ()); |
| 225 | } |
| 226 | break; |
| 227 | |
| 228 | case JSON_TRUE: |
| 229 | case JSON_FALSE: |
| 230 | case JSON_NULL: |
| 231 | /* All instances of literals compare equal to instances |
| 232 | of the same literal. */ |
| 233 | return 0; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | /* class json::object, a subclass of json::value, representing |
| 238 | an ordered collection of key/value pairs. */ |
| 239 | |
| 240 | /* json:object's dtor. */ |
| 241 | |
| 242 | object::~object () |
| 243 | { |
| 244 | for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) |
| 245 | { |
| 246 | free (ptr: const_cast <char *>((*it).first)); |
| 247 | delete ((*it).second); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /* Implementation of json::value::print for json::object. */ |
| 252 | |
| 253 | void |
| 254 | object::print (pretty_printer *pp, bool formatted) const |
| 255 | { |
| 256 | pp_character (pp, '{'); |
| 257 | if (formatted) |
| 258 | pp_indentation (pp) += 1; |
| 259 | |
| 260 | /* Iterate in the order that the keys were inserted. */ |
| 261 | unsigned i; |
| 262 | const char *key; |
| 263 | FOR_EACH_VEC_ELT (m_keys, i, key) |
| 264 | { |
| 265 | if (i > 0) |
| 266 | { |
| 267 | pp_string (pp, "," ); |
| 268 | if (formatted) |
| 269 | { |
| 270 | pp_newline (pp); |
| 271 | pp_indent (pp); |
| 272 | } |
| 273 | else |
| 274 | pp_space (pp); |
| 275 | } |
| 276 | map_t &mut_map = const_cast<map_t &> (m_map); |
| 277 | value *value = *mut_map.get (k: key); |
| 278 | print_escaped_json_string (pp, utf8_str: key, len: strlen (s: key)); |
| 279 | pp_string (pp, ": " ); |
| 280 | const int indent = strlen (s: key) + 4; |
| 281 | if (formatted) |
| 282 | pp_indentation (pp) += indent; |
| 283 | value->print (pp, formatted); |
| 284 | if (formatted) |
| 285 | pp_indentation (pp) -= indent; |
| 286 | } |
| 287 | if (formatted) |
| 288 | pp_indentation (pp) -= 1; |
| 289 | pp_character (pp, '}'); |
| 290 | } |
| 291 | |
| 292 | /* Set the json::value * for KEY, taking ownership of V |
| 293 | (and taking a copy of KEY if necessary). */ |
| 294 | |
| 295 | void |
| 296 | object::set (const char *key, value *v) |
| 297 | { |
| 298 | gcc_assert (key); |
| 299 | gcc_assert (v); |
| 300 | |
| 301 | value **ptr = m_map.get (k: key); |
| 302 | if (ptr) |
| 303 | { |
| 304 | /* If the key is already present, delete the existing value |
| 305 | and overwrite it. */ |
| 306 | delete *ptr; |
| 307 | *ptr = v; |
| 308 | } |
| 309 | else |
| 310 | { |
| 311 | /* If the key wasn't already present, take a copy of the key, |
| 312 | and store the value. */ |
| 313 | char *owned_key = xstrdup (key); |
| 314 | m_map.put (k: owned_key, v); |
| 315 | m_keys.safe_push (obj: owned_key); |
| 316 | } |
| 317 | |
| 318 | v->m_pointer_token = pointer::token (*this, key); |
| 319 | } |
| 320 | |
| 321 | /* Get the json::value * for KEY. |
| 322 | |
| 323 | The object retains ownership of the value. */ |
| 324 | |
| 325 | value * |
| 326 | object::get (const char *key) const |
| 327 | { |
| 328 | gcc_assert (key); |
| 329 | |
| 330 | value **ptr = const_cast <map_t &> (m_map).get (k: key); |
| 331 | if (ptr) |
| 332 | return *ptr; |
| 333 | else |
| 334 | return NULL; |
| 335 | } |
| 336 | |
| 337 | /* Set value of KEY within this object to a JSON |
| 338 | string value based on UTF8_VALUE. */ |
| 339 | |
| 340 | void |
| 341 | object::set_string (const char *key, const char *utf8_value) |
| 342 | { |
| 343 | set (key, v: new json::string (utf8_value)); |
| 344 | } |
| 345 | |
| 346 | /* Set value of KEY within this object to a JSON |
| 347 | integer value based on V. */ |
| 348 | |
| 349 | void |
| 350 | object::set_integer (const char *key, long v) |
| 351 | { |
| 352 | set (key, v: new json::integer_number (v)); |
| 353 | } |
| 354 | |
| 355 | /* Set value of KEY within this object to a JSON |
| 356 | floating point value based on V. */ |
| 357 | |
| 358 | void |
| 359 | object::set_float (const char *key, double v) |
| 360 | { |
| 361 | set (key, v: new json::float_number (v)); |
| 362 | } |
| 363 | |
| 364 | /* Set value of KEY within this object to the JSON |
| 365 | literal true or false, based on V. */ |
| 366 | |
| 367 | void |
| 368 | object::set_bool (const char *key, bool v) |
| 369 | { |
| 370 | set (key, v: new json::literal (v)); |
| 371 | } |
| 372 | |
| 373 | /* Subroutine of json::compare for comparing a pairs of objects. */ |
| 374 | |
| 375 | int |
| 376 | object::compare (const json::object &obj_a, const json::object &obj_b) |
| 377 | { |
| 378 | if (int cmp_size = (int)obj_a.m_keys.length () - (int)obj_b.m_keys.length ()) |
| 379 | return cmp_size; |
| 380 | |
| 381 | for (auto iter_a : obj_a.m_map) |
| 382 | { |
| 383 | const char *key = iter_a.first; |
| 384 | const value *value_a = iter_a.second; |
| 385 | gcc_assert (value_a); |
| 386 | |
| 387 | const value *value_b = obj_b.get (key); |
| 388 | if (!value_b) |
| 389 | /* Key is in OBJ_A but not in OBJ_B. */ |
| 390 | return 1; |
| 391 | /* If key is OBJ_B but not in OBJ_A, then the |
| 392 | count of keys will have been different, or |
| 393 | OBJ_A would have had a key not in OBJ_B. */ |
| 394 | if (int cmp_value = value::compare (val_a: *value_a, val_b: *value_b)) |
| 395 | /* Values for key are non-equal. */ |
| 396 | return cmp_value; |
| 397 | } |
| 398 | |
| 399 | /* Objects are equal. */ |
| 400 | return 0; |
| 401 | } |
| 402 | |
| 403 | /* class json::array, a subclass of json::value, representing |
| 404 | an ordered collection of values. */ |
| 405 | |
| 406 | /* json::array's dtor. */ |
| 407 | |
| 408 | array::~array () |
| 409 | { |
| 410 | unsigned i; |
| 411 | value *v; |
| 412 | FOR_EACH_VEC_ELT (m_elements, i, v) |
| 413 | delete v; |
| 414 | } |
| 415 | |
| 416 | /* Implementation of json::value::print for json::array. */ |
| 417 | |
| 418 | void |
| 419 | array::print (pretty_printer *pp, bool formatted) const |
| 420 | { |
| 421 | pp_character (pp, '['); |
| 422 | if (formatted) |
| 423 | pp_indentation (pp) += 1; |
| 424 | unsigned i; |
| 425 | value *v; |
| 426 | FOR_EACH_VEC_ELT (m_elements, i, v) |
| 427 | { |
| 428 | if (i) |
| 429 | { |
| 430 | pp_string (pp, "," ); |
| 431 | if (formatted) |
| 432 | { |
| 433 | pp_newline (pp); |
| 434 | pp_indent (pp); |
| 435 | } |
| 436 | else |
| 437 | pp_space (pp); |
| 438 | } |
| 439 | v->print (pp, formatted); |
| 440 | } |
| 441 | if (formatted) |
| 442 | pp_indentation (pp) -= 1; |
| 443 | pp_character (pp, ']'); |
| 444 | } |
| 445 | |
| 446 | /* Append non-NULL value V to a json::array, taking ownership of V. */ |
| 447 | |
| 448 | void |
| 449 | array::append (value *v) |
| 450 | { |
| 451 | gcc_assert (v); |
| 452 | v->m_pointer_token = pointer::token (*this, m_elements.length ()); |
| 453 | m_elements.safe_push (obj: v); |
| 454 | } |
| 455 | |
| 456 | void |
| 457 | array::append_string (const char *utf8_value) |
| 458 | { |
| 459 | gcc_assert (utf8_value); |
| 460 | append (v: new json::string (utf8_value)); |
| 461 | } |
| 462 | |
| 463 | /* class json::float_number, a subclass of json::value, wrapping a double. */ |
| 464 | |
| 465 | /* Implementation of json::value::print for json::float_number. */ |
| 466 | |
| 467 | void |
| 468 | float_number::print (pretty_printer *pp, |
| 469 | bool formatted ATTRIBUTE_UNUSED) const |
| 470 | { |
| 471 | char tmp[1024]; |
| 472 | snprintf (s: tmp, maxlen: sizeof (tmp), format: "%g" , m_value); |
| 473 | pp_string (pp, tmp); |
| 474 | } |
| 475 | |
| 476 | /* class json::integer_number, a subclass of json::value, wrapping a long. */ |
| 477 | |
| 478 | /* Implementation of json::value::print for json::integer_number. */ |
| 479 | |
| 480 | void |
| 481 | integer_number::print (pretty_printer *pp, |
| 482 | bool formatted ATTRIBUTE_UNUSED) const |
| 483 | { |
| 484 | char tmp[1024]; |
| 485 | snprintf (s: tmp, maxlen: sizeof (tmp), format: "%ld" , m_value); |
| 486 | pp_string (pp, tmp); |
| 487 | } |
| 488 | |
| 489 | |
| 490 | /* class json::string, a subclass of json::value. */ |
| 491 | |
| 492 | /* json::string's ctor. */ |
| 493 | |
| 494 | string::string (const char *utf8) |
| 495 | { |
| 496 | gcc_assert (utf8); |
| 497 | m_utf8 = xstrdup (utf8); |
| 498 | m_len = strlen (s: utf8); |
| 499 | } |
| 500 | |
| 501 | string::string (const char *utf8, size_t len) |
| 502 | { |
| 503 | gcc_assert (utf8); |
| 504 | m_utf8 = XNEWVEC (char, len); |
| 505 | m_len = len; |
| 506 | memcpy (dest: m_utf8, src: utf8, n: len); |
| 507 | } |
| 508 | |
| 509 | /* Implementation of json::value::print for json::string. */ |
| 510 | |
| 511 | void |
| 512 | string::print (pretty_printer *pp, |
| 513 | bool formatted ATTRIBUTE_UNUSED) const |
| 514 | { |
| 515 | print_escaped_json_string (pp, utf8_str: m_utf8, len: m_len); |
| 516 | } |
| 517 | |
| 518 | /* class json::literal, a subclass of json::value. */ |
| 519 | |
| 520 | /* Implementation of json::value::print for json::literal. */ |
| 521 | |
| 522 | void |
| 523 | literal::print (pretty_printer *pp, |
| 524 | bool formatted ATTRIBUTE_UNUSED) const |
| 525 | { |
| 526 | switch (m_kind) |
| 527 | { |
| 528 | case JSON_TRUE: |
| 529 | pp_string (pp, "true" ); |
| 530 | break; |
| 531 | case JSON_FALSE: |
| 532 | pp_string (pp, "false" ); |
| 533 | break; |
| 534 | case JSON_NULL: |
| 535 | pp_string (pp, "null" ); |
| 536 | break; |
| 537 | default: |
| 538 | gcc_unreachable (); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | |
| 543 | #if CHECKING_P |
| 544 | |
| 545 | namespace selftest { |
| 546 | |
| 547 | /* Selftests. */ |
| 548 | |
| 549 | /* Verify that JV->print () prints EXPECTED_JSON. */ |
| 550 | |
| 551 | void |
| 552 | assert_print_eq (const location &loc, |
| 553 | const json::value &jv, |
| 554 | bool formatted, |
| 555 | const char *expected_json) |
| 556 | { |
| 557 | pretty_printer pp; |
| 558 | jv.print (pp: &pp, formatted); |
| 559 | ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp)); |
| 560 | } |
| 561 | |
| 562 | #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \ |
| 563 | assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON) |
| 564 | |
| 565 | /* Verify that object::get works as expected. */ |
| 566 | |
| 567 | static void |
| 568 | test_object_get () |
| 569 | { |
| 570 | object obj; |
| 571 | value *val = new json::string ("value" ); |
| 572 | obj.set (key: "foo" , v: val); |
| 573 | ASSERT_EQ (obj.get ("foo" ), val); |
| 574 | ASSERT_EQ (obj.get ("not-present" ), NULL); |
| 575 | } |
| 576 | |
| 577 | /* Verify that JSON objects are written correctly. */ |
| 578 | |
| 579 | static void |
| 580 | test_writing_objects () |
| 581 | { |
| 582 | object obj; |
| 583 | obj.set_string (key: "foo" , utf8_value: "bar" ); |
| 584 | obj.set_string (key: "baz" , utf8_value: "quux" ); |
| 585 | obj.set_string (key: "\"\\\b\f\n\r\t" , utf8_value: "value for awkward key" ); |
| 586 | |
| 587 | /* This test relies on json::object writing out key/value pairs |
| 588 | in key-insertion order. */ |
| 589 | ASSERT_PRINT_EQ (obj, true, |
| 590 | "{\"foo\": \"bar\",\n" |
| 591 | " \"baz\": \"quux\",\n" |
| 592 | " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}" ); |
| 593 | ASSERT_PRINT_EQ (obj, false, |
| 594 | "{\"foo\": \"bar\", \"baz\": \"quux\"" |
| 595 | ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}" ); |
| 596 | } |
| 597 | |
| 598 | /* Verify that JSON arrays are written correctly. */ |
| 599 | |
| 600 | static void |
| 601 | test_writing_arrays () |
| 602 | { |
| 603 | array arr; |
| 604 | ASSERT_PRINT_EQ (arr, true, "[]" ); |
| 605 | |
| 606 | arr.append (v: new json::string ("foo" )); |
| 607 | ASSERT_PRINT_EQ (arr, true, "[\"foo\"]" ); |
| 608 | |
| 609 | arr.append_string (utf8_value: "bar" ); |
| 610 | ASSERT_PRINT_EQ (arr, true, |
| 611 | "[\"foo\",\n" |
| 612 | " \"bar\"]" ); |
| 613 | ASSERT_PRINT_EQ (arr, false, |
| 614 | "[\"foo\", \"bar\"]" ); |
| 615 | } |
| 616 | |
| 617 | /* Verify that JSON numbers are written correctly. */ |
| 618 | |
| 619 | static void |
| 620 | test_writing_float_numbers () |
| 621 | { |
| 622 | ASSERT_PRINT_EQ (float_number (0), true, "0" ); |
| 623 | ASSERT_PRINT_EQ (float_number (42), true, "42" ); |
| 624 | ASSERT_PRINT_EQ (float_number (-100), true, "-100" ); |
| 625 | ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08" ); |
| 626 | } |
| 627 | |
| 628 | static void |
| 629 | test_writing_integer_numbers () |
| 630 | { |
| 631 | ASSERT_PRINT_EQ (integer_number (0), true, "0" ); |
| 632 | ASSERT_PRINT_EQ (integer_number (42), true, "42" ); |
| 633 | ASSERT_PRINT_EQ (integer_number (-100), true, "-100" ); |
| 634 | ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789" ); |
| 635 | ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789" ); |
| 636 | } |
| 637 | |
| 638 | /* Verify that JSON strings are written correctly. */ |
| 639 | |
| 640 | static void |
| 641 | test_writing_strings () |
| 642 | { |
| 643 | string foo ("foo" ); |
| 644 | ASSERT_PRINT_EQ (foo, true, "\"foo\"" ); |
| 645 | |
| 646 | string contains_quotes ("before \"quoted\" after" ); |
| 647 | ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"" ); |
| 648 | |
| 649 | const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'}; |
| 650 | string not_terminated (data, 3); |
| 651 | ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"" ); |
| 652 | string embedded_null (data, sizeof data); |
| 653 | ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"" ); |
| 654 | } |
| 655 | |
| 656 | /* Verify that JSON literals are written correctly. */ |
| 657 | |
| 658 | static void |
| 659 | test_writing_literals () |
| 660 | { |
| 661 | ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true" ); |
| 662 | ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false" ); |
| 663 | ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null" ); |
| 664 | |
| 665 | ASSERT_PRINT_EQ (literal (true), true, "true" ); |
| 666 | ASSERT_PRINT_EQ (literal (false), true, "false" ); |
| 667 | } |
| 668 | |
| 669 | /* Verify that nested values are formatted correctly when written. |
| 670 | |
| 671 | Also, make use of array::append(std::unique_ptr<value>) and |
| 672 | object::set (const char *key, std::unique_ptr<value> v).*/ |
| 673 | |
| 674 | static void |
| 675 | test_formatting () |
| 676 | { |
| 677 | object obj; |
| 678 | object *child = new object; |
| 679 | std::unique_ptr<object> grandchild = std::make_unique<object> (); |
| 680 | |
| 681 | obj.set_string (key: "str" , utf8_value: "bar" ); |
| 682 | obj.set (key: "child" , v: child); |
| 683 | obj.set_integer (key: "int" , v: 42); |
| 684 | |
| 685 | array *arr = new array; |
| 686 | for (int i = 0; i < 3; i++) |
| 687 | arr->append (v: std::make_unique<integer_number> (args&: i)); |
| 688 | grandchild->set (key: "arr" , v: arr); |
| 689 | grandchild->set_integer (key: "int" , v: 1066); |
| 690 | |
| 691 | child->set (key: "grandchild" , v: std::move (grandchild)); |
| 692 | child->set_integer (key: "int" , v: 1776); |
| 693 | |
| 694 | /* This test relies on json::object writing out key/value pairs |
| 695 | in key-insertion order. */ |
| 696 | ASSERT_PRINT_EQ (obj, true, |
| 697 | ("{\"str\": \"bar\",\n" |
| 698 | " \"child\": {\"grandchild\": {\"arr\": [0,\n" |
| 699 | " 1,\n" |
| 700 | " 2],\n" |
| 701 | " \"int\": 1066},\n" |
| 702 | " \"int\": 1776},\n" |
| 703 | " \"int\": 42}" )); |
| 704 | ASSERT_PRINT_EQ (obj, false, |
| 705 | ("{\"str\": \"bar\", \"child\": {\"grandchild\":" |
| 706 | " {\"arr\": [0, 1, 2], \"int\": 1066}," |
| 707 | " \"int\": 1776}, \"int\": 42}" )); |
| 708 | } |
| 709 | |
| 710 | /* Helper function for reporting failure of JSON comparisons. */ |
| 711 | |
| 712 | static void |
| 713 | fail_comparison (const location &loc, |
| 714 | const char *desc, |
| 715 | const value &val_a, const value &val_b, |
| 716 | const char *desc_expected_value, |
| 717 | int actual_value) |
| 718 | { |
| 719 | fprintf (stderr, format: "val_a: " ); |
| 720 | val_a.dump (); |
| 721 | |
| 722 | fprintf (stderr, format: "val_b: " ); |
| 723 | val_b.dump (); |
| 724 | |
| 725 | selftest::fail_formatted (loc, |
| 726 | fmt: "%s: failed JSON comparison:" |
| 727 | " expected: %s got: %i\n" , |
| 728 | desc, |
| 729 | desc_expected_value, actual_value); |
| 730 | } |
| 731 | |
| 732 | /* Implementation of ASSERT_JSON_EQ. */ |
| 733 | |
| 734 | static void |
| 735 | assert_json_equal (const location &loc, |
| 736 | const char *desc, |
| 737 | const value &val_a, const value &val_b) |
| 738 | { |
| 739 | /* Comparison should return zero, both ways, indicating no differences. */ |
| 740 | const int a_vs_b = value::compare (val_a, val_b); |
| 741 | if (a_vs_b != 0) |
| 742 | fail_comparison (loc, desc, val_a, val_b, desc_expected_value: "zero" , actual_value: a_vs_b); |
| 743 | |
| 744 | const int b_vs_a = value::compare (val_a: val_b, val_b: val_a); |
| 745 | if (b_vs_a != 0) |
| 746 | fail_comparison (loc, desc, val_a: val_b, val_b: val_a, desc_expected_value: "zero" , actual_value: b_vs_a); |
| 747 | } |
| 748 | |
| 749 | /* Verify that json::value::compare returns 0 ("no differences") on |
| 750 | VAL1 and VAL2, in both orders. */ |
| 751 | |
| 752 | #define ASSERT_JSON_EQ(VAL1, VAL2) \ |
| 753 | SELFTEST_BEGIN_STMT \ |
| 754 | assert_json_equal ((SELFTEST_LOCATION), \ |
| 755 | "ASSERT_JSON_EQ", \ |
| 756 | (VAL1), (VAL2)); \ |
| 757 | SELFTEST_END_STMT |
| 758 | |
| 759 | /* Implementation of ASSERT_JSON_NE. */ |
| 760 | |
| 761 | static void |
| 762 | assert_json_non_equal (const location &loc, |
| 763 | const char *desc, |
| 764 | const value &val_a, const value &val_b) |
| 765 | { |
| 766 | /* Comparison should be non-zero, indicating differences. */ |
| 767 | const int a_vs_b = value::compare (val_a, val_b); |
| 768 | if (a_vs_b == 0) |
| 769 | fail_comparison (loc, desc, val_a, val_b, desc_expected_value: "non-zero" , actual_value: a_vs_b); |
| 770 | |
| 771 | const int b_vs_a = value::compare (val_a: val_b, val_b: val_a); |
| 772 | ASSERT_NE_AT (loc, b_vs_a, 0); |
| 773 | if (b_vs_a == 0) |
| 774 | fail_comparison (loc, desc, val_a: val_b, val_b: val_a, desc_expected_value: "non-zero" , actual_value: b_vs_a); |
| 775 | |
| 776 | /* Swapping the args should swap the sign of the result |
| 777 | (but isn't necessarily the negation). */ |
| 778 | if ( (a_vs_b > 0) == (b_vs_a > 0) ) |
| 779 | fail_comparison (loc, desc, val_a: val_b, val_b: val_a, desc_expected_value: "opposite signs" , actual_value: 1); |
| 780 | } |
| 781 | |
| 782 | /* Verify that json::value::compare returns non-zero ("different") on |
| 783 | VAL1 and VAL2, in both orders, and that they have opposite |
| 784 | sign. */ |
| 785 | |
| 786 | #define ASSERT_JSON_NE(VAL1, VAL2) \ |
| 787 | SELFTEST_BEGIN_STMT \ |
| 788 | assert_json_non_equal ((SELFTEST_LOCATION), \ |
| 789 | "ASSERT_JSON_NE", \ |
| 790 | (VAL1), (VAL2)); \ |
| 791 | SELFTEST_END_STMT |
| 792 | |
| 793 | /* Verify that json::value::compare works as expected. */ |
| 794 | |
| 795 | static void |
| 796 | test_comparisons () |
| 797 | { |
| 798 | /* Literals. */ |
| 799 | |
| 800 | literal null_lit (JSON_NULL); |
| 801 | ASSERT_JSON_EQ (null_lit, null_lit); |
| 802 | |
| 803 | literal other_null_lit (JSON_NULL); |
| 804 | ASSERT_JSON_EQ (null_lit, other_null_lit); |
| 805 | |
| 806 | literal true_lit (JSON_TRUE); |
| 807 | ASSERT_JSON_EQ (true_lit, true_lit); |
| 808 | ASSERT_JSON_NE (true_lit, null_lit); |
| 809 | |
| 810 | literal false_lit (JSON_FALSE); |
| 811 | ASSERT_JSON_EQ (false_lit, false_lit); |
| 812 | ASSERT_JSON_NE (false_lit, true_lit); |
| 813 | ASSERT_JSON_NE (false_lit, null_lit); |
| 814 | |
| 815 | /* Strings. */ |
| 816 | string str_foo_1 ("foo" ); |
| 817 | ASSERT_JSON_EQ (str_foo_1, str_foo_1); |
| 818 | |
| 819 | string str_foo_2 ("foo" ); |
| 820 | ASSERT_JSON_EQ (str_foo_1, str_foo_2); |
| 821 | |
| 822 | string str_bar ("bar" ); |
| 823 | ASSERT_JSON_NE (str_bar, str_foo_1); |
| 824 | |
| 825 | /* Numbers. */ |
| 826 | integer_number i_42 (42); |
| 827 | ASSERT_JSON_EQ (i_42, i_42); |
| 828 | integer_number i_42_2 (42); |
| 829 | ASSERT_JSON_EQ (i_42, i_42_2); |
| 830 | integer_number i_43 (43); |
| 831 | ASSERT_JSON_NE (i_42, i_43); |
| 832 | |
| 833 | float_number f_zero (0.0); |
| 834 | ASSERT_JSON_EQ (f_zero, f_zero); |
| 835 | float_number f_zero_2 (0.0); |
| 836 | ASSERT_JSON_EQ (f_zero, f_zero_2); |
| 837 | float_number f_one (1.0); |
| 838 | ASSERT_JSON_NE (f_zero, f_one); |
| 839 | /* We don't yet test the more awkward cases e.g. NaN. */ |
| 840 | |
| 841 | /* Objects. */ |
| 842 | |
| 843 | // Empty object |
| 844 | // Self comparison should be 0 |
| 845 | object empty_obj_a; |
| 846 | ASSERT_JSON_EQ (empty_obj_a, empty_obj_a); |
| 847 | |
| 848 | // Instances of empty objects should compare equal to each other |
| 849 | object empty_obj_b; |
| 850 | ASSERT_JSON_EQ (empty_obj_a, empty_obj_b); |
| 851 | |
| 852 | // Object with one field: |
| 853 | object obj_1; |
| 854 | obj_1.set_string (key: "foo" , utf8_value: "bar" ); |
| 855 | // Self comparison should be 0 |
| 856 | ASSERT_JSON_EQ (obj_1, obj_1); |
| 857 | |
| 858 | // but should be different to an empty object: |
| 859 | ASSERT_JSON_NE (obj_1, empty_obj_a); |
| 860 | |
| 861 | // Another with one field, with same key/value: |
| 862 | object obj_2; |
| 863 | obj_2.set_string (key: "foo" , utf8_value: "bar" ); |
| 864 | ASSERT_JSON_EQ (obj_1, obj_2); |
| 865 | |
| 866 | // Same key, different value: |
| 867 | object obj_3; |
| 868 | obj_3.set_string (key: "foo" , utf8_value: "baz" ); |
| 869 | ASSERT_JSON_NE (obj_1, obj_3); |
| 870 | |
| 871 | // Adding an extra property: |
| 872 | obj_2.set_integer (key: "year" , v: 1066); |
| 873 | ASSERT_JSON_NE (obj_1, obj_2); |
| 874 | |
| 875 | /* Different insertion order, but same k-v pairs should be equal, |
| 876 | despite having different serialization. */ |
| 877 | object obj_4; |
| 878 | obj_4.set_integer (key: "year" , v: 1066); |
| 879 | obj_4.set_string (key: "foo" , utf8_value: "bar" ); |
| 880 | ASSERT_JSON_EQ (obj_2, obj_4); |
| 881 | ASSERT_PRINT_EQ (obj_2, false, "{\"foo\": \"bar\", \"year\": 1066}" ); |
| 882 | ASSERT_PRINT_EQ (obj_4, false, "{\"year\": 1066, \"foo\": \"bar\"}" ); |
| 883 | |
| 884 | /* Arrays. */ |
| 885 | |
| 886 | // Empty array |
| 887 | array empty_arr_a; |
| 888 | // Self comparison should be 0 |
| 889 | ASSERT_JSON_EQ (empty_arr_a, empty_arr_a); |
| 890 | |
| 891 | // Objects and arrays are different |
| 892 | ASSERT_JSON_NE (empty_obj_a, empty_arr_a); |
| 893 | |
| 894 | // Instances of empty arrays should compare equal to each other |
| 895 | array empty_arr_b; |
| 896 | ASSERT_JSON_EQ (empty_arr_a, empty_arr_b); |
| 897 | |
| 898 | // Array with one element: |
| 899 | array arr_1; |
| 900 | arr_1.append (v: std::make_unique<string> (args: "foo" )); |
| 901 | // Self comparison should be 0 |
| 902 | ASSERT_JSON_EQ (arr_1, arr_1); |
| 903 | |
| 904 | // but should be different to an empty array: |
| 905 | ASSERT_JSON_NE (arr_1, empty_arr_a); |
| 906 | |
| 907 | // Another with one element: |
| 908 | array arr_2; |
| 909 | arr_2.append (v: std::make_unique<string> (args: "foo" )); |
| 910 | ASSERT_JSON_EQ (arr_1, arr_2); |
| 911 | |
| 912 | // Adding an extra element: |
| 913 | arr_2.append (v: std::make_unique<string> (args: "bar" )); |
| 914 | ASSERT_JSON_NE (arr_1, arr_2); |
| 915 | } |
| 916 | |
| 917 | /* Run all of the selftests within this file. */ |
| 918 | |
| 919 | void |
| 920 | json_cc_tests () |
| 921 | { |
| 922 | test_object_get (); |
| 923 | test_writing_objects (); |
| 924 | test_writing_arrays (); |
| 925 | test_writing_float_numbers (); |
| 926 | test_writing_integer_numbers (); |
| 927 | test_writing_strings (); |
| 928 | test_writing_literals (); |
| 929 | test_formatting (); |
| 930 | test_comparisons (); |
| 931 | } |
| 932 | |
| 933 | } // namespace selftest |
| 934 | |
| 935 | #endif /* #if CHECKING_P */ |
| 936 | |