LCOV - code coverage report
Current view: directory - js/src - jsonparser.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 335 253 75.5 %
Date: 2012-06-02 Functions: 14 14 100.0 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=99:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is SpiderMonkey JSON.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * the Mozilla Foundation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Jeff Walden <jwalden+code@mit.edu> (original author)
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "jsarray.h"
      42                 : #include "jsnum.h"
      43                 : #include "jsonparser.h"
      44                 : 
      45                 : #include "jsobjinlines.h"
      46                 : 
      47                 : #include "vm/StringBuffer-inl.h"
      48                 : 
      49                 : using namespace js;
      50                 : 
      51                 : void
      52            3173 : JSONParser::error(const char *msg)
      53                 : {
      54            3173 :     if (errorHandling == RaiseError)
      55             210 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, msg);
      56            3173 : }
      57                 : 
      58                 : bool
      59            3173 : JSONParser::errorReturn()
      60                 : {
      61            3173 :     return errorHandling == NoError;
      62                 : }
      63                 : 
      64                 : template<JSONParser::StringType ST>
      65                 : JSONParser::Token
      66          350881 : JSONParser::readString()
      67                 : {
      68          350881 :     JS_ASSERT(current < end);
      69          350881 :     JS_ASSERT(*current == '"');
      70                 : 
      71                 :     /*
      72                 :      * JSONString:
      73                 :      *   /^"([^\u0000-\u001F"\\]|\\(["/\\bfnrt]|u[0-9a-fA-F]{4}))*"$/
      74                 :      */
      75                 : 
      76          350881 :     if (++current == end) {
      77               1 :         error("unterminated string literal");
      78               1 :         return token(Error);
      79                 :     }
      80                 : 
      81                 :     /*
      82                 :      * Optimization: if the source contains no escaped characters, create the
      83                 :      * string directly from the source text.
      84                 :      */
      85          350880 :     RangedPtr<const jschar> start = current;
      86         9630847 :     for (; current < end; current++) {
      87         9630847 :         if (*current == '"') {
      88          346968 :             size_t length = current - start;
      89          346968 :             current++;
      90                 :             JSFlatString *str = (ST == JSONParser::PropertyName)
      91                 :                                 ? js_AtomizeChars(cx, start.get(), length)
      92          346968 :                                 : js_NewStringCopyN(cx, start.get(), length);
      93          346968 :             if (!str)
      94               0 :                 return token(OOM);
      95          346968 :             return stringToken(str);
      96                 :         }
      97                 : 
      98         9283879 :         if (*current == '\\')
      99            3912 :             break;
     100                 : 
     101         9279967 :         if (*current <= 0x001F) {
     102               0 :             error("bad control character in string literal");
     103               0 :             return token(Error);
     104                 :         }
     105                 :     }
     106                 : 
     107                 :     /*
     108                 :      * Slow case: string contains escaped characters.  Copy a maximal sequence
     109                 :      * of unescaped characters into a temporary buffer, then an escaped
     110                 :      * character, and repeat until the entire string is consumed.
     111                 :      */
     112            7824 :     StringBuffer buffer(cx);
     113          104080 :     do {
     114          107992 :         if (start < current && !buffer.append(start.get(), current.get()))
     115               0 :             return token(OOM);
     116                 : 
     117          107992 :         if (current >= end)
     118               0 :             break;
     119                 : 
     120          107992 :         jschar c = *current++;
     121          107992 :         if (c == '"') {
     122                 :             JSFlatString *str = (ST == JSONParser::PropertyName)
     123                 :                                 ? buffer.finishAtom()
     124            3912 :                                 : buffer.finishString();
     125            3912 :             if (!str)
     126               0 :                 return token(OOM);
     127            3912 :             return stringToken(str);
     128                 :         }
     129                 : 
     130          104080 :         if (c != '\\') {
     131               0 :             error("bad character in string literal");
     132               0 :             return token(Error);
     133                 :         }
     134                 : 
     135          104080 :         if (current >= end)
     136               0 :             break;
     137                 : 
     138          104080 :         switch (*current++) {
     139           74776 :           case '"':  c = '"';  break;
     140               0 :           case '/':  c = '/';  break;
     141           29299 :           case '\\': c = '\\'; break;
     142               1 :           case 'b':  c = '\b'; break;
     143               0 :           case 'f':  c = '\f'; break;
     144               1 :           case 'n':  c = '\n'; break;
     145               0 :           case 'r':  c = '\r'; break;
     146               0 :           case 't':  c = '\t'; break;
     147                 : 
     148                 :           case 'u':
     149               3 :             if (end - current < 4) {
     150               0 :                 error("bad Unicode escape");
     151               0 :                 return token(Error);
     152                 :             }
     153               3 :             if (JS7_ISHEX(current[0]) &&
     154                 :                 JS7_ISHEX(current[1]) &&
     155                 :                 JS7_ISHEX(current[2]) &&
     156                 :                 JS7_ISHEX(current[3]))
     157                 :             {
     158               3 :                 c = (JS7_UNHEX(current[0]) << 12)
     159                 :                   | (JS7_UNHEX(current[1]) << 8)
     160                 :                   | (JS7_UNHEX(current[2]) << 4)
     161                 :                   | (JS7_UNHEX(current[3]));
     162               3 :                 current += 4;
     163               3 :                 break;
     164                 :             }
     165                 :             /* FALL THROUGH */
     166                 : 
     167                 :           default:
     168               0 :             error("bad escaped character");
     169               0 :             return token(Error);
     170                 :         }
     171          104080 :         if (!buffer.append(c))
     172               0 :             return token(OOM);
     173                 : 
     174          104080 :         start = current;
     175          628707 :         for (; current < end; current++) {
     176          628707 :             if (*current == '"' || *current == '\\' || *current <= 0x001F)
     177          208160 :                 break;
     178                 :         }
     179                 :     } while (current < end);
     180                 : 
     181               0 :     error("unterminated string");
     182               0 :     return token(Error);
     183                 : }
     184                 : 
     185                 : JSONParser::Token
     186           63779 : JSONParser::readNumber()
     187                 : {
     188           63779 :     JS_ASSERT(current < end);
     189           63779 :     JS_ASSERT(JS7_ISDEC(*current) || *current == '-');
     190                 : 
     191                 :     /*
     192                 :      * JSONNumber:
     193                 :      *   /^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][\+\-]?[0-9]+)?$/
     194                 :      */
     195                 : 
     196           63779 :     bool negative = *current == '-';
     197                 : 
     198                 :     /* -? */
     199           63779 :     if (negative && ++current == end) {
     200               0 :         error("no number after minus sign");
     201               0 :         return token(Error);
     202                 :     }
     203                 : 
     204           63779 :     const RangedPtr<const jschar> digitStart = current;
     205                 : 
     206                 :     /* 0|[1-9][0-9]+ */
     207           63779 :     if (!JS7_ISDEC(*current)) {
     208             162 :         error("unexpected non-digit");
     209             162 :         return token(Error);
     210                 :     }
     211           63617 :     if (*current++ != '0') {
     212          547735 :         for (; current < end; current++) {
     213          547729 :             if (!JS7_ISDEC(*current))
     214           61732 :                 break;
     215                 :         }
     216                 :     }
     217                 : 
     218                 :     /* Fast path: no fractional or exponent part. */
     219           63617 :     if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
     220                 :         const jschar *dummy;
     221                 :         double d;
     222           61307 :         if (!GetPrefixInteger(cx, digitStart.get(), current.get(), 10, &dummy, &d))
     223               0 :             return token(OOM);
     224           61307 :         JS_ASSERT(current == dummy);
     225           61307 :         return numberToken(negative ? -d : d);
     226                 :     }
     227                 : 
     228                 :     /* (\.[0-9]+)? */
     229            2310 :     if (current < end && *current == '.') {
     230            2308 :         if (++current == end) {
     231               0 :             error("missing digits after decimal point");
     232               0 :             return token(Error);
     233                 :         }
     234            2308 :         if (!JS7_ISDEC(*current)) {
     235               0 :             error("unterminated fractional number");
     236               0 :             return token(Error);
     237                 :         }
     238           10079 :         while (++current < end) {
     239            7744 :             if (!JS7_ISDEC(*current))
     240            2281 :                 break;
     241                 :         }
     242                 :     }
     243                 : 
     244                 :     /* ([eE][\+\-]?[0-9]+)? */
     245            2310 :     if (current < end && (*current == 'e' || *current == 'E')) {
     246               2 :         if (++current == end) {
     247               0 :             error("missing digits after exponent indicator");
     248               0 :             return token(Error);
     249                 :         }
     250               2 :         if (*current == '+' || *current == '-') {
     251               0 :             if (++current == end) {
     252               0 :                 error("missing digits after exponent sign");
     253               0 :                 return token(Error);
     254                 :             }
     255                 :         }
     256               2 :         if (!JS7_ISDEC(*current)) {
     257               0 :             error("exponent part is missing a number");
     258               0 :             return token(Error);
     259                 :         }
     260               8 :         while (++current < end) {
     261               4 :             if (!JS7_ISDEC(*current))
     262               0 :                 break;
     263                 :         }
     264                 :     }
     265                 : 
     266                 :     double d;
     267                 :     const jschar *finish;
     268            2310 :     if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d))
     269               0 :         return token(OOM);
     270            2310 :     JS_ASSERT(current == finish);
     271            2310 :     return numberToken(negative ? -d : d);
     272                 : }
     273                 : 
     274                 : static inline bool
     275         1393490 : IsJSONWhitespace(jschar c)
     276                 : {
     277         1393490 :     return c == '\t' || c == '\r' || c == '\n' || c == ' ';
     278                 : }
     279                 : 
     280                 : JSONParser::Token
     281          273894 : JSONParser::advance()
     282                 : {
     283          666032 :     while (current < end && IsJSONWhitespace(*current))
     284          118244 :         current++;
     285          273894 :     if (current >= end) {
     286               1 :         error("unexpected end of data");
     287               1 :         return token(Error);
     288                 :     }
     289                 : 
     290          273893 :     switch (*current) {
     291                 :       case '"':
     292          139436 :         return readString<LiteralValue>();
     293                 : 
     294                 :       case '-':
     295                 :       case '0':
     296                 :       case '1':
     297                 :       case '2':
     298                 :       case '3':
     299                 :       case '4':
     300                 :       case '5':
     301                 :       case '6':
     302                 :       case '7':
     303                 :       case '8':
     304                 :       case '9':
     305           63779 :         return readNumber();
     306                 : 
     307                 :       case 't':
     308             247 :         if (end - current < 4 || current[1] != 'r' || current[2] != 'u' || current[3] != 'e') {
     309               0 :             error("unexpected keyword");
     310               0 :             return token(Error);
     311                 :         }
     312             247 :         current += 4;
     313             247 :         return token(True);
     314                 : 
     315                 :       case 'f':
     316           18235 :         if (end - current < 5 ||
     317           14021 :             current[1] != 'a' || current[2] != 'l' || current[3] != 's' || current[4] != 'e')
     318                 :         {
     319             945 :             error("unexpected keyword");
     320             945 :             return token(Error);
     321                 :         }
     322            3269 :         current += 5;
     323            3269 :         return token(False);
     324                 : 
     325                 :       case 'n':
     326            3952 :         if (end - current < 4 || current[1] != 'u' || current[2] != 'l' || current[3] != 'l') {
     327              18 :             error("unexpected keyword");
     328              18 :             return token(Error);
     329                 :         }
     330            3934 :         current += 4;
     331            3934 :         return token(Null);
     332                 : 
     333                 :       case '[':
     334            9161 :         current++;
     335            9161 :         return token(ArrayOpen);
     336                 :       case ']':
     337            1896 :         current++;
     338            1896 :         return token(ArrayClose);
     339                 : 
     340                 :       case '{':
     341           49365 :         current++;
     342           49365 :         return token(ObjectOpen);
     343                 :       case '}':
     344               1 :         current++;
     345               1 :         return token(ObjectClose);
     346                 : 
     347                 :       case ',':
     348              28 :         current++;
     349              28 :         return token(Comma);
     350                 : 
     351                 :       case ':':
     352               0 :         current++;
     353               0 :         return token(Colon);
     354                 : 
     355                 :       default:
     356            1814 :         error("unexpected character");
     357            1814 :         return token(Error);
     358                 :     }
     359                 : }
     360                 : 
     361                 : JSONParser::Token
     362           49365 : JSONParser::advanceAfterObjectOpen()
     363                 : {
     364           49365 :     JS_ASSERT(current[-1] == '{');
     365                 : 
     366          219003 :     while (current < end && IsJSONWhitespace(*current))
     367          120273 :         current++;
     368           49365 :     if (current >= end) {
     369               0 :         error("end of data while reading object contents");
     370               0 :         return token(Error);
     371                 :     }
     372                 : 
     373           49365 :     if (*current == '"')
     374           48042 :         return readString<PropertyName>();
     375                 : 
     376            1323 :     if (*current == '}') {
     377            1303 :         current++;
     378            1303 :         return token(ObjectClose);
     379                 :     }
     380                 : 
     381              20 :     error("expected property name or '}'");
     382              20 :     return token(Error);
     383                 : }
     384                 : 
     385                 : static inline void
     386          256724 : AssertPastValue(const RangedPtr<const jschar> current)
     387                 : {
     388                 :     /*
     389                 :      * We're past an arbitrary JSON value, so the previous character is
     390                 :      * *somewhat* constrained, even if this assertion is pretty broad.  Don't
     391                 :      * knock it till you tried it: this assertion *did* catch a bug once.
     392                 :      */
     393         1513165 :     JS_ASSERT((current[-1] == 'l' &&
     394                 :                current[-2] == 'l' &&
     395                 :                current[-3] == 'u' &&
     396                 :                current[-4] == 'n') ||
     397                 :               (current[-1] == 'e' &&
     398                 :                current[-2] == 'u' &&
     399                 :                current[-3] == 'r' &&
     400                 :                current[-4] == 't') ||
     401                 :               (current[-1] == 'e' &&
     402                 :                current[-2] == 's' &&
     403                 :                current[-3] == 'l' &&
     404                 :                current[-4] == 'a' &&
     405                 :                current[-5] == 'f') ||
     406                 :               current[-1] == '}' ||
     407                 :               current[-1] == ']' ||
     408                 :               current[-1] == '"' ||
     409         1513165 :               JS7_ISDEC(current[-1]));
     410          256724 : }
     411                 : 
     412                 : JSONParser::Token
     413           45279 : JSONParser::advanceAfterArrayElement()
     414                 : {
     415           45279 :     AssertPastValue(current);
     416                 : 
     417           90954 :     while (current < end && IsJSONWhitespace(*current))
     418             396 :         current++;
     419           45279 :     if (current >= end) {
     420               0 :         error("end of data when ',' or ']' was expected");
     421               0 :         return token(Error);
     422                 :     }
     423                 : 
     424           45279 :     if (*current == ',') {
     425           39782 :         current++;
     426           39782 :         return token(Comma);
     427                 :     }
     428                 : 
     429            5497 :     if (*current == ']') {
     430            5362 :         current++;
     431            5362 :         return token(ArrayClose);
     432                 :     }
     433                 : 
     434             135 :     error("expected ',' or ']' after array element");
     435             135 :     return token(Error);
     436                 : }
     437                 : 
     438                 : JSONParser::Token
     439          163404 : JSONParser::advancePropertyName()
     440                 : {
     441          163404 :     JS_ASSERT(current[-1] == ',');
     442                 : 
     443          454303 :     while (current < end && IsJSONWhitespace(*current))
     444          127495 :         current++;
     445          163404 :     if (current >= end) {
     446               0 :         error("end of data when property name was expected");
     447               0 :         return token(Error);
     448                 :     }
     449                 : 
     450          163404 :     if (*current == '"')
     451          163403 :         return readString<PropertyName>();
     452                 : 
     453               1 :     if (parsingMode == LegacyJSON && *current == '}') {
     454                 :         /*
     455                 :          * Previous JSON parsing accepted trailing commas in non-empty object
     456                 :          * syntax, and some users depend on this.  (Specifically, Places data
     457                 :          * serialization in versions of Firefox before 4.0.  We can remove this
     458                 :          * mode when profile upgrades from 3.6 become unsupported.)  Permit
     459                 :          * such trailing commas only when legacy parsing is specifically
     460                 :          * requested.
     461                 :          */
     462               0 :         current++;
     463               0 :         return token(ObjectClose);
     464                 :     }
     465                 : 
     466               1 :     error("expected double-quoted property name");
     467               1 :     return token(Error);
     468                 : }
     469                 : 
     470                 : JSONParser::Token
     471          211445 : JSONParser::advancePropertyColon()
     472                 : {
     473          211445 :     JS_ASSERT(current[-1] == '"');
     474                 : 
     475          422890 :     while (current < end && IsJSONWhitespace(*current))
     476               0 :         current++;
     477          211445 :     if (current >= end) {
     478               0 :         error("end of data after property name when ':' was expected");
     479               0 :         return token(Error);
     480                 :     }
     481                 : 
     482          211445 :     if (*current == ':') {
     483          211445 :         current++;
     484          211445 :         return token(Colon);
     485                 :     }
     486                 : 
     487               0 :     error("expected ':' after property name in object");
     488               0 :     return token(Error);
     489                 : }
     490                 : 
     491                 : JSONParser::Token
     492          211445 : JSONParser::advanceAfterProperty()
     493                 : {
     494          211445 :     AssertPastValue(current);
     495                 : 
     496          495103 :     while (current < end && IsJSONWhitespace(*current))
     497           72213 :         current++;
     498          211445 :     if (current >= end) {
     499               0 :         error("end of data after property value in object");
     500               0 :         return token(Error);
     501                 :     }
     502                 : 
     503          211445 :     if (*current == ',') {
     504          163404 :         current++;
     505          163404 :         return token(Comma);
     506                 :     }
     507                 : 
     508           48041 :     if (*current == '}') {
     509           48041 :         current++;
     510           48041 :         return token(ObjectClose);
     511                 :     }
     512                 : 
     513               0 :     error("expected ',' or '}' after property value in object");
     514               0 :     return token(Error);
     515                 : }
     516                 : 
     517                 : /*
     518                 :  * This enum is local to JSONParser::parse, below, but ISO C++98 doesn't allow
     519                 :  * templates to depend on local types.  Boo-urns!
     520                 :  */
     521                 : enum ParserState { FinishArrayElement, FinishObjectMember, JSONValue };
     522                 : 
     523                 : bool
     524           13506 : JSONParser::parse(Value *vp)
     525                 : {
     526           27012 :     Vector<ParserState> stateStack(cx);
     527           27012 :     AutoValueVector valueStack(cx);
     528                 : 
     529           13506 :     *vp = UndefinedValue();
     530                 : 
     531                 :     Token token;
     532           13506 :     ParserState state = JSONValue;
     533          256724 :     while (true) {
     534          270230 :         switch (state) {
     535                 :           case FinishObjectMember: {
     536          211445 :             Value v = valueStack.popCopy();
     537                 :             /*
     538                 :              * NB: Relies on js_DefineNativeProperty performing
     539                 :              *     js_CheckForStringIndex.
     540                 :              */
     541          211445 :             jsid propid = ATOM_TO_JSID(&valueStack.popCopy().toString()->asAtom());
     542          422890 :             if (!DefineNativeProperty(cx, &valueStack.back().toObject(), propid, v,
     543                 :                                       JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
     544          211445 :                                       0, 0))
     545                 :             {
     546               0 :                 return false;
     547                 :             }
     548          211445 :             token = advanceAfterProperty();
     549          211445 :             if (token == ObjectClose)
     550           48041 :                 break;
     551          163404 :             if (token != Comma) {
     552               0 :                 if (token == OOM)
     553               0 :                     return false;
     554               0 :                 if (token != Error)
     555               0 :                     error("expected ',' or '}' after property-value pair in object literal");
     556               0 :                 return errorReturn();
     557                 :             }
     558          163404 :             token = advancePropertyName();
     559                 :             /* FALL THROUGH */
     560                 :           }
     561                 : 
     562                 :           JSONMember:
     563          211466 :             if (token == String) {
     564          211445 :                 if (!valueStack.append(atomValue()))
     565               0 :                     return false;
     566          211445 :                 token = advancePropertyColon();
     567          211445 :                 if (token != Colon) {
     568               0 :                     JS_ASSERT(token == Error);
     569               0 :                     return errorReturn();
     570                 :                 }
     571          211445 :                 if (!stateStack.append(FinishObjectMember))
     572               0 :                     return false;
     573          211445 :                 goto JSONValue;
     574                 :             }
     575              21 :             if (token == ObjectClose) {
     576               0 :                 JS_ASSERT(state == FinishObjectMember);
     577               0 :                 JS_ASSERT(parsingMode == LegacyJSON);
     578               0 :                 break;
     579                 :             }
     580              21 :             if (token == OOM)
     581               0 :                 return false;
     582              21 :             if (token != Error)
     583               0 :                 error("property names must be double-quoted strings");
     584              21 :             return errorReturn();
     585                 : 
     586                 :           case FinishArrayElement: {
     587           45279 :             Value v = valueStack.popCopy();
     588           45279 :             if (!js_NewbornArrayPush(cx, &valueStack.back().toObject(), v))
     589               0 :                 return false;
     590           45279 :             token = advanceAfterArrayElement();
     591           45279 :             if (token == Comma) {
     592           39782 :                 if (!stateStack.append(FinishArrayElement))
     593               0 :                     return false;
     594           39782 :                 goto JSONValue;
     595                 :             }
     596            5497 :             if (token == ArrayClose)
     597            5362 :                 break;
     598             135 :             JS_ASSERT(token == Error);
     599             135 :             return errorReturn();
     600                 :           }
     601                 : 
     602                 :           JSONValue:
     603                 :           case JSONValue:
     604          264733 :             token = advance();
     605                 :           JSONValueSwitch:
     606          272012 :             switch (token) {
     607                 :               case String:
     608                 :               case Number:
     609          203052 :                 if (!valueStack.append(token == String ? stringValue() : numberValue()))
     610               0 :                     return false;
     611          203052 :                 break;
     612                 :               case True:
     613             247 :                 if (!valueStack.append(BooleanValue(true)))
     614               0 :                     return false;
     615             247 :                 break;
     616                 :               case False:
     617            3269 :                 if (!valueStack.append(BooleanValue(false)))
     618               0 :                     return false;
     619            3269 :                 break;
     620                 :               case Null:
     621            3934 :                 if (!valueStack.append(NullValue()))
     622               0 :                     return false;
     623            3934 :                 break;
     624                 : 
     625                 :               case ArrayOpen: {
     626            9161 :                 JSObject *obj = NewDenseEmptyArray(cx);
     627            9161 :                 if (!obj || !valueStack.append(ObjectValue(*obj)))
     628               0 :                     return false;
     629            9161 :                 token = advance();
     630            9161 :                 if (token == ArrayClose)
     631            1882 :                     break;
     632            7279 :                 if (!stateStack.append(FinishArrayElement))
     633               0 :                     return false;
     634            7279 :                 goto JSONValueSwitch;
     635                 :               }
     636                 : 
     637                 :               case ObjectOpen: {
     638           49365 :                 JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
     639           49365 :                 if (!obj || !valueStack.append(ObjectValue(*obj)))
     640               0 :                     return false;
     641           49365 :                 token = advanceAfterObjectOpen();
     642           49365 :                 if (token == ObjectClose)
     643            1303 :                     break;
     644           48062 :                 goto JSONMember;
     645                 :               }
     646                 : 
     647                 :               case ArrayClose:
     648              20 :                 if (parsingMode == LegacyJSON &&
     649               3 :                     !stateStack.empty() &&
     650               3 :                     stateStack.back() == FinishArrayElement) {
     651                 :                     /*
     652                 :                      * Previous JSON parsing accepted trailing commas in
     653                 :                      * non-empty array syntax, and some users depend on this.
     654                 :                      * (Specifically, Places data serialization in versions of
     655                 :                      * Firefox prior to 4.0.  We can remove this mode when
     656                 :                      * profile upgrades from 3.6 become unsupported.)  Permit
     657                 :                      * such trailing commas only when specifically
     658                 :                      * instructed to do so.
     659                 :                      */
     660               3 :                     stateStack.popBack();
     661               3 :                     break;
     662                 :                 }
     663                 :                 /* FALL THROUGH */
     664                 : 
     665                 :               case ObjectClose:
     666                 :               case Colon:
     667                 :               case Comma:
     668              40 :                 error("unexpected character");
     669              40 :                 return errorReturn();
     670                 : 
     671                 :               case OOM:
     672               0 :                 return false;
     673                 : 
     674                 :               case Error:
     675            2941 :                 return errorReturn();
     676                 :             }
     677          213690 :             break;
     678                 :         }
     679                 : 
     680          267093 :         if (stateStack.empty())
     681                 :             break;
     682          256724 :         state = stateStack.popCopy();
     683                 :     }
     684                 : 
     685           10371 :     for (; current < end; current++) {
     686              38 :         if (!IsJSONWhitespace(*current)) {
     687              36 :             error("unexpected non-whitespace character after JSON data");
     688              36 :             return errorReturn();
     689                 :         }
     690                 :     }
     691                 : 
     692           10333 :     JS_ASSERT(end == current);
     693           10333 :     JS_ASSERT(valueStack.length() == 1);
     694           10333 :     *vp = valueStack[0];
     695           10333 :     return true;
     696                 : }

Generated by: LCOV version 1.7