testFrontendJSON.cpp (20567B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/Maybe.h" // mozilla::Maybe 8 9 #include <string> 10 11 #include "js/AllocPolicy.h" // js::SystemAllocPolicy 12 #include "js/JSON.h" 13 #include "js/Vector.h" // js::Vector 14 #include "jsapi-tests/tests.h" 15 16 using namespace JS; 17 18 BEGIN_FRONTEND_TEST(testIsValidJSONLatin1) { 19 const char* source; 20 21 source = "true"; 22 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 23 strlen(source))); 24 25 source = "false"; 26 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 27 strlen(source))); 28 29 source = "null"; 30 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 31 strlen(source))); 32 33 source = "0"; 34 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 35 strlen(source))); 36 37 source = "1"; 38 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 39 strlen(source))); 40 41 source = "-1"; 42 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 43 strlen(source))); 44 45 source = "1.75"; 46 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 47 strlen(source))); 48 49 source = "9000000000"; 50 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 51 strlen(source))); 52 53 source = "\"foo\""; 54 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 55 strlen(source))); 56 57 source = "[]"; 58 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 59 strlen(source))); 60 61 source = "[1, true]"; 62 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 63 strlen(source))); 64 65 source = "{}"; 66 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 67 strlen(source))); 68 69 source = "{\"key\": 10}"; 70 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 71 strlen(source))); 72 73 source = "{\"key\": 10, \"prop\": 20}"; 74 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 75 strlen(source))); 76 77 source = "1 "; 78 CHECK(IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 79 strlen(source))); 80 81 // Invalid cases. 82 83 source = ""; 84 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 85 strlen(source))); 86 87 source = "1 1"; 88 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 89 strlen(source))); 90 91 source = ".1"; 92 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 93 strlen(source))); 94 95 source = "undefined"; 96 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 97 strlen(source))); 98 99 source = "TRUE"; 100 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 101 strlen(source))); 102 103 source = "'foo'"; 104 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 105 strlen(source))); 106 107 source = "["; 108 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 109 strlen(source))); 110 111 source = "{"; 112 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 113 strlen(source))); 114 115 source = "/a/"; 116 CHECK(!IsValidJSON(reinterpret_cast<const JS::Latin1Char*>(source), 117 strlen(source))); 118 119 return true; 120 } 121 122 END_TEST(testIsValidJSONLatin1) 123 124 BEGIN_FRONTEND_TEST(testIsValidJSONTwoBytes) { 125 const char16_t* source; 126 127 source = u"true"; 128 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 129 130 source = u"false"; 131 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 132 133 source = u"null"; 134 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 135 136 source = u"0"; 137 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 138 139 source = u"1"; 140 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 141 142 source = u"-1"; 143 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 144 145 source = u"1.75"; 146 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 147 148 source = u"9000000000"; 149 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 150 151 source = u"\"foo\""; 152 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 153 154 source = u"[]"; 155 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 156 157 source = u"[1, true]"; 158 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 159 160 source = u"{}"; 161 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 162 163 source = u"{\"key\": 10}"; 164 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 165 166 source = u"{\"key\": 10, \"prop\": 20}"; 167 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 168 169 source = u"1 "; 170 CHECK(IsValidJSON(source, std::char_traits<char16_t>::length(source))); 171 172 // Invalid cases. 173 174 source = u""; 175 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 176 177 source = u"1 1"; 178 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 179 180 source = u".1"; 181 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 182 183 source = u"undefined"; 184 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 185 186 source = u"TRUE"; 187 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 188 189 source = u"'foo'"; 190 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 191 192 source = u"["; 193 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 194 195 source = u"{"; 196 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 197 198 source = u"/a/"; 199 CHECK(!IsValidJSON(source, std::char_traits<char16_t>::length(source))); 200 201 return true; 202 } 203 204 END_TEST(testIsValidJSONTwoBytes) 205 206 BEGIN_FRONTEND_TEST(testParseJSONWithHandler) { 207 { 208 MyHandler handler; 209 210 const char* source = 211 "{ \"prop1\": 10.5, \"prop\\uff12\": [true, false, null, \"Ascii\", " 212 "\"\\u3042\\u3044\\u3046\", \"\\u0020\", \"\\u0080\"] }"; 213 CHECK(JS::ParseJSONWithHandler((const JS::Latin1Char*)source, 214 std::char_traits<char>::length(source), 215 &handler)); 216 217 size_t i = 0; 218 219 CHECK(handler.events[i++] == MyHandler::Event::StartObject); 220 221 // Non-escaped ASCII property name in Latin1 input should be passed with 222 // Latin1. 223 CHECK(handler.events[i++] == MyHandler::Event::Latin1Prop1); 224 225 CHECK(handler.events[i++] == MyHandler::Event::Number); 226 227 // Escaped non-Latin1 property name in Latin1 input should be passed with 228 // TwoBytes. 229 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2); 230 CHECK(handler.events[i++] == MyHandler::Event::StartArray); 231 CHECK(handler.events[i++] == MyHandler::Event::True); 232 CHECK(handler.events[i++] == MyHandler::Event::False); 233 CHECK(handler.events[i++] == MyHandler::Event::Null); 234 235 // Non-escaped ASCII string in Latin1 input should be passed with Latin1. 236 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str1); 237 238 // Escaped non-Latin1 string in Latin1 input should be passed with TwoBytes. 239 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2); 240 241 // Escaped ASCII-range string in Latin1 input should be passed with Latin1. 242 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3); 243 244 // Escaped Latin1-range string in Latin1 input should be passed with Latin1. 245 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4); 246 247 CHECK(handler.events[i++] == MyHandler::Event::EndArray); 248 CHECK(handler.events[i++] == MyHandler::Event::EndObject); 249 CHECK(handler.events.length() == i); 250 } 251 { 252 MyHandler handler; 253 254 const char* source = "{"; 255 CHECK(!JS::ParseJSONWithHandler((const JS::Latin1Char*)source, 256 std::char_traits<char>::length(source), 257 &handler)); 258 259 size_t i = 0; 260 261 CHECK(handler.events[i++] == MyHandler::Event::StartObject); 262 CHECK(handler.events[i++] == MyHandler::Event::Error); 263 CHECK(handler.events.length() == i); 264 } 265 266 { 267 MyHandler handler; 268 269 const char16_t* source = 270 u"{ \"prop1\": 10.5, \"prop\uff12\": [true, false, null, \"Ascii\", " 271 u"\"\\u3042\\u3044\\u3046\", \"\\u0020\", \"\\u0080\"] }"; 272 CHECK(JS::ParseJSONWithHandler( 273 source, std::char_traits<char16_t>::length(source), &handler)); 274 275 size_t i = 0; 276 277 CHECK(handler.events[i++] == MyHandler::Event::StartObject); 278 279 // Non-escaped ASCII property name in TwoBytes input should be passed with 280 // TwoBytes. 281 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp1); 282 283 CHECK(handler.events[i++] == MyHandler::Event::Number); 284 285 // Escaped non-Latin1 property name in TwoBytes input should be passed with 286 // TwoBytes. 287 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2); 288 289 CHECK(handler.events[i++] == MyHandler::Event::StartArray); 290 CHECK(handler.events[i++] == MyHandler::Event::True); 291 CHECK(handler.events[i++] == MyHandler::Event::False); 292 CHECK(handler.events[i++] == MyHandler::Event::Null); 293 294 // Non-escaped ASCII string in TwoBytes input should be passed with 295 // TwoBytes. 296 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr1); 297 298 // Escaped non-Latin1 string in TwoBytes input should be passed with 299 // TwoBytes. 300 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2); 301 302 // Escaped ASCII-range string in TwoBytes input should be passed with 303 // Latin1. 304 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3); 305 306 // Escaped Latin1-range string in TwoBytes input should be passed with 307 // Latin1. 308 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4); 309 310 CHECK(handler.events[i++] == MyHandler::Event::EndArray); 311 CHECK(handler.events[i++] == MyHandler::Event::EndObject); 312 CHECK(handler.events.length() == i); 313 } 314 315 { 316 MyHandler handler; 317 318 const char16_t* source = u"{"; 319 CHECK(!JS::ParseJSONWithHandler( 320 source, std::char_traits<char16_t>::length(source), &handler)); 321 322 size_t i = 0; 323 324 CHECK(handler.events[i++] == MyHandler::Event::StartObject); 325 CHECK(handler.events[i++] == MyHandler::Event::Error); 326 CHECK(handler.events.length() == i); 327 } 328 329 // Verify the failure case is handled properly and no methods are called 330 // after the failure. 331 332 bool checkedLast = false; 333 for (size_t failAt = 1; !checkedLast; failAt++) { 334 MyHandler handler; 335 handler.failAt.emplace(failAt); 336 337 const char* source = 338 "{ \"prop1\": 10.5, \"prop\\uff12\": [true, false, null, \"Ascii\", " 339 "\"\\u3042\\u3044\\u3046\", \"\\u0020\", \"\\u0080\"] }"; 340 CHECK(!JS::ParseJSONWithHandler((const JS::Latin1Char*)source, 341 std::char_traits<char>::length(source), 342 &handler)); 343 344 CHECK(handler.events.length() == failAt); 345 346 size_t i = 0; 347 348 CHECK(handler.events[i++] == MyHandler::Event::StartObject); 349 if (i >= failAt) { 350 continue; 351 } 352 CHECK(handler.events[i++] == MyHandler::Event::Latin1Prop1); 353 if (i >= failAt) { 354 continue; 355 } 356 CHECK(handler.events[i++] == MyHandler::Event::Number); 357 if (i >= failAt) { 358 continue; 359 } 360 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2); 361 if (i >= failAt) { 362 continue; 363 } 364 CHECK(handler.events[i++] == MyHandler::Event::StartArray); 365 if (i >= failAt) { 366 continue; 367 } 368 CHECK(handler.events[i++] == MyHandler::Event::True); 369 if (i >= failAt) { 370 continue; 371 } 372 CHECK(handler.events[i++] == MyHandler::Event::False); 373 if (i >= failAt) { 374 continue; 375 } 376 CHECK(handler.events[i++] == MyHandler::Event::Null); 377 if (i >= failAt) { 378 continue; 379 } 380 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str1); 381 if (i >= failAt) { 382 continue; 383 } 384 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2); 385 if (i >= failAt) { 386 continue; 387 } 388 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3); 389 if (i >= failAt) { 390 continue; 391 } 392 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4); 393 if (i >= failAt) { 394 continue; 395 } 396 CHECK(handler.events[i++] == MyHandler::Event::EndArray); 397 if (i >= failAt) { 398 continue; 399 } 400 CHECK(handler.events[i++] == MyHandler::Event::EndObject); 401 checkedLast = true; 402 } 403 404 checkedLast = false; 405 for (size_t failAt = 1; !checkedLast; failAt++) { 406 MyHandler handler; 407 handler.failAt.emplace(failAt); 408 409 const char16_t* source = 410 u"{ \"prop1\": 10.5, \"prop\uff12\": [true, false, null, \"Ascii\", " 411 u"\"\\u3042\\u3044\\u3046\", \"\\u0020\", \"\\u0080\"] }"; 412 CHECK(!JS::ParseJSONWithHandler( 413 source, std::char_traits<char16_t>::length(source), &handler)); 414 415 CHECK(handler.events.length() == failAt); 416 417 size_t i = 0; 418 419 CHECK(handler.events[i++] == MyHandler::Event::StartObject); 420 if (i >= failAt) { 421 continue; 422 } 423 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp1); 424 if (i >= failAt) { 425 continue; 426 } 427 CHECK(handler.events[i++] == MyHandler::Event::Number); 428 if (i >= failAt) { 429 continue; 430 } 431 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesProp2); 432 if (i >= failAt) { 433 continue; 434 } 435 CHECK(handler.events[i++] == MyHandler::Event::StartArray); 436 if (i >= failAt) { 437 continue; 438 } 439 CHECK(handler.events[i++] == MyHandler::Event::True); 440 if (i >= failAt) { 441 continue; 442 } 443 CHECK(handler.events[i++] == MyHandler::Event::False); 444 if (i >= failAt) { 445 continue; 446 } 447 CHECK(handler.events[i++] == MyHandler::Event::Null); 448 if (i >= failAt) { 449 continue; 450 } 451 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr1); 452 if (i >= failAt) { 453 continue; 454 } 455 CHECK(handler.events[i++] == MyHandler::Event::TwoBytesStr2); 456 if (i >= failAt) { 457 continue; 458 } 459 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str3); 460 if (i >= failAt) { 461 continue; 462 } 463 CHECK(handler.events[i++] == MyHandler::Event::Latin1Str4); 464 if (i >= failAt) { 465 continue; 466 } 467 CHECK(handler.events[i++] == MyHandler::Event::EndArray); 468 if (i >= failAt) { 469 continue; 470 } 471 CHECK(handler.events[i++] == MyHandler::Event::EndObject); 472 checkedLast = true; 473 } 474 475 { 476 const size_t failAt = 1; 477 MyHandler handler; 478 const char16_t* source; 479 480 #define IMMEDIATE_FAIL(json) \ 481 handler.failAt.emplace(failAt); \ 482 source = json; \ 483 CHECK(!JS::ParseJSONWithHandler( \ 484 source, std::char_traits<char16_t>::length(source), &handler)); \ 485 CHECK(handler.events.length() == failAt); \ 486 handler.events.clear(); 487 488 IMMEDIATE_FAIL(u"{"); 489 IMMEDIATE_FAIL(u"["); 490 IMMEDIATE_FAIL(u"\"string\""); 491 IMMEDIATE_FAIL(u"1"); 492 IMMEDIATE_FAIL(u"true"); 493 IMMEDIATE_FAIL(u"null"); 494 495 #undef IMMEDIATE_FAIL 496 } 497 498 return true; 499 } 500 501 class MyHandler : public JS::JSONParseHandler { 502 public: 503 enum class Event { 504 Uninitialized = 0, 505 506 StartObject, 507 Latin1Prop1, 508 TwoBytesProp1, 509 Number, 510 TwoBytesProp2, 511 StartArray, 512 True, 513 False, 514 Null, 515 Latin1Str1, 516 TwoBytesStr1, 517 TwoBytesStr2, 518 Latin1Str3, 519 Latin1Str4, 520 EndArray, 521 EndObject, 522 Error, 523 UnexpectedNumber, 524 UnexpectedLatin1Prop, 525 UnexpectedTwoBytesProp, 526 UnexpectedLatin1String, 527 UnexpectedTwoBytesString, 528 }; 529 js::Vector<Event, 0, js::SystemAllocPolicy> events; 530 mozilla::Maybe<size_t> failAt; 531 532 MyHandler() {} 533 virtual ~MyHandler() {} 534 535 bool startObject() override { 536 MOZ_ALWAYS_TRUE(events.append(Event::StartObject)); 537 if (failAt.isSome() && events.length() == *failAt) { 538 failAt.reset(); 539 return false; 540 } 541 return true; 542 } 543 bool propertyName(const JS::Latin1Char* name, size_t length) override { 544 if (length == 5 && name[0] == 'p' && name[1] == 'r' && name[2] == 'o' && 545 name[3] == 'p' && name[4] == '1') { 546 MOZ_ALWAYS_TRUE(events.append(Event::Latin1Prop1)); 547 } else { 548 MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedLatin1Prop)); 549 } 550 if (failAt.isSome() && events.length() == *failAt) { 551 failAt.reset(); 552 return false; 553 } 554 return true; 555 } 556 bool propertyName(const char16_t* name, size_t length) override { 557 if (length == 5 && name[0] == 'p' && name[1] == 'r' && name[2] == 'o' && 558 name[3] == 'p' && name[4] == '1') { 559 MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesProp1)); 560 } else if (length == 5 && name[0] == 'p' && name[1] == 'r' && 561 name[2] == 'o' && name[3] == 'p' && name[4] == 0xff12) { 562 MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesProp2)); 563 } else { 564 MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedTwoBytesProp)); 565 } 566 if (failAt.isSome() && events.length() == *failAt) { 567 failAt.reset(); 568 return false; 569 } 570 return true; 571 } 572 bool endObject() override { 573 MOZ_ALWAYS_TRUE(events.append(Event::EndObject)); 574 if (failAt.isSome() && events.length() == *failAt) { 575 failAt.reset(); 576 return false; 577 } 578 return true; 579 } 580 581 bool startArray() override { 582 MOZ_ALWAYS_TRUE(events.append(Event::StartArray)); 583 if (failAt.isSome() && events.length() == *failAt) { 584 failAt.reset(); 585 return false; 586 } 587 return true; 588 } 589 bool endArray() override { 590 MOZ_ALWAYS_TRUE(events.append(Event::EndArray)); 591 if (failAt.isSome() && events.length() == *failAt) { 592 failAt.reset(); 593 return false; 594 } 595 return true; 596 } 597 598 bool stringValue(const JS::Latin1Char* name, size_t length) override { 599 if (length == 5 && name[0] == 'A' && name[1] == 's' && name[2] == 'c' && 600 name[3] == 'i' && name[4] == 'i') { 601 MOZ_ALWAYS_TRUE(events.append(Event::Latin1Str1)); 602 } else if (length == 1 && name[0] == ' ') { 603 MOZ_ALWAYS_TRUE(events.append(Event::Latin1Str3)); 604 } else if (length == 1 && name[0] == 0x80) { 605 MOZ_ALWAYS_TRUE(events.append(Event::Latin1Str4)); 606 } else { 607 MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedLatin1String)); 608 } 609 if (failAt.isSome() && events.length() == *failAt) { 610 failAt.reset(); 611 return false; 612 } 613 return true; 614 } 615 bool stringValue(const char16_t* name, size_t length) override { 616 if (length == 5 && name[0] == 'A' && name[1] == 's' && name[2] == 'c' && 617 name[3] == 'i' && name[4] == 'i') { 618 MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesStr1)); 619 } else if (length == 3 && name[0] == 0x3042 && name[1] == 0x3044 && 620 name[2] == 0x3046) { 621 MOZ_ALWAYS_TRUE(events.append(Event::TwoBytesStr2)); 622 } else { 623 MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedTwoBytesString)); 624 } 625 if (failAt.isSome() && events.length() == *failAt) { 626 failAt.reset(); 627 return false; 628 } 629 return true; 630 } 631 bool numberValue(double d) override { 632 if (d == 10.5) { 633 MOZ_ALWAYS_TRUE(events.append(Event::Number)); 634 } else { 635 MOZ_ALWAYS_TRUE(events.append(Event::UnexpectedNumber)); 636 } 637 if (failAt.isSome() && events.length() == *failAt) { 638 failAt.reset(); 639 return false; 640 } 641 return true; 642 } 643 bool booleanValue(bool v) override { 644 if (v) { 645 MOZ_ALWAYS_TRUE(events.append(Event::True)); 646 } else { 647 MOZ_ALWAYS_TRUE(events.append(Event::False)); 648 } 649 if (failAt.isSome() && events.length() == *failAt) { 650 failAt.reset(); 651 return false; 652 } 653 return true; 654 } 655 bool nullValue() override { 656 MOZ_ALWAYS_TRUE(events.append(Event::Null)); 657 if (failAt.isSome() && events.length() == *failAt) { 658 failAt.reset(); 659 return false; 660 } 661 return true; 662 } 663 664 void error(const char* msg, uint32_t line, uint32_t column) override { 665 MOZ_ALWAYS_TRUE(events.append(Event::Error)); 666 } 667 }; 668 669 END_TEST(testParseJSONWithHandler)