js/src/jsonparser.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jsonparser.h"
michael@0 8
michael@0 9 #include "mozilla/RangedPtr.h"
michael@0 10
michael@0 11 #include <ctype.h>
michael@0 12
michael@0 13 #include "jsarray.h"
michael@0 14 #include "jscompartment.h"
michael@0 15 #include "jsnum.h"
michael@0 16 #include "jsprf.h"
michael@0 17
michael@0 18 #include "vm/StringBuffer.h"
michael@0 19
michael@0 20 #include "jsobjinlines.h"
michael@0 21
michael@0 22 using namespace js;
michael@0 23
michael@0 24 using mozilla::RangedPtr;
michael@0 25
michael@0 26 JSONParser::~JSONParser()
michael@0 27 {
michael@0 28 for (size_t i = 0; i < stack.length(); i++) {
michael@0 29 if (stack[i].state == FinishArrayElement)
michael@0 30 js_delete(&stack[i].elements());
michael@0 31 else
michael@0 32 js_delete(&stack[i].properties());
michael@0 33 }
michael@0 34
michael@0 35 for (size_t i = 0; i < freeElements.length(); i++)
michael@0 36 js_delete(freeElements[i]);
michael@0 37
michael@0 38 for (size_t i = 0; i < freeProperties.length(); i++)
michael@0 39 js_delete(freeProperties[i]);
michael@0 40 }
michael@0 41
michael@0 42 void
michael@0 43 JSONParser::trace(JSTracer *trc)
michael@0 44 {
michael@0 45 for (size_t i = 0; i < stack.length(); i++) {
michael@0 46 if (stack[i].state == FinishArrayElement) {
michael@0 47 ElementVector &elements = stack[i].elements();
michael@0 48 for (size_t j = 0; j < elements.length(); j++)
michael@0 49 gc::MarkValueRoot(trc, &elements[j], "JSONParser element");
michael@0 50 } else {
michael@0 51 PropertyVector &properties = stack[i].properties();
michael@0 52 for (size_t j = 0; j < properties.length(); j++) {
michael@0 53 gc::MarkValueRoot(trc, &properties[j].value, "JSONParser property value");
michael@0 54 gc::MarkIdRoot(trc, &properties[j].id, "JSONParser property id");
michael@0 55 }
michael@0 56 }
michael@0 57 }
michael@0 58 }
michael@0 59
michael@0 60 void
michael@0 61 JSONParser::getTextPosition(uint32_t *column, uint32_t *line)
michael@0 62 {
michael@0 63 ConstTwoByteChars ptr = begin;
michael@0 64 uint32_t col = 1;
michael@0 65 uint32_t row = 1;
michael@0 66 for (; ptr < current; ptr++) {
michael@0 67 if (*ptr == '\n' || *ptr == '\r') {
michael@0 68 ++row;
michael@0 69 col = 1;
michael@0 70 // \r\n is treated as a single newline.
michael@0 71 if (ptr + 1 < current && *ptr == '\r' && *(ptr + 1) == '\n')
michael@0 72 ++ptr;
michael@0 73 } else {
michael@0 74 ++col;
michael@0 75 }
michael@0 76 }
michael@0 77 *column = col;
michael@0 78 *line = row;
michael@0 79 }
michael@0 80
michael@0 81 void
michael@0 82 JSONParser::error(const char *msg)
michael@0 83 {
michael@0 84 if (errorHandling == RaiseError) {
michael@0 85 uint32_t column = 1, line = 1;
michael@0 86 getTextPosition(&column, &line);
michael@0 87
michael@0 88 const size_t MaxWidth = sizeof("4294967295");
michael@0 89 char columnNumber[MaxWidth];
michael@0 90 JS_snprintf(columnNumber, sizeof columnNumber, "%lu", column);
michael@0 91 char lineNumber[MaxWidth];
michael@0 92 JS_snprintf(lineNumber, sizeof lineNumber, "%lu", line);
michael@0 93
michael@0 94 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_JSON_BAD_PARSE,
michael@0 95 msg, lineNumber, columnNumber);
michael@0 96 }
michael@0 97 }
michael@0 98
michael@0 99 bool
michael@0 100 JSONParser::errorReturn()
michael@0 101 {
michael@0 102 return errorHandling == NoError;
michael@0 103 }
michael@0 104
michael@0 105 template<JSONParser::StringType ST>
michael@0 106 JSONParser::Token
michael@0 107 JSONParser::readString()
michael@0 108 {
michael@0 109 JS_ASSERT(current < end);
michael@0 110 JS_ASSERT(*current == '"');
michael@0 111
michael@0 112 /*
michael@0 113 * JSONString:
michael@0 114 * /^"([^\u0000-\u001F"\\]|\\(["/\\bfnrt]|u[0-9a-fA-F]{4}))*"$/
michael@0 115 */
michael@0 116
michael@0 117 if (++current == end) {
michael@0 118 error("unterminated string literal");
michael@0 119 return token(Error);
michael@0 120 }
michael@0 121
michael@0 122 /*
michael@0 123 * Optimization: if the source contains no escaped characters, create the
michael@0 124 * string directly from the source text.
michael@0 125 */
michael@0 126 RangedPtr<const jschar> start = current;
michael@0 127 for (; current < end; current++) {
michael@0 128 if (*current == '"') {
michael@0 129 size_t length = current - start;
michael@0 130 current++;
michael@0 131 JSFlatString *str = (ST == JSONParser::PropertyName)
michael@0 132 ? AtomizeChars(cx, start.get(), length)
michael@0 133 : js_NewStringCopyN<CanGC>(cx, start.get(), length);
michael@0 134 if (!str)
michael@0 135 return token(OOM);
michael@0 136 return stringToken(str);
michael@0 137 }
michael@0 138
michael@0 139 if (*current == '\\')
michael@0 140 break;
michael@0 141
michael@0 142 if (*current <= 0x001F) {
michael@0 143 error("bad control character in string literal");
michael@0 144 return token(Error);
michael@0 145 }
michael@0 146 }
michael@0 147
michael@0 148 /*
michael@0 149 * Slow case: string contains escaped characters. Copy a maximal sequence
michael@0 150 * of unescaped characters into a temporary buffer, then an escaped
michael@0 151 * character, and repeat until the entire string is consumed.
michael@0 152 */
michael@0 153 StringBuffer buffer(cx);
michael@0 154 do {
michael@0 155 if (start < current && !buffer.append(start.get(), current.get()))
michael@0 156 return token(OOM);
michael@0 157
michael@0 158 if (current >= end)
michael@0 159 break;
michael@0 160
michael@0 161 jschar c = *current++;
michael@0 162 if (c == '"') {
michael@0 163 JSFlatString *str = (ST == JSONParser::PropertyName)
michael@0 164 ? buffer.finishAtom()
michael@0 165 : buffer.finishString();
michael@0 166 if (!str)
michael@0 167 return token(OOM);
michael@0 168 return stringToken(str);
michael@0 169 }
michael@0 170
michael@0 171 if (c != '\\') {
michael@0 172 --current;
michael@0 173 error("bad character in string literal");
michael@0 174 return token(Error);
michael@0 175 }
michael@0 176
michael@0 177 if (current >= end)
michael@0 178 break;
michael@0 179
michael@0 180 switch (*current++) {
michael@0 181 case '"': c = '"'; break;
michael@0 182 case '/': c = '/'; break;
michael@0 183 case '\\': c = '\\'; break;
michael@0 184 case 'b': c = '\b'; break;
michael@0 185 case 'f': c = '\f'; break;
michael@0 186 case 'n': c = '\n'; break;
michael@0 187 case 'r': c = '\r'; break;
michael@0 188 case 't': c = '\t'; break;
michael@0 189
michael@0 190 case 'u':
michael@0 191 if (end - current < 4 ||
michael@0 192 !(JS7_ISHEX(current[0]) &&
michael@0 193 JS7_ISHEX(current[1]) &&
michael@0 194 JS7_ISHEX(current[2]) &&
michael@0 195 JS7_ISHEX(current[3])))
michael@0 196 {
michael@0 197 // Point to the first non-hexadecimal character (which may be
michael@0 198 // missing).
michael@0 199 if (current == end || !JS7_ISHEX(current[0]))
michael@0 200 ; // already at correct location
michael@0 201 else if (current + 1 == end || !JS7_ISHEX(current[1]))
michael@0 202 current += 1;
michael@0 203 else if (current + 2 == end || !JS7_ISHEX(current[2]))
michael@0 204 current += 2;
michael@0 205 else if (current + 3 == end || !JS7_ISHEX(current[3]))
michael@0 206 current += 3;
michael@0 207 else
michael@0 208 MOZ_ASSUME_UNREACHABLE("logic error determining first erroneous character");
michael@0 209
michael@0 210 error("bad Unicode escape");
michael@0 211 return token(Error);
michael@0 212 }
michael@0 213 c = (JS7_UNHEX(current[0]) << 12)
michael@0 214 | (JS7_UNHEX(current[1]) << 8)
michael@0 215 | (JS7_UNHEX(current[2]) << 4)
michael@0 216 | (JS7_UNHEX(current[3]));
michael@0 217 current += 4;
michael@0 218 break;
michael@0 219
michael@0 220 default:
michael@0 221 current--;
michael@0 222 error("bad escaped character");
michael@0 223 return token(Error);
michael@0 224 }
michael@0 225 if (!buffer.append(c))
michael@0 226 return token(OOM);
michael@0 227
michael@0 228 start = current;
michael@0 229 for (; current < end; current++) {
michael@0 230 if (*current == '"' || *current == '\\' || *current <= 0x001F)
michael@0 231 break;
michael@0 232 }
michael@0 233 } while (current < end);
michael@0 234
michael@0 235 error("unterminated string");
michael@0 236 return token(Error);
michael@0 237 }
michael@0 238
michael@0 239 JSONParser::Token
michael@0 240 JSONParser::readNumber()
michael@0 241 {
michael@0 242 JS_ASSERT(current < end);
michael@0 243 JS_ASSERT(JS7_ISDEC(*current) || *current == '-');
michael@0 244
michael@0 245 /*
michael@0 246 * JSONNumber:
michael@0 247 * /^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][\+\-]?[0-9]+)?$/
michael@0 248 */
michael@0 249
michael@0 250 bool negative = *current == '-';
michael@0 251
michael@0 252 /* -? */
michael@0 253 if (negative && ++current == end) {
michael@0 254 error("no number after minus sign");
michael@0 255 return token(Error);
michael@0 256 }
michael@0 257
michael@0 258 const RangedPtr<const jschar> digitStart = current;
michael@0 259
michael@0 260 /* 0|[1-9][0-9]+ */
michael@0 261 if (!JS7_ISDEC(*current)) {
michael@0 262 error("unexpected non-digit");
michael@0 263 return token(Error);
michael@0 264 }
michael@0 265 if (*current++ != '0') {
michael@0 266 for (; current < end; current++) {
michael@0 267 if (!JS7_ISDEC(*current))
michael@0 268 break;
michael@0 269 }
michael@0 270 }
michael@0 271
michael@0 272 /* Fast path: no fractional or exponent part. */
michael@0 273 if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
michael@0 274 TwoByteChars chars(digitStart.get(), current - digitStart);
michael@0 275 if (chars.length() < strlen("9007199254740992")) {
michael@0 276 // If the decimal number is shorter than the length of 2**53, (the
michael@0 277 // largest number a double can represent with integral precision),
michael@0 278 // parse it using a decimal-only parser. This comparison is
michael@0 279 // conservative but faster than a fully-precise check.
michael@0 280 double d = ParseDecimalNumber(chars);
michael@0 281 return numberToken(negative ? -d : d);
michael@0 282 }
michael@0 283
michael@0 284 double d;
michael@0 285 const jschar *dummy;
michael@0 286 if (!GetPrefixInteger(cx, digitStart.get(), current.get(), 10, &dummy, &d))
michael@0 287 return token(OOM);
michael@0 288 JS_ASSERT(current == dummy);
michael@0 289 return numberToken(negative ? -d : d);
michael@0 290 }
michael@0 291
michael@0 292 /* (\.[0-9]+)? */
michael@0 293 if (current < end && *current == '.') {
michael@0 294 if (++current == end) {
michael@0 295 error("missing digits after decimal point");
michael@0 296 return token(Error);
michael@0 297 }
michael@0 298 if (!JS7_ISDEC(*current)) {
michael@0 299 error("unterminated fractional number");
michael@0 300 return token(Error);
michael@0 301 }
michael@0 302 while (++current < end) {
michael@0 303 if (!JS7_ISDEC(*current))
michael@0 304 break;
michael@0 305 }
michael@0 306 }
michael@0 307
michael@0 308 /* ([eE][\+\-]?[0-9]+)? */
michael@0 309 if (current < end && (*current == 'e' || *current == 'E')) {
michael@0 310 if (++current == end) {
michael@0 311 error("missing digits after exponent indicator");
michael@0 312 return token(Error);
michael@0 313 }
michael@0 314 if (*current == '+' || *current == '-') {
michael@0 315 if (++current == end) {
michael@0 316 error("missing digits after exponent sign");
michael@0 317 return token(Error);
michael@0 318 }
michael@0 319 }
michael@0 320 if (!JS7_ISDEC(*current)) {
michael@0 321 error("exponent part is missing a number");
michael@0 322 return token(Error);
michael@0 323 }
michael@0 324 while (++current < end) {
michael@0 325 if (!JS7_ISDEC(*current))
michael@0 326 break;
michael@0 327 }
michael@0 328 }
michael@0 329
michael@0 330 double d;
michael@0 331 const jschar *finish;
michael@0 332 if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d))
michael@0 333 return token(OOM);
michael@0 334 JS_ASSERT(current == finish);
michael@0 335 return numberToken(negative ? -d : d);
michael@0 336 }
michael@0 337
michael@0 338 static inline bool
michael@0 339 IsJSONWhitespace(jschar c)
michael@0 340 {
michael@0 341 return c == '\t' || c == '\r' || c == '\n' || c == ' ';
michael@0 342 }
michael@0 343
michael@0 344 JSONParser::Token
michael@0 345 JSONParser::advance()
michael@0 346 {
michael@0 347 while (current < end && IsJSONWhitespace(*current))
michael@0 348 current++;
michael@0 349 if (current >= end) {
michael@0 350 error("unexpected end of data");
michael@0 351 return token(Error);
michael@0 352 }
michael@0 353
michael@0 354 switch (*current) {
michael@0 355 case '"':
michael@0 356 return readString<LiteralValue>();
michael@0 357
michael@0 358 case '-':
michael@0 359 case '0':
michael@0 360 case '1':
michael@0 361 case '2':
michael@0 362 case '3':
michael@0 363 case '4':
michael@0 364 case '5':
michael@0 365 case '6':
michael@0 366 case '7':
michael@0 367 case '8':
michael@0 368 case '9':
michael@0 369 return readNumber();
michael@0 370
michael@0 371 case 't':
michael@0 372 if (end - current < 4 || current[1] != 'r' || current[2] != 'u' || current[3] != 'e') {
michael@0 373 error("unexpected keyword");
michael@0 374 return token(Error);
michael@0 375 }
michael@0 376 current += 4;
michael@0 377 return token(True);
michael@0 378
michael@0 379 case 'f':
michael@0 380 if (end - current < 5 ||
michael@0 381 current[1] != 'a' || current[2] != 'l' || current[3] != 's' || current[4] != 'e')
michael@0 382 {
michael@0 383 error("unexpected keyword");
michael@0 384 return token(Error);
michael@0 385 }
michael@0 386 current += 5;
michael@0 387 return token(False);
michael@0 388
michael@0 389 case 'n':
michael@0 390 if (end - current < 4 || current[1] != 'u' || current[2] != 'l' || current[3] != 'l') {
michael@0 391 error("unexpected keyword");
michael@0 392 return token(Error);
michael@0 393 }
michael@0 394 current += 4;
michael@0 395 return token(Null);
michael@0 396
michael@0 397 case '[':
michael@0 398 current++;
michael@0 399 return token(ArrayOpen);
michael@0 400 case ']':
michael@0 401 current++;
michael@0 402 return token(ArrayClose);
michael@0 403
michael@0 404 case '{':
michael@0 405 current++;
michael@0 406 return token(ObjectOpen);
michael@0 407 case '}':
michael@0 408 current++;
michael@0 409 return token(ObjectClose);
michael@0 410
michael@0 411 case ',':
michael@0 412 current++;
michael@0 413 return token(Comma);
michael@0 414
michael@0 415 case ':':
michael@0 416 current++;
michael@0 417 return token(Colon);
michael@0 418
michael@0 419 default:
michael@0 420 error("unexpected character");
michael@0 421 return token(Error);
michael@0 422 }
michael@0 423 }
michael@0 424
michael@0 425 JSONParser::Token
michael@0 426 JSONParser::advanceAfterObjectOpen()
michael@0 427 {
michael@0 428 JS_ASSERT(current[-1] == '{');
michael@0 429
michael@0 430 while (current < end && IsJSONWhitespace(*current))
michael@0 431 current++;
michael@0 432 if (current >= end) {
michael@0 433 error("end of data while reading object contents");
michael@0 434 return token(Error);
michael@0 435 }
michael@0 436
michael@0 437 if (*current == '"')
michael@0 438 return readString<PropertyName>();
michael@0 439
michael@0 440 if (*current == '}') {
michael@0 441 current++;
michael@0 442 return token(ObjectClose);
michael@0 443 }
michael@0 444
michael@0 445 error("expected property name or '}'");
michael@0 446 return token(Error);
michael@0 447 }
michael@0 448
michael@0 449 static inline void
michael@0 450 AssertPastValue(const RangedPtr<const jschar> current)
michael@0 451 {
michael@0 452 /*
michael@0 453 * We're past an arbitrary JSON value, so the previous character is
michael@0 454 * *somewhat* constrained, even if this assertion is pretty broad. Don't
michael@0 455 * knock it till you tried it: this assertion *did* catch a bug once.
michael@0 456 */
michael@0 457 JS_ASSERT((current[-1] == 'l' &&
michael@0 458 current[-2] == 'l' &&
michael@0 459 current[-3] == 'u' &&
michael@0 460 current[-4] == 'n') ||
michael@0 461 (current[-1] == 'e' &&
michael@0 462 current[-2] == 'u' &&
michael@0 463 current[-3] == 'r' &&
michael@0 464 current[-4] == 't') ||
michael@0 465 (current[-1] == 'e' &&
michael@0 466 current[-2] == 's' &&
michael@0 467 current[-3] == 'l' &&
michael@0 468 current[-4] == 'a' &&
michael@0 469 current[-5] == 'f') ||
michael@0 470 current[-1] == '}' ||
michael@0 471 current[-1] == ']' ||
michael@0 472 current[-1] == '"' ||
michael@0 473 JS7_ISDEC(current[-1]));
michael@0 474 }
michael@0 475
michael@0 476 JSONParser::Token
michael@0 477 JSONParser::advanceAfterArrayElement()
michael@0 478 {
michael@0 479 AssertPastValue(current);
michael@0 480
michael@0 481 while (current < end && IsJSONWhitespace(*current))
michael@0 482 current++;
michael@0 483 if (current >= end) {
michael@0 484 error("end of data when ',' or ']' was expected");
michael@0 485 return token(Error);
michael@0 486 }
michael@0 487
michael@0 488 if (*current == ',') {
michael@0 489 current++;
michael@0 490 return token(Comma);
michael@0 491 }
michael@0 492
michael@0 493 if (*current == ']') {
michael@0 494 current++;
michael@0 495 return token(ArrayClose);
michael@0 496 }
michael@0 497
michael@0 498 error("expected ',' or ']' after array element");
michael@0 499 return token(Error);
michael@0 500 }
michael@0 501
michael@0 502 JSONParser::Token
michael@0 503 JSONParser::advancePropertyName()
michael@0 504 {
michael@0 505 JS_ASSERT(current[-1] == ',');
michael@0 506
michael@0 507 while (current < end && IsJSONWhitespace(*current))
michael@0 508 current++;
michael@0 509 if (current >= end) {
michael@0 510 error("end of data when property name was expected");
michael@0 511 return token(Error);
michael@0 512 }
michael@0 513
michael@0 514 if (*current == '"')
michael@0 515 return readString<PropertyName>();
michael@0 516
michael@0 517 error("expected double-quoted property name");
michael@0 518 return token(Error);
michael@0 519 }
michael@0 520
michael@0 521 JSONParser::Token
michael@0 522 JSONParser::advancePropertyColon()
michael@0 523 {
michael@0 524 JS_ASSERT(current[-1] == '"');
michael@0 525
michael@0 526 while (current < end && IsJSONWhitespace(*current))
michael@0 527 current++;
michael@0 528 if (current >= end) {
michael@0 529 error("end of data after property name when ':' was expected");
michael@0 530 return token(Error);
michael@0 531 }
michael@0 532
michael@0 533 if (*current == ':') {
michael@0 534 current++;
michael@0 535 return token(Colon);
michael@0 536 }
michael@0 537
michael@0 538 error("expected ':' after property name in object");
michael@0 539 return token(Error);
michael@0 540 }
michael@0 541
michael@0 542 JSONParser::Token
michael@0 543 JSONParser::advanceAfterProperty()
michael@0 544 {
michael@0 545 AssertPastValue(current);
michael@0 546
michael@0 547 while (current < end && IsJSONWhitespace(*current))
michael@0 548 current++;
michael@0 549 if (current >= end) {
michael@0 550 error("end of data after property value in object");
michael@0 551 return token(Error);
michael@0 552 }
michael@0 553
michael@0 554 if (*current == ',') {
michael@0 555 current++;
michael@0 556 return token(Comma);
michael@0 557 }
michael@0 558
michael@0 559 if (*current == '}') {
michael@0 560 current++;
michael@0 561 return token(ObjectClose);
michael@0 562 }
michael@0 563
michael@0 564 error("expected ',' or '}' after property value in object");
michael@0 565 return token(Error);
michael@0 566 }
michael@0 567
michael@0 568 JSObject *
michael@0 569 JSONParser::createFinishedObject(PropertyVector &properties)
michael@0 570 {
michael@0 571 /*
michael@0 572 * Look for an existing cached type and shape for objects with this set of
michael@0 573 * properties.
michael@0 574 */
michael@0 575 {
michael@0 576 JSObject *obj = cx->compartment()->types.newTypedObject(cx, properties.begin(),
michael@0 577 properties.length());
michael@0 578 if (obj)
michael@0 579 return obj;
michael@0 580 }
michael@0 581
michael@0 582 /*
michael@0 583 * Make a new object sized for the given number of properties and fill its
michael@0 584 * shape in manually.
michael@0 585 */
michael@0 586 gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length());
michael@0 587 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
michael@0 588 if (!obj)
michael@0 589 return nullptr;
michael@0 590
michael@0 591 RootedId propid(cx);
michael@0 592 RootedValue value(cx);
michael@0 593
michael@0 594 for (size_t i = 0; i < properties.length(); i++) {
michael@0 595 propid = properties[i].id;
michael@0 596 value = properties[i].value;
michael@0 597 if (!DefineNativeProperty(cx, obj, propid, value, JS_PropertyStub, JS_StrictPropertyStub,
michael@0 598 JSPROP_ENUMERATE)) {
michael@0 599 return nullptr;
michael@0 600 }
michael@0 601 }
michael@0 602
michael@0 603 /*
michael@0 604 * Try to assign a new type to the object with type information for its
michael@0 605 * properties, and update the initializer type object cache with this
michael@0 606 * object's final shape.
michael@0 607 */
michael@0 608 cx->compartment()->types.fixObjectType(cx, obj);
michael@0 609
michael@0 610 return obj;
michael@0 611 }
michael@0 612
michael@0 613 inline bool
michael@0 614 JSONParser::finishObject(MutableHandleValue vp, PropertyVector &properties)
michael@0 615 {
michael@0 616 JS_ASSERT(&properties == &stack.back().properties());
michael@0 617
michael@0 618 JSObject *obj = createFinishedObject(properties);
michael@0 619 if (!obj)
michael@0 620 return false;
michael@0 621
michael@0 622 vp.setObject(*obj);
michael@0 623 if (!freeProperties.append(&properties))
michael@0 624 return false;
michael@0 625 stack.popBack();
michael@0 626 return true;
michael@0 627 }
michael@0 628
michael@0 629 inline bool
michael@0 630 JSONParser::finishArray(MutableHandleValue vp, ElementVector &elements)
michael@0 631 {
michael@0 632 JS_ASSERT(&elements == &stack.back().elements());
michael@0 633
michael@0 634 JSObject *obj = NewDenseCopiedArray(cx, elements.length(), elements.begin());
michael@0 635 if (!obj)
michael@0 636 return false;
michael@0 637
michael@0 638 /* Try to assign a new type to the array according to its elements. */
michael@0 639 cx->compartment()->types.fixArrayType(cx, obj);
michael@0 640
michael@0 641 vp.setObject(*obj);
michael@0 642 if (!freeElements.append(&elements))
michael@0 643 return false;
michael@0 644 stack.popBack();
michael@0 645 return true;
michael@0 646 }
michael@0 647
michael@0 648 bool
michael@0 649 JSONParser::parse(MutableHandleValue vp)
michael@0 650 {
michael@0 651 RootedValue value(cx);
michael@0 652 JS_ASSERT(stack.empty());
michael@0 653
michael@0 654 vp.setUndefined();
michael@0 655
michael@0 656 Token token;
michael@0 657 ParserState state = JSONValue;
michael@0 658 while (true) {
michael@0 659 switch (state) {
michael@0 660 case FinishObjectMember: {
michael@0 661 PropertyVector &properties = stack.back().properties();
michael@0 662 properties.back().value = value;
michael@0 663
michael@0 664 token = advanceAfterProperty();
michael@0 665 if (token == ObjectClose) {
michael@0 666 if (!finishObject(&value, properties))
michael@0 667 return false;
michael@0 668 break;
michael@0 669 }
michael@0 670 if (token != Comma) {
michael@0 671 if (token == OOM)
michael@0 672 return false;
michael@0 673 if (token != Error)
michael@0 674 error("expected ',' or '}' after property-value pair in object literal");
michael@0 675 return errorReturn();
michael@0 676 }
michael@0 677 token = advancePropertyName();
michael@0 678 /* FALL THROUGH */
michael@0 679 }
michael@0 680
michael@0 681 JSONMember:
michael@0 682 if (token == String) {
michael@0 683 jsid id = AtomToId(atomValue());
michael@0 684 PropertyVector &properties = stack.back().properties();
michael@0 685 if (!properties.append(IdValuePair(id)))
michael@0 686 return false;
michael@0 687 token = advancePropertyColon();
michael@0 688 if (token != Colon) {
michael@0 689 JS_ASSERT(token == Error);
michael@0 690 return errorReturn();
michael@0 691 }
michael@0 692 goto JSONValue;
michael@0 693 }
michael@0 694 if (token == OOM)
michael@0 695 return false;
michael@0 696 if (token != Error)
michael@0 697 error("property names must be double-quoted strings");
michael@0 698 return errorReturn();
michael@0 699
michael@0 700 case FinishArrayElement: {
michael@0 701 ElementVector &elements = stack.back().elements();
michael@0 702 if (!elements.append(value.get()))
michael@0 703 return false;
michael@0 704 token = advanceAfterArrayElement();
michael@0 705 if (token == Comma)
michael@0 706 goto JSONValue;
michael@0 707 if (token == ArrayClose) {
michael@0 708 if (!finishArray(&value, elements))
michael@0 709 return false;
michael@0 710 break;
michael@0 711 }
michael@0 712 JS_ASSERT(token == Error);
michael@0 713 return errorReturn();
michael@0 714 }
michael@0 715
michael@0 716 JSONValue:
michael@0 717 case JSONValue:
michael@0 718 token = advance();
michael@0 719 JSONValueSwitch:
michael@0 720 switch (token) {
michael@0 721 case String:
michael@0 722 value = stringValue();
michael@0 723 break;
michael@0 724 case Number:
michael@0 725 value = numberValue();
michael@0 726 break;
michael@0 727 case True:
michael@0 728 value = BooleanValue(true);
michael@0 729 break;
michael@0 730 case False:
michael@0 731 value = BooleanValue(false);
michael@0 732 break;
michael@0 733 case Null:
michael@0 734 value = NullValue();
michael@0 735 break;
michael@0 736
michael@0 737 case ArrayOpen: {
michael@0 738 ElementVector *elements;
michael@0 739 if (!freeElements.empty()) {
michael@0 740 elements = freeElements.popCopy();
michael@0 741 elements->clear();
michael@0 742 } else {
michael@0 743 elements = cx->new_<ElementVector>(cx);
michael@0 744 if (!elements)
michael@0 745 return false;
michael@0 746 }
michael@0 747 if (!stack.append(elements))
michael@0 748 return false;
michael@0 749
michael@0 750 token = advance();
michael@0 751 if (token == ArrayClose) {
michael@0 752 if (!finishArray(&value, *elements))
michael@0 753 return false;
michael@0 754 break;
michael@0 755 }
michael@0 756 goto JSONValueSwitch;
michael@0 757 }
michael@0 758
michael@0 759 case ObjectOpen: {
michael@0 760 PropertyVector *properties;
michael@0 761 if (!freeProperties.empty()) {
michael@0 762 properties = freeProperties.popCopy();
michael@0 763 properties->clear();
michael@0 764 } else {
michael@0 765 properties = cx->new_<PropertyVector>(cx);
michael@0 766 if (!properties)
michael@0 767 return false;
michael@0 768 }
michael@0 769 if (!stack.append(properties))
michael@0 770 return false;
michael@0 771
michael@0 772 token = advanceAfterObjectOpen();
michael@0 773 if (token == ObjectClose) {
michael@0 774 if (!finishObject(&value, *properties))
michael@0 775 return false;
michael@0 776 break;
michael@0 777 }
michael@0 778 goto JSONMember;
michael@0 779 }
michael@0 780
michael@0 781 case ArrayClose:
michael@0 782 case ObjectClose:
michael@0 783 case Colon:
michael@0 784 case Comma:
michael@0 785 // Move the current pointer backwards so that the position
michael@0 786 // reported in the error message is correct.
michael@0 787 --current;
michael@0 788 error("unexpected character");
michael@0 789 return errorReturn();
michael@0 790
michael@0 791 case OOM:
michael@0 792 return false;
michael@0 793
michael@0 794 case Error:
michael@0 795 return errorReturn();
michael@0 796 }
michael@0 797 break;
michael@0 798 }
michael@0 799
michael@0 800 if (stack.empty())
michael@0 801 break;
michael@0 802 state = stack.back().state;
michael@0 803 }
michael@0 804
michael@0 805 for (; current < end; current++) {
michael@0 806 if (!IsJSONWhitespace(*current)) {
michael@0 807 error("unexpected non-whitespace character after JSON data");
michael@0 808 return errorReturn();
michael@0 809 }
michael@0 810 }
michael@0 811
michael@0 812 JS_ASSERT(end == current);
michael@0 813 JS_ASSERT(stack.empty());
michael@0 814
michael@0 815 vp.set(value);
michael@0 816 return true;
michael@0 817 }

mercurial