1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsonparser.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,817 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jsonparser.h" 1.11 + 1.12 +#include "mozilla/RangedPtr.h" 1.13 + 1.14 +#include <ctype.h> 1.15 + 1.16 +#include "jsarray.h" 1.17 +#include "jscompartment.h" 1.18 +#include "jsnum.h" 1.19 +#include "jsprf.h" 1.20 + 1.21 +#include "vm/StringBuffer.h" 1.22 + 1.23 +#include "jsobjinlines.h" 1.24 + 1.25 +using namespace js; 1.26 + 1.27 +using mozilla::RangedPtr; 1.28 + 1.29 +JSONParser::~JSONParser() 1.30 +{ 1.31 + for (size_t i = 0; i < stack.length(); i++) { 1.32 + if (stack[i].state == FinishArrayElement) 1.33 + js_delete(&stack[i].elements()); 1.34 + else 1.35 + js_delete(&stack[i].properties()); 1.36 + } 1.37 + 1.38 + for (size_t i = 0; i < freeElements.length(); i++) 1.39 + js_delete(freeElements[i]); 1.40 + 1.41 + for (size_t i = 0; i < freeProperties.length(); i++) 1.42 + js_delete(freeProperties[i]); 1.43 +} 1.44 + 1.45 +void 1.46 +JSONParser::trace(JSTracer *trc) 1.47 +{ 1.48 + for (size_t i = 0; i < stack.length(); i++) { 1.49 + if (stack[i].state == FinishArrayElement) { 1.50 + ElementVector &elements = stack[i].elements(); 1.51 + for (size_t j = 0; j < elements.length(); j++) 1.52 + gc::MarkValueRoot(trc, &elements[j], "JSONParser element"); 1.53 + } else { 1.54 + PropertyVector &properties = stack[i].properties(); 1.55 + for (size_t j = 0; j < properties.length(); j++) { 1.56 + gc::MarkValueRoot(trc, &properties[j].value, "JSONParser property value"); 1.57 + gc::MarkIdRoot(trc, &properties[j].id, "JSONParser property id"); 1.58 + } 1.59 + } 1.60 + } 1.61 +} 1.62 + 1.63 +void 1.64 +JSONParser::getTextPosition(uint32_t *column, uint32_t *line) 1.65 +{ 1.66 + ConstTwoByteChars ptr = begin; 1.67 + uint32_t col = 1; 1.68 + uint32_t row = 1; 1.69 + for (; ptr < current; ptr++) { 1.70 + if (*ptr == '\n' || *ptr == '\r') { 1.71 + ++row; 1.72 + col = 1; 1.73 + // \r\n is treated as a single newline. 1.74 + if (ptr + 1 < current && *ptr == '\r' && *(ptr + 1) == '\n') 1.75 + ++ptr; 1.76 + } else { 1.77 + ++col; 1.78 + } 1.79 + } 1.80 + *column = col; 1.81 + *line = row; 1.82 +} 1.83 + 1.84 +void 1.85 +JSONParser::error(const char *msg) 1.86 +{ 1.87 + if (errorHandling == RaiseError) { 1.88 + uint32_t column = 1, line = 1; 1.89 + getTextPosition(&column, &line); 1.90 + 1.91 + const size_t MaxWidth = sizeof("4294967295"); 1.92 + char columnNumber[MaxWidth]; 1.93 + JS_snprintf(columnNumber, sizeof columnNumber, "%lu", column); 1.94 + char lineNumber[MaxWidth]; 1.95 + JS_snprintf(lineNumber, sizeof lineNumber, "%lu", line); 1.96 + 1.97 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_JSON_BAD_PARSE, 1.98 + msg, lineNumber, columnNumber); 1.99 + } 1.100 +} 1.101 + 1.102 +bool 1.103 +JSONParser::errorReturn() 1.104 +{ 1.105 + return errorHandling == NoError; 1.106 +} 1.107 + 1.108 +template<JSONParser::StringType ST> 1.109 +JSONParser::Token 1.110 +JSONParser::readString() 1.111 +{ 1.112 + JS_ASSERT(current < end); 1.113 + JS_ASSERT(*current == '"'); 1.114 + 1.115 + /* 1.116 + * JSONString: 1.117 + * /^"([^\u0000-\u001F"\\]|\\(["/\\bfnrt]|u[0-9a-fA-F]{4}))*"$/ 1.118 + */ 1.119 + 1.120 + if (++current == end) { 1.121 + error("unterminated string literal"); 1.122 + return token(Error); 1.123 + } 1.124 + 1.125 + /* 1.126 + * Optimization: if the source contains no escaped characters, create the 1.127 + * string directly from the source text. 1.128 + */ 1.129 + RangedPtr<const jschar> start = current; 1.130 + for (; current < end; current++) { 1.131 + if (*current == '"') { 1.132 + size_t length = current - start; 1.133 + current++; 1.134 + JSFlatString *str = (ST == JSONParser::PropertyName) 1.135 + ? AtomizeChars(cx, start.get(), length) 1.136 + : js_NewStringCopyN<CanGC>(cx, start.get(), length); 1.137 + if (!str) 1.138 + return token(OOM); 1.139 + return stringToken(str); 1.140 + } 1.141 + 1.142 + if (*current == '\\') 1.143 + break; 1.144 + 1.145 + if (*current <= 0x001F) { 1.146 + error("bad control character in string literal"); 1.147 + return token(Error); 1.148 + } 1.149 + } 1.150 + 1.151 + /* 1.152 + * Slow case: string contains escaped characters. Copy a maximal sequence 1.153 + * of unescaped characters into a temporary buffer, then an escaped 1.154 + * character, and repeat until the entire string is consumed. 1.155 + */ 1.156 + StringBuffer buffer(cx); 1.157 + do { 1.158 + if (start < current && !buffer.append(start.get(), current.get())) 1.159 + return token(OOM); 1.160 + 1.161 + if (current >= end) 1.162 + break; 1.163 + 1.164 + jschar c = *current++; 1.165 + if (c == '"') { 1.166 + JSFlatString *str = (ST == JSONParser::PropertyName) 1.167 + ? buffer.finishAtom() 1.168 + : buffer.finishString(); 1.169 + if (!str) 1.170 + return token(OOM); 1.171 + return stringToken(str); 1.172 + } 1.173 + 1.174 + if (c != '\\') { 1.175 + --current; 1.176 + error("bad character in string literal"); 1.177 + return token(Error); 1.178 + } 1.179 + 1.180 + if (current >= end) 1.181 + break; 1.182 + 1.183 + switch (*current++) { 1.184 + case '"': c = '"'; break; 1.185 + case '/': c = '/'; break; 1.186 + case '\\': c = '\\'; break; 1.187 + case 'b': c = '\b'; break; 1.188 + case 'f': c = '\f'; break; 1.189 + case 'n': c = '\n'; break; 1.190 + case 'r': c = '\r'; break; 1.191 + case 't': c = '\t'; break; 1.192 + 1.193 + case 'u': 1.194 + if (end - current < 4 || 1.195 + !(JS7_ISHEX(current[0]) && 1.196 + JS7_ISHEX(current[1]) && 1.197 + JS7_ISHEX(current[2]) && 1.198 + JS7_ISHEX(current[3]))) 1.199 + { 1.200 + // Point to the first non-hexadecimal character (which may be 1.201 + // missing). 1.202 + if (current == end || !JS7_ISHEX(current[0])) 1.203 + ; // already at correct location 1.204 + else if (current + 1 == end || !JS7_ISHEX(current[1])) 1.205 + current += 1; 1.206 + else if (current + 2 == end || !JS7_ISHEX(current[2])) 1.207 + current += 2; 1.208 + else if (current + 3 == end || !JS7_ISHEX(current[3])) 1.209 + current += 3; 1.210 + else 1.211 + MOZ_ASSUME_UNREACHABLE("logic error determining first erroneous character"); 1.212 + 1.213 + error("bad Unicode escape"); 1.214 + return token(Error); 1.215 + } 1.216 + c = (JS7_UNHEX(current[0]) << 12) 1.217 + | (JS7_UNHEX(current[1]) << 8) 1.218 + | (JS7_UNHEX(current[2]) << 4) 1.219 + | (JS7_UNHEX(current[3])); 1.220 + current += 4; 1.221 + break; 1.222 + 1.223 + default: 1.224 + current--; 1.225 + error("bad escaped character"); 1.226 + return token(Error); 1.227 + } 1.228 + if (!buffer.append(c)) 1.229 + return token(OOM); 1.230 + 1.231 + start = current; 1.232 + for (; current < end; current++) { 1.233 + if (*current == '"' || *current == '\\' || *current <= 0x001F) 1.234 + break; 1.235 + } 1.236 + } while (current < end); 1.237 + 1.238 + error("unterminated string"); 1.239 + return token(Error); 1.240 +} 1.241 + 1.242 +JSONParser::Token 1.243 +JSONParser::readNumber() 1.244 +{ 1.245 + JS_ASSERT(current < end); 1.246 + JS_ASSERT(JS7_ISDEC(*current) || *current == '-'); 1.247 + 1.248 + /* 1.249 + * JSONNumber: 1.250 + * /^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][\+\-]?[0-9]+)?$/ 1.251 + */ 1.252 + 1.253 + bool negative = *current == '-'; 1.254 + 1.255 + /* -? */ 1.256 + if (negative && ++current == end) { 1.257 + error("no number after minus sign"); 1.258 + return token(Error); 1.259 + } 1.260 + 1.261 + const RangedPtr<const jschar> digitStart = current; 1.262 + 1.263 + /* 0|[1-9][0-9]+ */ 1.264 + if (!JS7_ISDEC(*current)) { 1.265 + error("unexpected non-digit"); 1.266 + return token(Error); 1.267 + } 1.268 + if (*current++ != '0') { 1.269 + for (; current < end; current++) { 1.270 + if (!JS7_ISDEC(*current)) 1.271 + break; 1.272 + } 1.273 + } 1.274 + 1.275 + /* Fast path: no fractional or exponent part. */ 1.276 + if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) { 1.277 + TwoByteChars chars(digitStart.get(), current - digitStart); 1.278 + if (chars.length() < strlen("9007199254740992")) { 1.279 + // If the decimal number is shorter than the length of 2**53, (the 1.280 + // largest number a double can represent with integral precision), 1.281 + // parse it using a decimal-only parser. This comparison is 1.282 + // conservative but faster than a fully-precise check. 1.283 + double d = ParseDecimalNumber(chars); 1.284 + return numberToken(negative ? -d : d); 1.285 + } 1.286 + 1.287 + double d; 1.288 + const jschar *dummy; 1.289 + if (!GetPrefixInteger(cx, digitStart.get(), current.get(), 10, &dummy, &d)) 1.290 + return token(OOM); 1.291 + JS_ASSERT(current == dummy); 1.292 + return numberToken(negative ? -d : d); 1.293 + } 1.294 + 1.295 + /* (\.[0-9]+)? */ 1.296 + if (current < end && *current == '.') { 1.297 + if (++current == end) { 1.298 + error("missing digits after decimal point"); 1.299 + return token(Error); 1.300 + } 1.301 + if (!JS7_ISDEC(*current)) { 1.302 + error("unterminated fractional number"); 1.303 + return token(Error); 1.304 + } 1.305 + while (++current < end) { 1.306 + if (!JS7_ISDEC(*current)) 1.307 + break; 1.308 + } 1.309 + } 1.310 + 1.311 + /* ([eE][\+\-]?[0-9]+)? */ 1.312 + if (current < end && (*current == 'e' || *current == 'E')) { 1.313 + if (++current == end) { 1.314 + error("missing digits after exponent indicator"); 1.315 + return token(Error); 1.316 + } 1.317 + if (*current == '+' || *current == '-') { 1.318 + if (++current == end) { 1.319 + error("missing digits after exponent sign"); 1.320 + return token(Error); 1.321 + } 1.322 + } 1.323 + if (!JS7_ISDEC(*current)) { 1.324 + error("exponent part is missing a number"); 1.325 + return token(Error); 1.326 + } 1.327 + while (++current < end) { 1.328 + if (!JS7_ISDEC(*current)) 1.329 + break; 1.330 + } 1.331 + } 1.332 + 1.333 + double d; 1.334 + const jschar *finish; 1.335 + if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d)) 1.336 + return token(OOM); 1.337 + JS_ASSERT(current == finish); 1.338 + return numberToken(negative ? -d : d); 1.339 +} 1.340 + 1.341 +static inline bool 1.342 +IsJSONWhitespace(jschar c) 1.343 +{ 1.344 + return c == '\t' || c == '\r' || c == '\n' || c == ' '; 1.345 +} 1.346 + 1.347 +JSONParser::Token 1.348 +JSONParser::advance() 1.349 +{ 1.350 + while (current < end && IsJSONWhitespace(*current)) 1.351 + current++; 1.352 + if (current >= end) { 1.353 + error("unexpected end of data"); 1.354 + return token(Error); 1.355 + } 1.356 + 1.357 + switch (*current) { 1.358 + case '"': 1.359 + return readString<LiteralValue>(); 1.360 + 1.361 + case '-': 1.362 + case '0': 1.363 + case '1': 1.364 + case '2': 1.365 + case '3': 1.366 + case '4': 1.367 + case '5': 1.368 + case '6': 1.369 + case '7': 1.370 + case '8': 1.371 + case '9': 1.372 + return readNumber(); 1.373 + 1.374 + case 't': 1.375 + if (end - current < 4 || current[1] != 'r' || current[2] != 'u' || current[3] != 'e') { 1.376 + error("unexpected keyword"); 1.377 + return token(Error); 1.378 + } 1.379 + current += 4; 1.380 + return token(True); 1.381 + 1.382 + case 'f': 1.383 + if (end - current < 5 || 1.384 + current[1] != 'a' || current[2] != 'l' || current[3] != 's' || current[4] != 'e') 1.385 + { 1.386 + error("unexpected keyword"); 1.387 + return token(Error); 1.388 + } 1.389 + current += 5; 1.390 + return token(False); 1.391 + 1.392 + case 'n': 1.393 + if (end - current < 4 || current[1] != 'u' || current[2] != 'l' || current[3] != 'l') { 1.394 + error("unexpected keyword"); 1.395 + return token(Error); 1.396 + } 1.397 + current += 4; 1.398 + return token(Null); 1.399 + 1.400 + case '[': 1.401 + current++; 1.402 + return token(ArrayOpen); 1.403 + case ']': 1.404 + current++; 1.405 + return token(ArrayClose); 1.406 + 1.407 + case '{': 1.408 + current++; 1.409 + return token(ObjectOpen); 1.410 + case '}': 1.411 + current++; 1.412 + return token(ObjectClose); 1.413 + 1.414 + case ',': 1.415 + current++; 1.416 + return token(Comma); 1.417 + 1.418 + case ':': 1.419 + current++; 1.420 + return token(Colon); 1.421 + 1.422 + default: 1.423 + error("unexpected character"); 1.424 + return token(Error); 1.425 + } 1.426 +} 1.427 + 1.428 +JSONParser::Token 1.429 +JSONParser::advanceAfterObjectOpen() 1.430 +{ 1.431 + JS_ASSERT(current[-1] == '{'); 1.432 + 1.433 + while (current < end && IsJSONWhitespace(*current)) 1.434 + current++; 1.435 + if (current >= end) { 1.436 + error("end of data while reading object contents"); 1.437 + return token(Error); 1.438 + } 1.439 + 1.440 + if (*current == '"') 1.441 + return readString<PropertyName>(); 1.442 + 1.443 + if (*current == '}') { 1.444 + current++; 1.445 + return token(ObjectClose); 1.446 + } 1.447 + 1.448 + error("expected property name or '}'"); 1.449 + return token(Error); 1.450 +} 1.451 + 1.452 +static inline void 1.453 +AssertPastValue(const RangedPtr<const jschar> current) 1.454 +{ 1.455 + /* 1.456 + * We're past an arbitrary JSON value, so the previous character is 1.457 + * *somewhat* constrained, even if this assertion is pretty broad. Don't 1.458 + * knock it till you tried it: this assertion *did* catch a bug once. 1.459 + */ 1.460 + JS_ASSERT((current[-1] == 'l' && 1.461 + current[-2] == 'l' && 1.462 + current[-3] == 'u' && 1.463 + current[-4] == 'n') || 1.464 + (current[-1] == 'e' && 1.465 + current[-2] == 'u' && 1.466 + current[-3] == 'r' && 1.467 + current[-4] == 't') || 1.468 + (current[-1] == 'e' && 1.469 + current[-2] == 's' && 1.470 + current[-3] == 'l' && 1.471 + current[-4] == 'a' && 1.472 + current[-5] == 'f') || 1.473 + current[-1] == '}' || 1.474 + current[-1] == ']' || 1.475 + current[-1] == '"' || 1.476 + JS7_ISDEC(current[-1])); 1.477 +} 1.478 + 1.479 +JSONParser::Token 1.480 +JSONParser::advanceAfterArrayElement() 1.481 +{ 1.482 + AssertPastValue(current); 1.483 + 1.484 + while (current < end && IsJSONWhitespace(*current)) 1.485 + current++; 1.486 + if (current >= end) { 1.487 + error("end of data when ',' or ']' was expected"); 1.488 + return token(Error); 1.489 + } 1.490 + 1.491 + if (*current == ',') { 1.492 + current++; 1.493 + return token(Comma); 1.494 + } 1.495 + 1.496 + if (*current == ']') { 1.497 + current++; 1.498 + return token(ArrayClose); 1.499 + } 1.500 + 1.501 + error("expected ',' or ']' after array element"); 1.502 + return token(Error); 1.503 +} 1.504 + 1.505 +JSONParser::Token 1.506 +JSONParser::advancePropertyName() 1.507 +{ 1.508 + JS_ASSERT(current[-1] == ','); 1.509 + 1.510 + while (current < end && IsJSONWhitespace(*current)) 1.511 + current++; 1.512 + if (current >= end) { 1.513 + error("end of data when property name was expected"); 1.514 + return token(Error); 1.515 + } 1.516 + 1.517 + if (*current == '"') 1.518 + return readString<PropertyName>(); 1.519 + 1.520 + error("expected double-quoted property name"); 1.521 + return token(Error); 1.522 +} 1.523 + 1.524 +JSONParser::Token 1.525 +JSONParser::advancePropertyColon() 1.526 +{ 1.527 + JS_ASSERT(current[-1] == '"'); 1.528 + 1.529 + while (current < end && IsJSONWhitespace(*current)) 1.530 + current++; 1.531 + if (current >= end) { 1.532 + error("end of data after property name when ':' was expected"); 1.533 + return token(Error); 1.534 + } 1.535 + 1.536 + if (*current == ':') { 1.537 + current++; 1.538 + return token(Colon); 1.539 + } 1.540 + 1.541 + error("expected ':' after property name in object"); 1.542 + return token(Error); 1.543 +} 1.544 + 1.545 +JSONParser::Token 1.546 +JSONParser::advanceAfterProperty() 1.547 +{ 1.548 + AssertPastValue(current); 1.549 + 1.550 + while (current < end && IsJSONWhitespace(*current)) 1.551 + current++; 1.552 + if (current >= end) { 1.553 + error("end of data after property value in object"); 1.554 + return token(Error); 1.555 + } 1.556 + 1.557 + if (*current == ',') { 1.558 + current++; 1.559 + return token(Comma); 1.560 + } 1.561 + 1.562 + if (*current == '}') { 1.563 + current++; 1.564 + return token(ObjectClose); 1.565 + } 1.566 + 1.567 + error("expected ',' or '}' after property value in object"); 1.568 + return token(Error); 1.569 +} 1.570 + 1.571 +JSObject * 1.572 +JSONParser::createFinishedObject(PropertyVector &properties) 1.573 +{ 1.574 + /* 1.575 + * Look for an existing cached type and shape for objects with this set of 1.576 + * properties. 1.577 + */ 1.578 + { 1.579 + JSObject *obj = cx->compartment()->types.newTypedObject(cx, properties.begin(), 1.580 + properties.length()); 1.581 + if (obj) 1.582 + return obj; 1.583 + } 1.584 + 1.585 + /* 1.586 + * Make a new object sized for the given number of properties and fill its 1.587 + * shape in manually. 1.588 + */ 1.589 + gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length()); 1.590 + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind)); 1.591 + if (!obj) 1.592 + return nullptr; 1.593 + 1.594 + RootedId propid(cx); 1.595 + RootedValue value(cx); 1.596 + 1.597 + for (size_t i = 0; i < properties.length(); i++) { 1.598 + propid = properties[i].id; 1.599 + value = properties[i].value; 1.600 + if (!DefineNativeProperty(cx, obj, propid, value, JS_PropertyStub, JS_StrictPropertyStub, 1.601 + JSPROP_ENUMERATE)) { 1.602 + return nullptr; 1.603 + } 1.604 + } 1.605 + 1.606 + /* 1.607 + * Try to assign a new type to the object with type information for its 1.608 + * properties, and update the initializer type object cache with this 1.609 + * object's final shape. 1.610 + */ 1.611 + cx->compartment()->types.fixObjectType(cx, obj); 1.612 + 1.613 + return obj; 1.614 +} 1.615 + 1.616 +inline bool 1.617 +JSONParser::finishObject(MutableHandleValue vp, PropertyVector &properties) 1.618 +{ 1.619 + JS_ASSERT(&properties == &stack.back().properties()); 1.620 + 1.621 + JSObject *obj = createFinishedObject(properties); 1.622 + if (!obj) 1.623 + return false; 1.624 + 1.625 + vp.setObject(*obj); 1.626 + if (!freeProperties.append(&properties)) 1.627 + return false; 1.628 + stack.popBack(); 1.629 + return true; 1.630 +} 1.631 + 1.632 +inline bool 1.633 +JSONParser::finishArray(MutableHandleValue vp, ElementVector &elements) 1.634 +{ 1.635 + JS_ASSERT(&elements == &stack.back().elements()); 1.636 + 1.637 + JSObject *obj = NewDenseCopiedArray(cx, elements.length(), elements.begin()); 1.638 + if (!obj) 1.639 + return false; 1.640 + 1.641 + /* Try to assign a new type to the array according to its elements. */ 1.642 + cx->compartment()->types.fixArrayType(cx, obj); 1.643 + 1.644 + vp.setObject(*obj); 1.645 + if (!freeElements.append(&elements)) 1.646 + return false; 1.647 + stack.popBack(); 1.648 + return true; 1.649 +} 1.650 + 1.651 +bool 1.652 +JSONParser::parse(MutableHandleValue vp) 1.653 +{ 1.654 + RootedValue value(cx); 1.655 + JS_ASSERT(stack.empty()); 1.656 + 1.657 + vp.setUndefined(); 1.658 + 1.659 + Token token; 1.660 + ParserState state = JSONValue; 1.661 + while (true) { 1.662 + switch (state) { 1.663 + case FinishObjectMember: { 1.664 + PropertyVector &properties = stack.back().properties(); 1.665 + properties.back().value = value; 1.666 + 1.667 + token = advanceAfterProperty(); 1.668 + if (token == ObjectClose) { 1.669 + if (!finishObject(&value, properties)) 1.670 + return false; 1.671 + break; 1.672 + } 1.673 + if (token != Comma) { 1.674 + if (token == OOM) 1.675 + return false; 1.676 + if (token != Error) 1.677 + error("expected ',' or '}' after property-value pair in object literal"); 1.678 + return errorReturn(); 1.679 + } 1.680 + token = advancePropertyName(); 1.681 + /* FALL THROUGH */ 1.682 + } 1.683 + 1.684 + JSONMember: 1.685 + if (token == String) { 1.686 + jsid id = AtomToId(atomValue()); 1.687 + PropertyVector &properties = stack.back().properties(); 1.688 + if (!properties.append(IdValuePair(id))) 1.689 + return false; 1.690 + token = advancePropertyColon(); 1.691 + if (token != Colon) { 1.692 + JS_ASSERT(token == Error); 1.693 + return errorReturn(); 1.694 + } 1.695 + goto JSONValue; 1.696 + } 1.697 + if (token == OOM) 1.698 + return false; 1.699 + if (token != Error) 1.700 + error("property names must be double-quoted strings"); 1.701 + return errorReturn(); 1.702 + 1.703 + case FinishArrayElement: { 1.704 + ElementVector &elements = stack.back().elements(); 1.705 + if (!elements.append(value.get())) 1.706 + return false; 1.707 + token = advanceAfterArrayElement(); 1.708 + if (token == Comma) 1.709 + goto JSONValue; 1.710 + if (token == ArrayClose) { 1.711 + if (!finishArray(&value, elements)) 1.712 + return false; 1.713 + break; 1.714 + } 1.715 + JS_ASSERT(token == Error); 1.716 + return errorReturn(); 1.717 + } 1.718 + 1.719 + JSONValue: 1.720 + case JSONValue: 1.721 + token = advance(); 1.722 + JSONValueSwitch: 1.723 + switch (token) { 1.724 + case String: 1.725 + value = stringValue(); 1.726 + break; 1.727 + case Number: 1.728 + value = numberValue(); 1.729 + break; 1.730 + case True: 1.731 + value = BooleanValue(true); 1.732 + break; 1.733 + case False: 1.734 + value = BooleanValue(false); 1.735 + break; 1.736 + case Null: 1.737 + value = NullValue(); 1.738 + break; 1.739 + 1.740 + case ArrayOpen: { 1.741 + ElementVector *elements; 1.742 + if (!freeElements.empty()) { 1.743 + elements = freeElements.popCopy(); 1.744 + elements->clear(); 1.745 + } else { 1.746 + elements = cx->new_<ElementVector>(cx); 1.747 + if (!elements) 1.748 + return false; 1.749 + } 1.750 + if (!stack.append(elements)) 1.751 + return false; 1.752 + 1.753 + token = advance(); 1.754 + if (token == ArrayClose) { 1.755 + if (!finishArray(&value, *elements)) 1.756 + return false; 1.757 + break; 1.758 + } 1.759 + goto JSONValueSwitch; 1.760 + } 1.761 + 1.762 + case ObjectOpen: { 1.763 + PropertyVector *properties; 1.764 + if (!freeProperties.empty()) { 1.765 + properties = freeProperties.popCopy(); 1.766 + properties->clear(); 1.767 + } else { 1.768 + properties = cx->new_<PropertyVector>(cx); 1.769 + if (!properties) 1.770 + return false; 1.771 + } 1.772 + if (!stack.append(properties)) 1.773 + return false; 1.774 + 1.775 + token = advanceAfterObjectOpen(); 1.776 + if (token == ObjectClose) { 1.777 + if (!finishObject(&value, *properties)) 1.778 + return false; 1.779 + break; 1.780 + } 1.781 + goto JSONMember; 1.782 + } 1.783 + 1.784 + case ArrayClose: 1.785 + case ObjectClose: 1.786 + case Colon: 1.787 + case Comma: 1.788 + // Move the current pointer backwards so that the position 1.789 + // reported in the error message is correct. 1.790 + --current; 1.791 + error("unexpected character"); 1.792 + return errorReturn(); 1.793 + 1.794 + case OOM: 1.795 + return false; 1.796 + 1.797 + case Error: 1.798 + return errorReturn(); 1.799 + } 1.800 + break; 1.801 + } 1.802 + 1.803 + if (stack.empty()) 1.804 + break; 1.805 + state = stack.back().state; 1.806 + } 1.807 + 1.808 + for (; current < end; current++) { 1.809 + if (!IsJSONWhitespace(*current)) { 1.810 + error("unexpected non-whitespace character after JSON data"); 1.811 + return errorReturn(); 1.812 + } 1.813 + } 1.814 + 1.815 + JS_ASSERT(end == current); 1.816 + JS_ASSERT(stack.empty()); 1.817 + 1.818 + vp.set(value); 1.819 + return true; 1.820 +}