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