Changeset 44923 in webkit


Ignore:
Timestamp:
Jun 21, 2009 4:02:13 PM (15 years ago)
Author:
oliver@apple.com
Message:

Bug 26587: Support JSON.parse
<https://bugs.webkit.org/show_bug.cgi?id=26587>

Reviewed by Darin Adler and Cameron Zwarich.

Extend the LiteralParser to support the full strict JSON
grammar, fix a few places where the grammar was incorrectly
lenient. Doesn't yet support the JSON.parse reviver function
but that does not block the JSON.parse functionality itself.

Location:
trunk
Files:
3 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/ChangeLog

    r44922 r44923  
     12009-06-21  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Darin Adler and Cameron Zwarich.
     4
     5        Bug 26587: Support JSON.parse
     6        <https://bugs.webkit.org/show_bug.cgi?id=26587>
     7
     8        Extend the LiteralParser to support the full strict JSON
     9        grammar, fix a few places where the grammar was incorrectly
     10        lenient.   Doesn't yet support the JSON.parse reviver function
     11        but that does not block the JSON.parse functionality itself.
     12
     13        * interpreter/Interpreter.cpp:
     14        (JSC::Interpreter::callEval):
     15        * runtime/JSGlobalObjectFunctions.cpp:
     16        (JSC::globalFuncEval):
     17        * runtime/JSONObject.cpp:
     18        (JSC::JSONProtoFuncParse):
     19        * runtime/LiteralParser.cpp:
     20        (JSC::LiteralParser::Lexer::lex):
     21        (JSC::isSafeStringCharacter):
     22        (JSC::LiteralParser::Lexer::lexString):
     23        (JSC::LiteralParser::parse):
     24        * runtime/LiteralParser.h:
     25        (JSC::LiteralParser::LiteralParser):
     26        (JSC::LiteralParser::tryJSONParse):
     27        (JSC::LiteralParser::):
     28        (JSC::LiteralParser::Lexer::Lexer):
     29
    1302009-06-21  David Levin  <levin@chromium.org>
    231
  • trunk/JavaScriptCore/interpreter/Interpreter.cpp

    r44705 r44923  
    351351    UString programSource = asString(program)->value();
    352352
    353     LiteralParser preparser(callFrame, programSource);
     353    LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON);
    354354    if (JSValue parsedObject = preparser.tryLiteralParse())
    355355        return parsedObject;
  • trunk/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp

    r43424 r44923  
    283283    UString s = x.toString(exec);
    284284
    285     LiteralParser preparser(exec, s);
     285    LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON);
    286286    if (JSValue parsedObject = preparser.tryLiteralParse())
    287287        return parsedObject;
  • trunk/JavaScriptCore/runtime/JSONObject.cpp

    r44813 r44923  
    3030#include "ExceptionHelpers.h"
    3131#include "JSArray.h"
     32#include "LiteralParser.h"
    3233#include "PropertyNameArray.h"
    3334#include <wtf/MathExtras.h>
     
    3738ASSERT_CLASS_FITS_IN_CELL(JSONObject);
    3839
     40static JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*, JSObject*, JSValue, const ArgList&);
    3941static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&);
    4042
     
    563565/* Source for JSONObject.lut.h
    564566@begin jsonTable
     567  parse         JSONProtoFuncParse             DontEnum|Function 1
    565568  stringify     JSONProtoFuncStringify         DontEnum|Function 1
    566569@end
     
    583586{
    584587    stringifier->mark();
     588}
     589
     590// ECMA-262 v5 15.12.3
     591JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args)
     592{
     593    if (args.isEmpty())
     594        return throwError(exec, GeneralError, "JSON.parse requires at least one parameter");
     595    JSValue value = args.at(0);
     596    UString source = value.toString(exec);
     597    if (exec->hadException())
     598        return jsNull();
     599   
     600    LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
     601    JSValue parsedObject = jsonParser.tryLiteralParse();
     602    if (!parsedObject)
     603        return throwError(exec, SyntaxError, "Unable to parse JSON string");
     604   
     605    return parsedObject;
    585606}
    586607
  • trunk/JavaScriptCore/runtime/LiteralParser.cpp

    r44644 r44923  
    2929#include "JSArray.h"
    3030#include "JSString.h"
     31#include "Lexer.h"
    3132#include <wtf/ASCIICType.h>
    3233
    3334namespace JSC {
    34 
    35 static bool isSafeStringCharacter(UChar c)
    36 {
    37     return (c >= ' ' && c <= 0xff && c != '\\') || c == '\t';
    38 }
    3935
    4036LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
     
    8581            return TokColon;
    8682        case '"':
    87             return lexString(token);
    88 
     83            if (m_mode == StrictJSON)
     84                return lexString<StrictJSON>(token);
     85            return lexString<NonStrictJSON>(token);
     86        case 't':
     87            if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') {
     88                m_ptr += 4;
     89                token.type = TokTrue;
     90                token.end = m_ptr;
     91                return TokTrue;
     92            }
     93            break;
     94        case 'f':
     95            if (m_end - m_ptr >= 5 && m_ptr[1] == 'a' && m_ptr[2] == 'l' && m_ptr[3] == 's' && m_ptr[4] == 'e') {
     96                m_ptr += 5;
     97                token.type = TokFalse;
     98                token.end = m_ptr;
     99                return TokFalse;
     100            }
     101            break;
     102        case 'n':
     103            if (m_end - m_ptr >= 4 && m_ptr[1] == 'u' && m_ptr[2] == 'l' && m_ptr[3] == 'l') {
     104                m_ptr += 4;
     105                token.type = TokNull;
     106                token.end = m_ptr;
     107                return TokNull;
     108            }
     109            break;   
    89110        case '-':
    90111        case '0':
     
    103124}
    104125
    105 LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token)
     126static inline bool isSafeStringCharacter(UChar c)
     127{
     128    return (c >= ' ' && c <= 0xff && c != '\\' && c != '"') || c == '\t';
     129}
     130
     131template <LiteralParser::ParserMode mode> LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token)
    106132{
    107133    ++m_ptr;
    108     while (m_ptr < m_end && isSafeStringCharacter(*m_ptr) && *m_ptr != '"')
    109         ++m_ptr;
    110     if (m_ptr >= m_end || *m_ptr != '"') {
    111         token.type = TokError;
    112         token.end = ++m_ptr;
     134    const UChar* runStart;
     135    token.stringToken = UString();
     136    do {
     137        runStart = m_ptr;
     138        while (m_ptr < m_end && isSafeStringCharacter(*m_ptr))
     139            ++m_ptr;
     140        if (runStart < m_ptr)
     141            token.stringToken.append(runStart, m_ptr - runStart);
     142        if ((mode == StrictJSON) && m_ptr < m_end && *m_ptr == '\\') {
     143            ++m_ptr;
     144            if (m_ptr >= m_end)
     145                return TokError;
     146            switch (*m_ptr) {
     147                case '"':
     148                    token.stringToken.append('"');
     149                    m_ptr++;
     150                    break;
     151                case '\\':
     152                    token.stringToken.append('\\');
     153                    m_ptr++;
     154                    break;
     155                case '/':
     156                    token.stringToken.append('/');
     157                    m_ptr++;
     158                    break;
     159                case 'b':
     160                    token.stringToken.append('\b');
     161                    m_ptr++;
     162                    break;
     163                case 'f':
     164                    token.stringToken.append('\f');
     165                    m_ptr++;
     166                    break;
     167                case 'n':
     168                    token.stringToken.append('\n');
     169                    m_ptr++;
     170                    break;
     171                case 'r':
     172                    token.stringToken.append('\r');
     173                    m_ptr++;
     174                    break;
     175                case 't':
     176                    token.stringToken.append('\t');
     177                    m_ptr++;
     178                    break;
     179
     180                case 'u':
     181                    if ((m_end - m_ptr) < 5) // uNNNN == 5 characters
     182                        return TokError;
     183                    for (int i = 1; i < 5; i++) {
     184                        if (!isASCIIHexDigit(m_ptr[i]))
     185                            return TokError;
     186                    }
     187                    token.stringToken.append(JSC::Lexer::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4]));
     188                    m_ptr += 5;
     189                    break;
     190
     191                default:
     192                    return TokError;
     193            }
     194        }
     195    } while ((mode == StrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != '"');
     196
     197    if (m_ptr >= m_end || *m_ptr != '"')
    113198        return TokError;
    114     }
     199
    115200    token.type = TokString;
    116201    token.end = ++m_ptr;
     
    152237        ++m_ptr;
    153238        // [0-9]+
    154         if (m_ptr >= m_end && !isASCIIDigit(*m_ptr))
     239        if (m_ptr >= m_end || !isASCIIDigit(*m_ptr))
    155240            return TokError;
    156241
     
    169254
    170255        // [0-9]+
    171         if (m_ptr >= m_end && !isASCIIDigit(*m_ptr))
     256        if (m_ptr >= m_end || !isASCIIDigit(*m_ptr))
    172257            return TokError;
    173258       
     
    227312                JSObject* object = constructEmptyObject(m_exec);
    228313                objectStack.append(object);
    229                 // fallthrough
     314
     315                TokenType type = m_lexer.next();
     316                if (type == TokString) {
     317                    Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
     318
     319                    // Check for colon
     320                    if (m_lexer.next() != TokColon)
     321                        return JSValue();
     322                   
     323                    m_lexer.next();
     324                    identifierStack.append(Identifier(m_exec, identifierToken.stringToken));
     325                    stateStack.append(DoParseObjectEndExpression);
     326                    goto startParseExpression;
     327                } else if (type != TokRBrace)
     328                    return JSValue();
     329                m_lexer.next();
     330                lastValue = objectStack.last();
     331                objectStack.removeLast();
     332                break;
    230333            }
    231334            doParseObjectStartExpression:
     
    240343
    241344                    m_lexer.next();
    242                     identifierStack.append(Identifier(m_exec, identifierToken.start + 1, identifierToken.end - identifierToken.start - 2));
     345                    identifierStack.append(Identifier(m_exec, identifierToken.stringToken));
    243346                    stateStack.append(DoParseObjectEndExpression);
    244347                    goto startParseExpression;
    245                 } else if (type != TokRBrace)
     348                } else
    246349                    return JSValue();
    247350                m_lexer.next();
     
    273376                        Lexer::LiteralParserToken stringToken = m_lexer.currentToken();
    274377                        m_lexer.next();
    275                         lastValue = jsString(m_exec, UString(stringToken.start + 1, stringToken.end - stringToken.start - 2));
     378                        lastValue = jsString(m_exec, stringToken.stringToken);
    276379                        break;
    277380                    }
     
    282385                        break;
    283386                    }
     387                    case TokNull:
     388                        m_lexer.next();
     389                        lastValue = jsNull();
     390                        break;
     391
     392                    case TokTrue:
     393                        m_lexer.next();
     394                        lastValue = jsBoolean(true);
     395                        break;
     396
     397                    case TokFalse:
     398                        m_lexer.next();
     399                        lastValue = jsBoolean(false);
     400                        break;
     401
    284402                    default:
    285403                        // Error
  • trunk/JavaScriptCore/runtime/LiteralParser.h

    r44644 r44923  
    3535    class LiteralParser {
    3636    public:
    37         LiteralParser(ExecState* exec, const UString& s)
     37        typedef enum { StrictJSON, NonStrictJSON } ParserMode;
     38        LiteralParser(ExecState* exec, const UString& s, ParserMode mode)
    3839            : m_exec(exec)
    39             , m_lexer(s)
     40            , m_lexer(s, mode)
     41            , m_mode(mode)
    4042        {
    4143        }
     
    4446        {
    4547            m_lexer.next();
    46             JSValue result = parse(StartParseStatement);
     48            JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement);
    4749            if (m_lexer.currentToken().type != TokEnd)
    4850                return JSValue();
     
    5658        enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace,
    5759                         TokString, TokIdentifier, TokNumber, TokColon,
    58                          TokLParen, TokRParen, TokComma, TokEnd, TokError };
     60                         TokLParen, TokRParen, TokComma, TokTrue, TokFalse,
     61                         TokNull, TokEnd, TokError };
    5962
    6063        class Lexer {
     
    6467                const UChar* start;
    6568                const UChar* end;
     69                UString stringToken;
    6670            };
    67             Lexer(const UString& s)
     71            Lexer(const UString& s, ParserMode mode)
    6872                : m_string(s)
     73                , m_mode(mode)
    6974                , m_ptr(s.data())
    7075                , m_end(s.data() + s.size())
     
    8489        private:
    8590            TokenType lex(LiteralParserToken&);
    86             TokenType lexString(LiteralParserToken&);
     91            template <ParserMode parserMode> TokenType lexString(LiteralParserToken&);
    8792            TokenType lexNumber(LiteralParserToken&);
    8893            LiteralParserToken m_currentToken;
    8994            UString m_string;
     95            ParserMode m_mode;
    9096            const UChar* m_ptr;
    9197            const UChar* m_end;
     
    97103        ExecState* m_exec;
    98104        LiteralParser::Lexer m_lexer;
     105        ParserMode m_mode;
    99106    };
    100107}
  • trunk/LayoutTests/ChangeLog

    r44916 r44923  
     12009-06-21  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Darin Adler and Cameron Zwarich.
     4
     5        Bug 26587: Support JSON.parse
     6
     7        Add tests to cover basic usage of JSON.parse
     8
     9        * fast/js/JSON-parse.html: Added.
     10        * fast/js/JSON-parse-expected.txt: Added.
     11        * fast/js/resources/JSON-parse.js: Added.
     12        (createTests.result):
     13        (createTests):
     14
    1152009-06-21  Drew Wilson  <atwilson@google.com>
    216 
Note: See TracChangeset for help on using the changeset viewer.