LCOV - code coverage report
Current view: top level - js/src/vm - JSONParser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 209 424 49.3 %
Date: 2017-07-14 16:53:18 Functions: 26 32 81.2 %
Legend: Lines: hit not hit

          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>;

Generated by: LCOV version 1.13