Ignore:
Timestamp:
Jun 17, 2011, 9:25:57 PM (14 years ago)
Author:
[email protected]
Message:

2011-06-17 Oliver Hunt <[email protected]>

Reviewed by Gavin Barraclough.

JSONP is unnecessarily slow
https://bugs.webkit.org/show_bug.cgi?id=62920

JSONP has unfortunately become a fairly common idiom online, yet
it triggers very poor performance in JSC as we end up doing codegen
for a large number of property accesses that will

  • only be run once, so the vast amount of logic we dump to handle caching of accesses is unnecessary.
  • We are doing codegen that is directly proportional to just creating the object in the first place.

This patch extends the use of the literal parser to JSONP-like structures
in global code, handling a number of different forms I have seen online.
In an extreme case this improves performance of JSONP by more than 2x
due to removal of code generation and execution time, and a few optimisations
that I made to the parser itself.

  • API/JSValueRef.cpp: (JSValueMakeFromJSONString):
  • interpreter/Interpreter.cpp: (JSC::Interpreter::callEval): (JSC::Interpreter::execute):
  • parser/Lexer.cpp: (JSC::Lexer::isKeyword):
  • parser/Lexer.h:
  • runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval):
  • runtime/JSONObject.cpp: (JSC::JSONProtoFuncParse):
  • runtime/LiteralParser.cpp: (JSC::LiteralParser::tryJSONPParse): (JSC::LiteralParser::makeIdentifier): (JSC::LiteralParser::Lexer::lex): (JSC::LiteralParser::Lexer::next): (JSC::isSafeStringCharacter): (JSC::LiteralParser::Lexer::lexString): (JSC::LiteralParser::Lexer::lexNumber): (JSC::LiteralParser::parse):
  • runtime/LiteralParser.h: (JSC::LiteralParser::LiteralParser): (JSC::LiteralParser::tryLiteralParse): (JSC::LiteralParser::Lexer::Lexer):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/LiteralParser.cpp

    r77151 r89184  
    4343}
    4444
    45 LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
     45bool LiteralParser::tryJSONPParse(Vector<JSONPData>& results)
     46{
     47    if (m_lexer.next() != TokIdentifier)
     48        return false;
     49    do {
     50        Vector<JSONPPathEntry> path;
     51        // Unguarded next to start off the lexer
     52        Identifier name = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
     53        JSONPPathEntry entry;
     54        if (name == m_exec->globalData().propertyNames->varKeyword) {
     55            if (m_lexer.next() != TokIdentifier)
     56                return false;
     57            entry.m_type = JSONPPathEntryTypeDeclare;
     58            entry.m_pathEntryName = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
     59            path.append(entry);
     60        } else {
     61            entry.m_type = JSONPPathEntryTypeDot;
     62            entry.m_pathEntryName = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
     63            path.append(entry);
     64        }
     65        if (m_exec->globalData().lexer->isKeyword(entry.m_pathEntryName))
     66            return false;
     67        TokenType tokenType = m_lexer.next();
     68        while (tokenType != TokAssign) {
     69            switch (tokenType) {
     70            case TokLBracket: {
     71                entry.m_type = JSONPPathEntryTypeLookup;
     72                if (m_lexer.next() != TokNumber)
     73                    return false;
     74                double doubleIndex = m_lexer.currentToken().numberToken;
     75                int index = (int)doubleIndex;
     76                if (index != doubleIndex || index < 0)
     77                    return false;
     78                entry.m_pathIndex = index;
     79                if (m_lexer.next() != TokRBracket)
     80                    return false;
     81                break;
     82            }
     83            case TokDot: {
     84                entry.m_type = JSONPPathEntryTypeDot;
     85                if (m_lexer.next() != TokIdentifier)
     86                    return false;
     87                entry.m_pathEntryName = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
     88                break;
     89            }
     90            default:
     91                return false;
     92            }
     93            path.append(entry);
     94            tokenType = m_lexer.next();
     95        }
     96        m_lexer.next();
     97        results.append(JSONPData());
     98        results.last().m_value.set(m_exec->globalData(), parse(StartParseExpression));
     99        if (!results.last().m_value)
     100            return false;
     101        results.last().m_path.swap(path);
     102        if (m_lexer.currentToken().type != TokSemi)
     103            break;
     104        m_lexer.next();
     105    } while (m_lexer.currentToken().type == TokIdentifier);
     106    return m_lexer.currentToken().type == TokEnd;
     107}
     108   
     109ALWAYS_INLINE const Identifier LiteralParser::makeIdentifier(const UChar* characters, size_t length)
     110{
     111    if (!length)
     112        return m_exec->globalData().propertyNames->emptyIdentifier;
     113    if (characters[0] >= MaximumCachableCharacter)
     114        return Identifier(&m_exec->globalData(), characters, length);
     115
     116    if (length == 1) {
     117        if (!m_shortIdentifiers[characters[0]].isNull())
     118            return m_shortIdentifiers[characters[0]];
     119        m_shortIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length);
     120        return m_shortIdentifiers[characters[0]];
     121    }
     122    if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length))
     123        return m_recentIdentifiers[characters[0]];
     124    m_recentIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length);
     125    return m_recentIdentifiers[characters[0]];
     126}
     127
     128template <LiteralParser::ParserMode mode> LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
    46129{
    47130    while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr))
     
    90173            return TokColon;
    91174        case '"':
    92             if (m_mode == StrictJSON)
    93                 return lexString<StrictJSON>(token);
    94             return lexString<NonStrictJSON>(token);
     175            return lexString<mode, '"'>(token);
    95176        case 't':
    96177            if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') {
     
    116197                return TokNull;
    117198            }
    118             break;   
     199            break;
    119200        case '-':
    120201        case '0':
     
    130211            return lexNumber(token);
    131212    }
     213    if (m_ptr < m_end) {
     214        if (*m_ptr == '.') {
     215            token.type = TokDot;
     216            token.end = ++m_ptr;
     217            return TokDot;
     218        }
     219        if (*m_ptr == '=') {
     220            token.type = TokAssign;
     221            token.end = ++m_ptr;
     222            return TokAssign;
     223        }
     224        if (*m_ptr == ';') {
     225            token.type = TokSemi;
     226            token.end = ++m_ptr;
     227            return TokAssign;
     228        }
     229        if (isASCIIAlpha(*m_ptr) || *m_ptr == '_' || *m_ptr == '$') {
     230            while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$'))
     231                m_ptr++;
     232            token.stringToken = token.start;
     233            token.stringLength = m_ptr - token.start;
     234            token.type = TokIdentifier;
     235            token.end = m_ptr;
     236            return TokIdentifier;
     237        }
     238        if (*m_ptr == '\'') {
     239            if (mode == StrictJSON)
     240                return TokError;
     241            return lexString<mode, '\''>(token);
     242        }
     243    }
    132244    return TokError;
    133245}
    134246
    135 template <LiteralParser::ParserMode mode> static inline bool isSafeStringCharacter(UChar c)
    136 {
    137     return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != '"') || c == '\t';
     247LiteralParser::TokenType LiteralParser::Lexer::next()
     248{
     249    if (m_mode == NonStrictJSON)
     250        return lex<NonStrictJSON>(m_currentToken);
     251    if (m_mode == JSONP)
     252        return lex<JSONP>(m_currentToken);
     253    return lex<StrictJSON>(m_currentToken);
     254}
     255
     256template <LiteralParser::ParserMode mode, UChar terminator> static inline bool isSafeStringCharacter(UChar c)
     257{
     258    return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != terminator) || c == '\t';
    138259}
    139260
    140261// "inline" is required here to help WINSCW compiler resolve specialized argument in templated functions.
    141 template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token)
     262template <LiteralParser::ParserMode mode, UChar terminator> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token)
    142263{
    143264    ++m_ptr;
    144     const UChar* runStart;
     265    const UChar* runStart = m_ptr;
    145266    UStringBuilder builder;
    146267    do {
    147268        runStart = m_ptr;
    148         while (m_ptr < m_end && isSafeStringCharacter<mode>(*m_ptr))
     269        while (m_ptr < m_end && isSafeStringCharacter<mode, terminator>(*m_ptr))
    149270            ++m_ptr;
    150         if (runStart < m_ptr)
     271        if (builder.length())
    151272            builder.append(runStart, m_ptr - runStart);
    152         if ((mode == StrictJSON) && m_ptr < m_end && *m_ptr == '\\') {
     273        if ((mode != NonStrictJSON) && m_ptr < m_end && *m_ptr == '\\') {
     274            if (builder.isEmpty() && runStart < m_ptr)
     275                builder.append(runStart, m_ptr - runStart);
    153276            ++m_ptr;
    154277            if (m_ptr >= m_end)
     
    200323
    201324                default:
     325                    if (*m_ptr == '\'' && mode != StrictJSON) {
     326                        builder.append('\'');
     327                        m_ptr++;
     328                        break;
     329                    }
    202330                    return TokError;
    203331            }
    204332        }
    205     } while ((mode == StrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != '"');
    206 
    207     if (m_ptr >= m_end || *m_ptr != '"')
     333    } while ((mode != NonStrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != terminator);
     334
     335    if (m_ptr >= m_end || *m_ptr != terminator)
    208336        return TokError;
    209337
    210     token.stringToken = builder.toUString();
     338    if (builder.isEmpty()) {
     339        token.stringBuffer = UString();
     340        token.stringToken = runStart;
     341        token.stringLength = m_ptr - runStart;
     342    } else {
     343        token.stringBuffer = builder.toUString();
     344        token.stringToken = token.stringBuffer.characters();
     345        token.stringLength = token.stringBuffer.length();
     346    }
    211347    token.type = TokString;
    212348    token.end = ++m_ptr;
     
    254390        while (m_ptr < m_end && isASCIIDigit(*m_ptr))
    255391            ++m_ptr;
     392    } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) < 10) {
     393        int result = 0;
     394        token.type = TokNumber;
     395        token.end = m_ptr;
     396        const UChar* digit = token.start;
     397        int negative = 1;
     398        if (*digit == '-') {
     399            negative = -1;
     400            digit++;
     401        }
     402       
     403        while (digit < m_ptr)
     404            result = result * 10 + (*digit++) - '0';
     405        result *= negative;
     406        token.numberToken = result;
     407        return TokNumber;
    256408    }
    257409
     
    338490
    339491                TokenType type = m_lexer.next();
    340                 if (type == TokString) {
     492                if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) {
    341493                    Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
    342494
     
    346498                   
    347499                    m_lexer.next();
    348                     identifierStack.append(Identifier(m_exec, identifierToken.stringToken));
     500                    identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength));
    349501                    stateStack.append(DoParseObjectEndExpression);
    350502                    goto startParseExpression;
    351                 } else if (type != TokRBrace)
     503                }
     504                if (type != TokRBrace)
    352505                    return JSValue();
    353506                m_lexer.next();
     
    359512            case DoParseObjectStartExpression: {
    360513                TokenType type = m_lexer.next();
    361                 if (type != TokString)
     514                if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier))
    362515                    return JSValue();
    363516                Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
     
    368521
    369522                m_lexer.next();
    370                 identifierStack.append(Identifier(m_exec, identifierToken.stringToken));
     523                identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength));
    371524                stateStack.append(DoParseObjectEndExpression);
    372525                goto startParseExpression;
     
    395548                        Lexer::LiteralParserToken stringToken = m_lexer.currentToken();
    396549                        m_lexer.next();
    397                         lastValue = jsString(m_exec, stringToken.stringToken);
     550                        lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken, stringToken.stringLength).ustring());
    398551                        break;
    399552                    }
Note: See TracChangeset for help on using the changeset viewer.