| /* |
| * Copyright (C) 2009 Apple Inc. All rights reserved. |
| * Copyright (C) 2012 Mathias Bynens (mathias@qiwi.be) |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "LiteralParser.h" |
| |
| #include "ButterflyInlines.h" |
| #include "CopiedSpaceInlines.h" |
| #include "JSArray.h" |
| #include "JSString.h" |
| #include "Lexer.h" |
| #include "StrongInlines.h" |
| #include <wtf/ASCIICType.h> |
| #include <wtf/dtoa.h> |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace JSC { |
| |
| template <typename CharType> |
| static inline bool isJSONWhiteSpace(const CharType& c) |
| { |
| // The JSON RFC 4627 defines a list of allowed characters to be considered |
| // insignificant white space: http://www.ietf.org/rfc/rfc4627.txt (2. JSON Grammar). |
| return c == ' ' || c == 0x9 || c == 0xA || c == 0xD; |
| } |
| |
| template <typename CharType> |
| bool LiteralParser<CharType>::tryJSONPParse(Vector<JSONPData>& results, bool needsFullSourceInfo) |
| { |
| if (m_lexer.next() != TokIdentifier) |
| return false; |
| do { |
| Vector<JSONPPathEntry> path; |
| // Unguarded next to start off the lexer |
| Identifier name = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); |
| JSONPPathEntry entry; |
| if (name == m_exec->globalData().propertyNames->varKeyword) { |
| if (m_lexer.next() != TokIdentifier) |
| return false; |
| entry.m_type = JSONPPathEntryTypeDeclare; |
| entry.m_pathEntryName = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); |
| path.append(entry); |
| } else { |
| entry.m_type = JSONPPathEntryTypeDot; |
| entry.m_pathEntryName = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); |
| path.append(entry); |
| } |
| if (m_exec->globalData().keywords->isKeyword(entry.m_pathEntryName)) |
| return false; |
| TokenType tokenType = m_lexer.next(); |
| while (tokenType != TokAssign) { |
| switch (tokenType) { |
| case TokLBracket: { |
| entry.m_type = JSONPPathEntryTypeLookup; |
| if (m_lexer.next() != TokNumber) |
| return false; |
| double doubleIndex = m_lexer.currentToken().numberToken; |
| int index = (int)doubleIndex; |
| if (index != doubleIndex || index < 0) |
| return false; |
| entry.m_pathIndex = index; |
| if (m_lexer.next() != TokRBracket) |
| return false; |
| break; |
| } |
| case TokDot: { |
| entry.m_type = JSONPPathEntryTypeDot; |
| if (m_lexer.next() != TokIdentifier) |
| return false; |
| entry.m_pathEntryName = Identifier(&m_exec->globalData(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); |
| break; |
| } |
| case TokLParen: { |
| if (path.last().m_type != JSONPPathEntryTypeDot || needsFullSourceInfo) |
| return false; |
| path.last().m_type = JSONPPathEntryTypeCall; |
| entry = path.last(); |
| goto startJSON; |
| } |
| default: |
| return false; |
| } |
| path.append(entry); |
| tokenType = m_lexer.next(); |
| } |
| startJSON: |
| m_lexer.next(); |
| results.append(JSONPData()); |
| results.last().m_value.set(m_exec->globalData(), parse(StartParseExpression)); |
| if (!results.last().m_value) |
| return false; |
| results.last().m_path.swap(path); |
| if (entry.m_type == JSONPPathEntryTypeCall) { |
| if (m_lexer.currentToken().type != TokRParen) |
| return false; |
| m_lexer.next(); |
| } |
| if (m_lexer.currentToken().type != TokSemi) |
| break; |
| m_lexer.next(); |
| } while (m_lexer.currentToken().type == TokIdentifier); |
| return m_lexer.currentToken().type == TokEnd; |
| } |
| |
| template <typename CharType> |
| ALWAYS_INLINE const Identifier LiteralParser<CharType>::makeIdentifier(const LChar* characters, size_t length) |
| { |
| if (!length) |
| return m_exec->globalData().propertyNames->emptyIdentifier; |
| if (characters[0] >= MaximumCachableCharacter) |
| return Identifier(&m_exec->globalData(), characters, length); |
| |
| if (length == 1) { |
| if (!m_shortIdentifiers[characters[0]].isNull()) |
| return m_shortIdentifiers[characters[0]]; |
| m_shortIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); |
| return m_shortIdentifiers[characters[0]]; |
| } |
| if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length)) |
| return m_recentIdentifiers[characters[0]]; |
| m_recentIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); |
| return m_recentIdentifiers[characters[0]]; |
| } |
| |
| template <typename CharType> |
| ALWAYS_INLINE const Identifier LiteralParser<CharType>::makeIdentifier(const UChar* characters, size_t length) |
| { |
| if (!length) |
| return m_exec->globalData().propertyNames->emptyIdentifier; |
| if (characters[0] >= MaximumCachableCharacter) |
| return Identifier(&m_exec->globalData(), characters, length); |
| |
| if (length == 1) { |
| if (!m_shortIdentifiers[characters[0]].isNull()) |
| return m_shortIdentifiers[characters[0]]; |
| m_shortIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); |
| return m_shortIdentifiers[characters[0]]; |
| } |
| if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length)) |
| return m_recentIdentifiers[characters[0]]; |
| m_recentIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length); |
| return m_recentIdentifiers[characters[0]]; |
| } |
| |
| template <typename CharType> |
| template <ParserMode mode> TokenType LiteralParser<CharType>::Lexer::lex(LiteralParserToken<CharType>& token) |
| { |
| while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr)) |
| ++m_ptr; |
| |
| ASSERT(m_ptr <= m_end); |
| if (m_ptr >= m_end) { |
| token.type = TokEnd; |
| token.start = token.end = m_ptr; |
| return TokEnd; |
| } |
| token.type = TokError; |
| token.start = m_ptr; |
| switch (*m_ptr) { |
| case '[': |
| token.type = TokLBracket; |
| token.end = ++m_ptr; |
| return TokLBracket; |
| case ']': |
| token.type = TokRBracket; |
| token.end = ++m_ptr; |
| return TokRBracket; |
| case '(': |
| token.type = TokLParen; |
| token.end = ++m_ptr; |
| return TokLParen; |
| case ')': |
| token.type = TokRParen; |
| token.end = ++m_ptr; |
| return TokRParen; |
| case '{': |
| token.type = TokLBrace; |
| token.end = ++m_ptr; |
| return TokLBrace; |
| case '}': |
| token.type = TokRBrace; |
| token.end = ++m_ptr; |
| return TokRBrace; |
| case ',': |
| token.type = TokComma; |
| token.end = ++m_ptr; |
| return TokComma; |
| case ':': |
| token.type = TokColon; |
| token.end = ++m_ptr; |
| return TokColon; |
| case '"': |
| return lexString<mode, '"'>(token); |
| case 't': |
| if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') { |
| m_ptr += 4; |
| token.type = TokTrue; |
| token.end = m_ptr; |
| return TokTrue; |
| } |
| break; |
| case 'f': |
| if (m_end - m_ptr >= 5 && m_ptr[1] == 'a' && m_ptr[2] == 'l' && m_ptr[3] == 's' && m_ptr[4] == 'e') { |
| m_ptr += 5; |
| token.type = TokFalse; |
| token.end = m_ptr; |
| return TokFalse; |
| } |
| break; |
| case 'n': |
| if (m_end - m_ptr >= 4 && m_ptr[1] == 'u' && m_ptr[2] == 'l' && m_ptr[3] == 'l') { |
| m_ptr += 4; |
| token.type = TokNull; |
| token.end = m_ptr; |
| return TokNull; |
| } |
| break; |
| case '-': |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| return lexNumber(token); |
| } |
| if (m_ptr < m_end) { |
| if (*m_ptr == '.') { |
| token.type = TokDot; |
| token.end = ++m_ptr; |
| return TokDot; |
| } |
| if (*m_ptr == '=') { |
| token.type = TokAssign; |
| token.end = ++m_ptr; |
| return TokAssign; |
| } |
| if (*m_ptr == ';') { |
| token.type = TokSemi; |
| token.end = ++m_ptr; |
| return TokAssign; |
| } |
| if (isASCIIAlpha(*m_ptr) || *m_ptr == '_' || *m_ptr == '$') |
| return lexIdentifier(token); |
| if (*m_ptr == '\'') { |
| if (mode == StrictJSON) { |
| m_lexErrorMessage = ASCIILiteral("Single quotes (\') are not allowed in JSON"); |
| return TokError; |
| } |
| return lexString<mode, '\''>(token); |
| } |
| } |
| m_lexErrorMessage = String::format("Unrecognized token '%c'", *m_ptr).impl(); |
| return TokError; |
| } |
| |
| template <> |
| ALWAYS_INLINE TokenType LiteralParser<LChar>::Lexer::lexIdentifier(LiteralParserToken<LChar>& token) |
| { |
| while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$')) |
| m_ptr++; |
| token.stringIs8Bit = 1; |
| token.stringToken8 = token.start; |
| token.stringLength = m_ptr - token.start; |
| token.type = TokIdentifier; |
| token.end = m_ptr; |
| return TokIdentifier; |
| } |
| |
| template <> |
| ALWAYS_INLINE TokenType LiteralParser<UChar>::Lexer::lexIdentifier(LiteralParserToken<UChar>& token) |
| { |
| while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$' || *m_ptr == 0x200C || *m_ptr == 0x200D)) |
| m_ptr++; |
| token.stringIs8Bit = 0; |
| token.stringToken16 = token.start; |
| token.stringLength = m_ptr - token.start; |
| token.type = TokIdentifier; |
| token.end = m_ptr; |
| return TokIdentifier; |
| } |
| |
| template <typename CharType> |
| TokenType LiteralParser<CharType>::Lexer::next() |
| { |
| if (m_mode == NonStrictJSON) |
| return lex<NonStrictJSON>(m_currentToken); |
| if (m_mode == JSONP) |
| return lex<JSONP>(m_currentToken); |
| return lex<StrictJSON>(m_currentToken); |
| } |
| |
| template <> |
| ALWAYS_INLINE void setParserTokenString<LChar>(LiteralParserToken<LChar>& token, const LChar* string) |
| { |
| token.stringIs8Bit = 1; |
| token.stringToken8 = string; |
| } |
| |
| template <> |
| ALWAYS_INLINE void setParserTokenString<UChar>(LiteralParserToken<UChar>& token, const UChar* string) |
| { |
| token.stringIs8Bit = 0; |
| token.stringToken16 = string; |
| } |
| |
| template <ParserMode mode, typename CharType, LChar terminator> static inline bool isSafeStringCharacter(LChar c) |
| { |
| return (c >= ' ' && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON); |
| } |
| |
| template <ParserMode mode, typename CharType, UChar terminator> static inline bool isSafeStringCharacter(UChar c) |
| { |
| return (c >= ' ' && (mode == StrictJSON || c <= 0xff) && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON); |
| } |
| |
| template <typename CharType> |
| template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType LiteralParser<CharType>::Lexer::lexString(LiteralParserToken<CharType>& token) |
| { |
| ++m_ptr; |
| const CharType* runStart = m_ptr; |
| StringBuilder builder; |
| do { |
| runStart = m_ptr; |
| while (m_ptr < m_end && isSafeStringCharacter<mode, CharType, terminator>(*m_ptr)) |
| ++m_ptr; |
| if (builder.length()) |
| builder.append(runStart, m_ptr - runStart); |
| if ((mode != NonStrictJSON) && m_ptr < m_end && *m_ptr == '\\') { |
| if (builder.isEmpty() && runStart < m_ptr) |
| builder.append(runStart, m_ptr - runStart); |
| ++m_ptr; |
| if (m_ptr >= m_end) { |
| m_lexErrorMessage = ASCIILiteral("Unterminated string"); |
| return TokError; |
| } |
| switch (*m_ptr) { |
| case '"': |
| builder.append('"'); |
| m_ptr++; |
| break; |
| case '\\': |
| builder.append('\\'); |
| m_ptr++; |
| break; |
| case '/': |
| builder.append('/'); |
| m_ptr++; |
| break; |
| case 'b': |
| builder.append('\b'); |
| m_ptr++; |
| break; |
| case 'f': |
| builder.append('\f'); |
| m_ptr++; |
| break; |
| case 'n': |
| builder.append('\n'); |
| m_ptr++; |
| break; |
| case 'r': |
| builder.append('\r'); |
| m_ptr++; |
| break; |
| case 't': |
| builder.append('\t'); |
| m_ptr++; |
| break; |
| |
| case 'u': |
| if ((m_end - m_ptr) < 5) { |
| m_lexErrorMessage = ASCIILiteral("\\u must be followed by 4 hex digits"); |
| return TokError; |
| } // uNNNN == 5 characters |
| for (int i = 1; i < 5; i++) { |
| if (!isASCIIHexDigit(m_ptr[i])) { |
| m_lexErrorMessage = String::format("\"\\%s\" is not a valid unicode escape", String(m_ptr, 5).ascii().data()).impl(); |
| return TokError; |
| } |
| } |
| builder.append(JSC::Lexer<CharType>::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4])); |
| m_ptr += 5; |
| break; |
| |
| default: |
| if (*m_ptr == '\'' && mode != StrictJSON) { |
| builder.append('\''); |
| m_ptr++; |
| break; |
| } |
| m_lexErrorMessage = String::format("Invalid escape character %c", *m_ptr).impl(); |
| return TokError; |
| } |
| } |
| } while ((mode != NonStrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != terminator); |
| |
| if (m_ptr >= m_end || *m_ptr != terminator) { |
| m_lexErrorMessage = ASCIILiteral("Unterminated string"); |
| return TokError; |
| } |
| |
| if (builder.isEmpty()) { |
| token.stringBuffer = String(); |
| setParserTokenString<CharType>(token, runStart); |
| token.stringLength = m_ptr - runStart; |
| } else { |
| token.stringBuffer = builder.toString(); |
| if (token.stringBuffer.is8Bit()) { |
| token.stringIs8Bit = 1; |
| token.stringToken8 = token.stringBuffer.characters8(); |
| } else { |
| token.stringIs8Bit = 0; |
| token.stringToken16 = token.stringBuffer.characters16(); |
| } |
| token.stringLength = token.stringBuffer.length(); |
| } |
| token.type = TokString; |
| token.end = ++m_ptr; |
| return TokString; |
| } |
| |
| template <typename CharType> |
| TokenType LiteralParser<CharType>::Lexer::lexNumber(LiteralParserToken<CharType>& token) |
| { |
| // ES5 and json.org define numbers as |
| // number |
| // int |
| // int frac? exp? |
| // |
| // int |
| // -? 0 |
| // -? digit1-9 digits? |
| // |
| // digits |
| // digit digits? |
| // |
| // -?(0 | [1-9][0-9]*) ('.' [0-9]+)? ([eE][+-]? [0-9]+)? |
| |
| if (m_ptr < m_end && *m_ptr == '-') // -? |
| ++m_ptr; |
| |
| // (0 | [1-9][0-9]*) |
| if (m_ptr < m_end && *m_ptr == '0') // 0 |
| ++m_ptr; |
| else if (m_ptr < m_end && *m_ptr >= '1' && *m_ptr <= '9') { // [1-9] |
| ++m_ptr; |
| // [0-9]* |
| while (m_ptr < m_end && isASCIIDigit(*m_ptr)) |
| ++m_ptr; |
| } else { |
| m_lexErrorMessage = ASCIILiteral("Invalid number"); |
| return TokError; |
| } |
| |
| // ('.' [0-9]+)? |
| if (m_ptr < m_end && *m_ptr == '.') { |
| ++m_ptr; |
| // [0-9]+ |
| if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) { |
| m_lexErrorMessage = ASCIILiteral("Invalid digits after decimal point"); |
| return TokError; |
| } |
| |
| ++m_ptr; |
| while (m_ptr < m_end && isASCIIDigit(*m_ptr)) |
| ++m_ptr; |
| } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) < 10) { |
| int result = 0; |
| token.type = TokNumber; |
| token.end = m_ptr; |
| const CharType* digit = token.start; |
| int negative = 1; |
| if (*digit == '-') { |
| negative = -1; |
| digit++; |
| } |
| |
| while (digit < m_ptr) |
| result = result * 10 + (*digit++) - '0'; |
| result *= negative; |
| token.numberToken = result; |
| return TokNumber; |
| } |
| |
| // ([eE][+-]? [0-9]+)? |
| if (m_ptr < m_end && (*m_ptr == 'e' || *m_ptr == 'E')) { // [eE] |
| ++m_ptr; |
| |
| // [-+]? |
| if (m_ptr < m_end && (*m_ptr == '-' || *m_ptr == '+')) |
| ++m_ptr; |
| |
| // [0-9]+ |
| if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) { |
| m_lexErrorMessage = ASCIILiteral("Exponent symbols should be followed by an optional '+' or '-' and then by at least one number"); |
| return TokError; |
| } |
| |
| ++m_ptr; |
| while (m_ptr < m_end && isASCIIDigit(*m_ptr)) |
| ++m_ptr; |
| } |
| |
| token.type = TokNumber; |
| token.end = m_ptr; |
| size_t parsedLength; |
| token.numberToken = parseDouble(token.start, token.end - token.start, parsedLength); |
| return TokNumber; |
| } |
| |
| template <typename CharType> |
| JSValue LiteralParser<CharType>::parse(ParserState initialState) |
| { |
| ParserState state = initialState; |
| MarkedArgumentBuffer objectStack; |
| JSValue lastValue; |
| Vector<ParserState, 16> stateStack; |
| Vector<Identifier, 16> identifierStack; |
| while (1) { |
| switch(state) { |
| startParseArray: |
| case StartParseArray: { |
| JSArray* array = constructEmptyArray(m_exec, 0); |
| objectStack.append(array); |
| // fallthrough |
| } |
| doParseArrayStartExpression: |
| case DoParseArrayStartExpression: { |
| TokenType lastToken = m_lexer.currentToken().type; |
| if (m_lexer.next() == TokRBracket) { |
| if (lastToken == TokComma) { |
| m_parseErrorMessage = ASCIILiteral("Unexpected comma at the end of array expression"); |
| return JSValue(); |
| } |
| m_lexer.next(); |
| lastValue = objectStack.last(); |
| objectStack.removeLast(); |
| break; |
| } |
| |
| stateStack.append(DoParseArrayEndExpression); |
| goto startParseExpression; |
| } |
| case DoParseArrayEndExpression: { |
| JSArray* array = asArray(objectStack.last()); |
| array->putDirectIndex(m_exec, array->length(), lastValue); |
| |
| if (m_lexer.currentToken().type == TokComma) |
| goto doParseArrayStartExpression; |
| |
| if (m_lexer.currentToken().type != TokRBracket) { |
| m_parseErrorMessage = ASCIILiteral("Expected ']'"); |
| return JSValue(); |
| } |
| |
| m_lexer.next(); |
| lastValue = objectStack.last(); |
| objectStack.removeLast(); |
| break; |
| } |
| startParseObject: |
| case StartParseObject: { |
| JSObject* object = constructEmptyObject(m_exec); |
| objectStack.append(object); |
| |
| TokenType type = m_lexer.next(); |
| if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) { |
| LiteralParserToken<CharType> identifierToken = m_lexer.currentToken(); |
| |
| // Check for colon |
| if (m_lexer.next() != TokColon) { |
| m_parseErrorMessage = ASCIILiteral("Expected ':' before value in object property definition"); |
| return JSValue(); |
| } |
| |
| m_lexer.next(); |
| if (identifierToken.stringIs8Bit) |
| identifierStack.append(makeIdentifier(identifierToken.stringToken8, identifierToken.stringLength)); |
| else |
| identifierStack.append(makeIdentifier(identifierToken.stringToken16, identifierToken.stringLength)); |
| stateStack.append(DoParseObjectEndExpression); |
| goto startParseExpression; |
| } |
| if (type != TokRBrace) { |
| m_parseErrorMessage = ASCIILiteral("Expected '}'"); |
| return JSValue(); |
| } |
| m_lexer.next(); |
| lastValue = objectStack.last(); |
| objectStack.removeLast(); |
| break; |
| } |
| doParseObjectStartExpression: |
| case DoParseObjectStartExpression: { |
| TokenType type = m_lexer.next(); |
| if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier)) { |
| m_parseErrorMessage = ASCIILiteral("Property name must be a string literal"); |
| return JSValue(); |
| } |
| LiteralParserToken<CharType> identifierToken = m_lexer.currentToken(); |
| |
| // Check for colon |
| if (m_lexer.next() != TokColon) { |
| m_parseErrorMessage = ASCIILiteral("Expected ':'"); |
| return JSValue(); |
| } |
| |
| m_lexer.next(); |
| if (identifierToken.stringIs8Bit) |
| identifierStack.append(makeIdentifier(identifierToken.stringToken8, identifierToken.stringLength)); |
| else |
| identifierStack.append(makeIdentifier(identifierToken.stringToken16, identifierToken.stringLength)); |
| stateStack.append(DoParseObjectEndExpression); |
| goto startParseExpression; |
| } |
| case DoParseObjectEndExpression: |
| { |
| JSObject* object = asObject(objectStack.last()); |
| PropertyName ident = identifierStack.last(); |
| unsigned i = ident.asIndex(); |
| if (i != PropertyName::NotAnIndex) |
| object->putDirectIndex(m_exec, i, lastValue); |
| else |
| object->putDirect(m_exec->globalData(), ident, lastValue); |
| identifierStack.removeLast(); |
| if (m_lexer.currentToken().type == TokComma) |
| goto doParseObjectStartExpression; |
| if (m_lexer.currentToken().type != TokRBrace) { |
| m_parseErrorMessage = ASCIILiteral("Expected '}'"); |
| return JSValue(); |
| } |
| m_lexer.next(); |
| lastValue = objectStack.last(); |
| objectStack.removeLast(); |
| break; |
| } |
| startParseExpression: |
| case StartParseExpression: { |
| switch (m_lexer.currentToken().type) { |
| case TokLBracket: |
| goto startParseArray; |
| case TokLBrace: |
| goto startParseObject; |
| case TokString: { |
| LiteralParserToken<CharType> stringToken = m_lexer.currentToken(); |
| m_lexer.next(); |
| if (stringToken.stringIs8Bit) |
| lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken8, stringToken.stringLength).string()); |
| else |
| lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken16, stringToken.stringLength).string()); |
| break; |
| } |
| case TokNumber: { |
| LiteralParserToken<CharType> numberToken = m_lexer.currentToken(); |
| m_lexer.next(); |
| lastValue = jsNumber(numberToken.numberToken); |
| break; |
| } |
| case TokNull: |
| m_lexer.next(); |
| lastValue = jsNull(); |
| break; |
| |
| case TokTrue: |
| m_lexer.next(); |
| lastValue = jsBoolean(true); |
| break; |
| |
| case TokFalse: |
| m_lexer.next(); |
| lastValue = jsBoolean(false); |
| break; |
| case TokRBracket: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ']'"); |
| return JSValue(); |
| case TokRBrace: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '}'"); |
| return JSValue(); |
| case TokIdentifier: { |
| const LiteralParserToken<CharType>& token = m_lexer.currentToken(); |
| if (token.stringIs8Bit) |
| m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", String(m_lexer.currentToken().stringToken8, m_lexer.currentToken().stringLength).ascii().data()).impl(); |
| else |
| m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", String(m_lexer.currentToken().stringToken16, m_lexer.currentToken().stringLength).ascii().data()).impl(); |
| return JSValue(); |
| } |
| case TokColon: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ':'"); |
| return JSValue(); |
| case TokLParen: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '('"); |
| return JSValue(); |
| case TokRParen: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ')'"); |
| return JSValue(); |
| case TokComma: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ','"); |
| return JSValue(); |
| case TokDot: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '.'"); |
| return JSValue(); |
| case TokAssign: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '='"); |
| return JSValue(); |
| case TokSemi: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ';'"); |
| return JSValue(); |
| case TokEnd: |
| m_parseErrorMessage = ASCIILiteral("Unexpected EOF"); |
| return JSValue(); |
| case TokError: |
| default: |
| // Error |
| m_parseErrorMessage = ASCIILiteral("Could not parse value expression"); |
| return JSValue(); |
| } |
| break; |
| } |
| case StartParseStatement: { |
| switch (m_lexer.currentToken().type) { |
| case TokLBracket: |
| case TokNumber: |
| case TokString: |
| goto startParseExpression; |
| |
| case TokLParen: { |
| m_lexer.next(); |
| stateStack.append(StartParseStatementEndStatement); |
| goto startParseExpression; |
| } |
| case TokRBracket: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ']'"); |
| return JSValue(); |
| case TokLBrace: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '{'"); |
| return JSValue(); |
| case TokRBrace: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '}'"); |
| return JSValue(); |
| case TokIdentifier: |
| m_parseErrorMessage = ASCIILiteral("Unexpected identifier"); |
| return JSValue(); |
| case TokColon: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ':'"); |
| return JSValue(); |
| case TokRParen: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ')'"); |
| return JSValue(); |
| case TokComma: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ','"); |
| return JSValue(); |
| case TokTrue: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token 'true'"); |
| return JSValue(); |
| case TokFalse: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token 'false'"); |
| return JSValue(); |
| case TokNull: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token 'null'"); |
| return JSValue(); |
| case TokEnd: |
| m_parseErrorMessage = ASCIILiteral("Unexpected EOF"); |
| return JSValue(); |
| case TokDot: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '.'"); |
| return JSValue(); |
| case TokAssign: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token '='"); |
| return JSValue(); |
| case TokSemi: |
| m_parseErrorMessage = ASCIILiteral("Unexpected token ';'"); |
| return JSValue(); |
| case TokError: |
| default: |
| m_parseErrorMessage = ASCIILiteral("Could not parse statement"); |
| return JSValue(); |
| } |
| } |
| case StartParseStatementEndStatement: { |
| ASSERT(stateStack.isEmpty()); |
| if (m_lexer.currentToken().type != TokRParen) |
| return JSValue(); |
| if (m_lexer.next() == TokEnd) |
| return lastValue; |
| m_parseErrorMessage = ASCIILiteral("Unexpected content at end of JSON literal"); |
| return JSValue(); |
| } |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| if (stateStack.isEmpty()) |
| return lastValue; |
| state = stateStack.last(); |
| stateStack.removeLast(); |
| continue; |
| } |
| } |
| |
| // Instantiate the two flavors of LiteralParser we need instead of putting most of this file in LiteralParser.h |
| template class LiteralParser<LChar>; |
| template class LiteralParser<UChar>; |
| |
| } |