LCOV - app.info - js/src/jsonparser.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsonparser.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 335 | 246 | 73.4 %
| Date: | 2012-04-07 | 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 "vm/StringBuffer.h"
          46                 :
          47                 : #include "jsobjinlines.h"
          48                 :
          49                 : using namespace js;
          50                 :
          51                 : void
          52            3127 : JSONParser::error(const char *msg)
          53                 : {
          54            3127 :     if (errorHandling == RaiseError)
          55             199 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, msg);
          56            3127 : }
          57                 :
          58                 : bool
          59            3127 : JSONParser::errorReturn()
          60                 : {
          61            3127 :     return errorHandling == NoError;
          62                 : }
          63                 :
          64                 : template<JSONParser::StringType ST>
          65                 : JSONParser::Token
          66           67788 : JSONParser::readString()
          67                 : {
          68           67788 :     JS_ASSERT(current < end);
          69           67788 :     JS_ASSERT(*current == '"');
          70                 :
          71                 :     /*
          72                 :      * JSONString:
          73                 :      *   /^"([^\u0000-\u001F"\\]|\\(["/\\bfnrt]|u[0-9a-fA-F]{4}))*"$/
          74                 :      */
          75                 :
          76           67788 :     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           67787 :     RangedPtr<const jschar> start = current;
          86          605098 :     for (; current < end; current++) {
          87          605098 :         if (*current == '"') {
          88           67782 :             size_t length = current - start;
          89           67782 :             current++;
          90                 :             JSFlatString *str = (ST == JSONParser::PropertyName)
          91                 :                                 ? js_AtomizeChars(cx, start.get(), length)
          92           67782 :                                 : js_NewStringCopyN(cx, start.get(), length);
          93           67782 :             if (!str)
          94               0 :                 return token(OOM);
          95           67782 :             return stringToken(str);
          96                 :         }
          97                 :
          98          537316 :         if (*current == '\\')
          99               5 :             break;
         100                 :
         101          537311 :         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              10 :     StringBuffer buffer(cx);
         113               5 :     do {
         114              10 :         if (start < current && !buffer.append(start.get(), current.get()))
         115               0 :             return token(OOM);
         116                 :
         117              10 :         if (current >= end)
         118               0 :             break;
         119                 :
         120              10 :         jschar c = *current++;
         121              10 :         if (c == '"') {
         122                 :             JSFlatString *str = (ST == JSONParser::PropertyName)
         123                 :                                 ? buffer.finishAtom()
         124               5 :                                 : buffer.finishString();
         125               5 :             if (!str)
         126               0 :                 return token(OOM);
         127               5 :             return stringToken(str);
         128                 :         }
         129                 :
         130               5 :         if (c != '\\') {
         131               0 :             error("bad character in string literal");
         132               0 :             return token(Error);
         133                 :         }
         134                 :
         135               5 :         if (current >= end)
         136               0 :             break;
         137                 :
         138               5 :         switch (*current++) {
         139               0 :           case '"':  c = '"';  break;
         140               0 :           case '/':  c = '/';  break;
         141               0 :           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               5 :         if (!buffer.append(c))
         172               0 :             return token(OOM);
         173                 :
         174               5 :         start = current;
         175               5 :         for (; current < end; current++) {
         176               5 :             if (*current == '"' || *current == '\\' || *current <= 0x001F)
         177              10 :                 break;
         178                 :         }
         179                 :     } while (current < end);
         180                 :
         181               0 :     error("unterminated string");
         182               0 :     return token(Error);
         183                 : }
         184                 :
         185                 : JSONParser::Token
         186           25250 : JSONParser::readNumber()
         187                 : {
         188           25250 :     JS_ASSERT(current < end);
         189           25250 :     JS_ASSERT(JS7_ISDEC(*current) || *current == '-');
         190                 :
         191                 :     /*
         192                 :      * JSONNumber:
         193                 :      *   /^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][\+\-]?[0-9]+)?$/
         194                 :      */
         195                 :
         196           25250 :     bool negative = *current == '-';
         197                 :
         198                 :     /* -? */
         199           25250 :     if (negative && ++current == end) {
         200               0 :         error("no number after minus sign");
         201               0 :         return token(Error);
         202                 :     }
         203                 :
         204           25250 :     const RangedPtr<const jschar> digitStart = current;
         205                 :
         206                 :     /* 0|[1-9][0-9]+ */
         207           25250 :     if (!JS7_ISDEC(*current)) {
         208             162 :         error("unexpected non-digit");
         209             162 :         return token(Error);
         210                 :     }
         211           25088 :     if (*current++ != '0') {
         212          110192 :         for (; current < end; current++) {
         213          110188 :             if (!JS7_ISDEC(*current))
         214           23805 :                 break;
         215                 :         }
         216                 :     }
         217                 :
         218                 :     /* Fast path: no fractional or exponent part. */
         219           25088 :     if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
         220                 :         const jschar *dummy;
         221                 :         double d;
         222           24481 :         if (!GetPrefixInteger(cx, digitStart.get(), current.get(), 10, &dummy, &d))
         223               0 :             return token(OOM);
         224           24481 :         JS_ASSERT(current == dummy);
         225           24481 :         return numberToken(negative ? -d : d);
         226                 :     }
         227                 :
         228                 :     /* (\.[0-9]+)? */
         229             607 :     if (current < end && *current == '.') {
         230             605 :         if (++current == end) {
         231               0 :             error("missing digits after decimal point");
         232               0 :             return token(Error);
         233                 :         }
         234             605 :         if (!JS7_ISDEC(*current)) {
         235               0 :             error("unterminated fractional number");
         236               0 :             return token(Error);
         237                 :         }
         238            4236 :         while (++current < end) {
         239            3629 :             if (!JS7_ISDEC(*current))
         240             603 :                 break;
         241                 :         }
         242                 :     }
         243                 :
         244                 :     /* ([eE][\+\-]?[0-9]+)? */
         245             607 :     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             607 :     if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d))
         269               0 :         return token(OOM);
         270             607 :     JS_ASSERT(current == finish);
         271             607 :     return numberToken(negative ? -d : d);
         272                 : }
         273                 :
         274                 : static inline bool
         275          644153 : IsJSONWhitespace(jschar c)
         276                 : {
         277          644153 :     return c == '\t' || c == '\r' || c == '\n' || c == ' ';
         278                 : }
         279                 :
         280                 : JSONParser::Token
         281           77704 : JSONParser::advance()
         282                 : {
         283          268323 :     while (current < end && IsJSONWhitespace(*current))
         284          112915 :         current++;
         285           77704 :     if (current >= end) {
         286               1 :         error("unexpected end of data");
         287               1 :         return token(Error);
         288                 :     }
         289                 :
         290           77703 :     switch (*current) {
         291                 :       case '"':
         292           22678 :         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           25250 :         return readNumber();
         306                 :
         307                 :       case 't':
         308             128 :         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             128 :         current += 4;
         313             128 :         return token(True);
         314                 :
         315                 :       case 'f':
         316            2482 :         if (end - current < 5 ||
         317            1433 :             current[1] != 'a' || current[2] != 'l' || current[3] != 's' || current[4] != 'e')
         318                 :         {
         319             921 :             error("unexpected keyword");
         320             921 :             return token(Error);
         321                 :         }
         322             128 :         current += 5;
         323             128 :         return token(False);
         324                 :
         325                 :       case 'n':
         326             200 :         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             182 :         current += 4;
         331             182 :         return token(Null);
         332                 :
         333                 :       case '[':
         334            3950 :         current++;
         335            3950 :         return token(ArrayOpen);
         336                 :       case ']':
         337              13 :         current++;
         338              13 :         return token(ArrayClose);
         339                 :
         340                 :       case '{':
         341           22614 :         current++;
         342           22614 :         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            1792 :         error("unexpected character");
         357            1792 :         return token(Error);
         358                 :     }
         359                 : }
         360                 :
         361                 : JSONParser::Token
         362           22614 : JSONParser::advanceAfterObjectOpen()
         363                 : {
         364           22614 :     JS_ASSERT(current[-1] == '{');
         365                 :
         366          157729 :     while (current < end && IsJSONWhitespace(*current))
         367          112501 :         current++;
         368           22614 :     if (current >= end) {
         369               0 :         error("end of data while reading object contents");
         370               0 :         return token(Error);
         371                 :     }
         372                 :
         373           22614 :     if (*current == '"')
         374           22547 :         return readString<PropertyName>();
         375                 :
         376              67 :     if (*current == '}') {
         377              47 :         current++;
         378              47 :         return token(ObjectClose);
         379                 :     }
         380                 :
         381              20 :     error("expected property name or '}'");
         382              20 :     return token(Error);
         383                 : }
         384                 :
         385                 : static inline void
         386           70709 : 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          404045 :     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          404045 :               JS7_ISDEC(current[-1]));
         410           70709 : }
         411                 :
         412                 : JSONParser::Token
         413           25599 : JSONParser::advanceAfterArrayElement()
         414                 : {
         415           25599 :     AssertPastValue(current);
         416                 :
         417           51198 :     while (current < end && IsJSONWhitespace(*current))
         418               0 :         current++;
         419           25599 :     if (current >= end) {
         420               0 :         error("end of data when ',' or ']' was expected");
         421               0 :         return token(Error);
         422                 :     }
         423                 :
         424           25599 :     if (*current == ',') {
         425           23428 :         current++;
         426           23428 :         return token(Comma);
         427                 :     }
         428                 :
         429            2171 :     if (*current == ']') {
         430            2036 :         current++;
         431            2036 :         return token(ArrayClose);
         432                 :     }
         433                 :
         434             135 :     error("expected ',' or ']' after array element");
         435             135 :     return token(Error);
         436                 : }
         437                 :
         438                 : JSONParser::Token
         439           22564 : JSONParser::advancePropertyName()
         440                 : {
         441           22564 :     JS_ASSERT(current[-1] == ',');
         442                 :
         443          157628 :     while (current < end && IsJSONWhitespace(*current))
         444          112500 :         current++;
         445           22564 :     if (current >= end) {
         446               0 :         error("end of data when property name was expected");
         447               0 :         return token(Error);
         448                 :     }
         449                 :
         450           22564 :     if (*current == '"')
         451           22563 :         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           45110 : JSONParser::advancePropertyColon()
         472                 : {
         473           45110 :     JS_ASSERT(current[-1] == '"');
         474                 :
         475           90220 :     while (current < end && IsJSONWhitespace(*current))
         476               0 :         current++;
         477           45110 :     if (current >= end) {
         478               0 :         error("end of data after property name when ':' was expected");
         479               0 :         return token(Error);
         480                 :     }
         481                 :
         482           45110 :     if (*current == ':') {
         483           45110 :         current++;
         484           45110 :         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           45110 : JSONParser::advanceAfterProperty()
         493                 : {
         494           45110 :     AssertPastValue(current);
         495                 :
         496          157721 :     while (current < end && IsJSONWhitespace(*current))
         497           67501 :         current++;
         498           45110 :     if (current >= end) {
         499               0 :         error("end of data after property value in object");
         500               0 :         return token(Error);
         501                 :     }
         502                 :
         503           45110 :     if (*current == ',') {
         504           22564 :         current++;
         505           22564 :         return token(Comma);
         506                 :     }
         507                 :
         508           22546 :     if (*current == '}') {
         509           22546 :         current++;
         510           22546 :         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            5216 : JSONParser::parse(Value *vp)
         525                 : {
         526           10432 :     Vector<ParserState> stateStack(cx);
         527           10432 :     AutoValueVector valueStack(cx);
         528                 :
         529            5216 :     *vp = UndefinedValue();
         530                 :
         531                 :     Token token;
         532            5216 :     ParserState state = JSONValue;
         533           70709 :     while (true) {
         534           75925 :         switch (state) {
         535                 :           case FinishObjectMember: {
         536           45110 :             Value v = valueStack.popCopy();
         537                 :             /*
         538                 :              * NB: Relies on js_DefineNativeProperty performing
         539                 :              *     js_CheckForStringIndex.
         540                 :              */
         541           45110 :             jsid propid = ATOM_TO_JSID(&valueStack.popCopy().toString()->asAtom());
         542           90220 :             if (!DefineNativeProperty(cx, &valueStack.back().toObject(), propid, v,
         543                 :                                       JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
         544           45110 :                                       0, 0))
         545                 :             {
         546               0 :                 return false;
         547                 :             }
         548           45110 :             token = advanceAfterProperty();
         549           45110 :             if (token == ObjectClose)
         550           22546 :                 break;
         551           22564 :             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           22564 :             token = advancePropertyName();
         559                 :             /* FALL THROUGH */
         560                 :           }
         561                 :
         562                 :           JSONMember:
         563           45131 :             if (token == String) {
         564           45110 :                 if (!valueStack.append(atomValue()))
         565               0 :                     return false;
         566           45110 :                 token = advancePropertyColon();
         567           45110 :                 if (token != Colon) {
         568               0 :                     JS_ASSERT(token == Error);
         569               0 :                     return errorReturn();
         570                 :                 }
         571           45110 :                 if (!stateStack.append(FinishObjectMember))
         572               0 :                     return false;
         573           45110 :                 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           25599 :             Value v = valueStack.popCopy();
         588           25599 :             if (!js_NewbornArrayPush(cx, &valueStack.back().toObject(), v))
         589               0 :                 return false;
         590           25599 :             token = advanceAfterArrayElement();
         591           25599 :             if (token == Comma) {
         592           23428 :                 if (!stateStack.append(FinishArrayElement))
         593               0 :                     return false;
         594           23428 :                 goto JSONValue;
         595                 :             }
         596            2171 :             if (token == ArrayClose)
         597            2036 :                 break;
         598             135 :             JS_ASSERT(token == Error);
         599             135 :             return errorReturn();
         600                 :           }
         601                 :
         602                 :           JSONValue:
         603                 :           case JSONValue:
         604           73754 :             token = advance();
         605                 :           JSONValueSwitch:
         606           77702 :             switch (token) {
         607                 :               case String:
         608                 :               case Number:
         609           47765 :                 if (!valueStack.append(token == String ? stringValue() : numberValue()))
         610               0 :                     return false;
         611           47765 :                 break;
         612                 :               case True:
         613             128 :                 if (!valueStack.append(BooleanValue(true)))
         614               0 :                     return false;
         615             128 :                 break;
         616                 :               case False:
         617             128 :                 if (!valueStack.append(BooleanValue(false)))
         618               0 :                     return false;
         619             128 :                 break;
         620                 :               case Null:
         621             182 :                 if (!valueStack.append(NullValue()))
         622               0 :                     return false;
         623             182 :                 break;
         624                 :
         625                 :               case ArrayOpen: {
         626            3950 :                 JSObject *obj = NewDenseEmptyArray(cx);
         627            3950 :                 if (!obj || !valueStack.append(ObjectValue(*obj)))
         628               0 :                     return false;
         629            3950 :                 token = advance();
         630            3950 :                 if (token == ArrayClose)
         631               2 :                     break;
         632            3948 :                 if (!stateStack.append(FinishArrayElement))
         633               0 :                     return false;
         634            3948 :                 goto JSONValueSwitch;
         635                 :               }
         636                 :
         637                 :               case ObjectOpen: {
         638           22614 :                 JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
         639           22614 :                 if (!obj || !valueStack.append(ObjectValue(*obj)))
         640               0 :                     return false;
         641           22614 :                 token = advanceAfterObjectOpen();
         642           22614 :                 if (token == ObjectClose)
         643              47 :                     break;
         644           22567 :                 goto JSONMember;
         645                 :               }
         646                 :
         647                 :               case ArrayClose:
         648              11 :                 if (parsingMode == LegacyJSON &&
         649               0 :                     !stateStack.empty() &&
         650               0 :                     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               0 :                     stateStack.popBack();
         661               0 :                     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            2895 :                 return errorReturn();
         676                 :             }
         677           48252 :             break;
         678                 :         }
         679                 :
         680           72834 :         if (stateStack.empty())
         681                 :             break;
         682           70709 :         state = stateStack.popCopy();
         683                 :     }
         684                 :
         685            2125 :     for (; current < end; current++) {
         686              36 :         if (!IsJSONWhitespace(*current)) {
         687              36 :             error("unexpected non-whitespace character after JSON data");
         688              36 :             return errorReturn();
         689                 :         }
         690                 :     }
         691                 :
         692            2089 :     JS_ASSERT(end == current);
         693            2089 :     JS_ASSERT(valueStack.length() == 1);
         694            2089 :     *vp = valueStack[0];
         695            2089 :     return true;
         696                 : }

Generated by: LCOV version 1.7