Ignore:
Timestamp:
Jun 19, 2011, 12:47:45 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/interpreter/Interpreter.cpp

    r89192 r89219  
    397397        // FIXME: We can use the preparser in strict mode, we just need additional logic
    398398        // to prevent duplicates.
    399         LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON);
     399        LiteralParser preparser(callFrame, programSource.characters(), programSource.length(), LiteralParser::NonStrictJSON);
    400400        if (JSValue parsedObject = preparser.tryLiteralParse())
    401401            return parsedObject;
     
    745745
    746746    DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get());
     747    LiteralParser literalParser(callFrame, program->source().data(), program->source().length(), LiteralParser::JSONP);
     748    Vector<LiteralParser::JSONPData> JSONPData;
     749    if (literalParser.tryJSONPParse(JSONPData)) {
     750        JSGlobalObject* globalObject = scopeChain->globalObject.get();
     751        JSValue result;
     752        for (unsigned entry = 0; entry < JSONPData.size(); entry++) {
     753            Vector<LiteralParser::JSONPPathEntry> JSONPPath;
     754            JSONPPath.swap(JSONPData[entry].m_path);
     755            JSValue JSONPValue = JSONPData[entry].m_value.get();
     756            if (JSONPPath.size() == 1 && JSONPPath[0].m_type == LiteralParser::JSONPPathEntryTypeDeclare) {
     757                if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) {
     758                    PutPropertySlot slot;
     759                    globalObject->put(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot);
     760                } else
     761                    globalObject->putWithAttributes(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete);
     762                // var declarations return undefined
     763                result = jsUndefined();
     764                continue;
     765            }
     766            JSValue baseObject(globalObject);
     767            for (unsigned i = 0; i < JSONPPath.size() - 1; i++) {
     768                ASSERT(JSONPPath[i].m_type != LiteralParser::JSONPPathEntryTypeDeclare);
     769                switch (JSONPPath[i].m_type) {
     770                case LiteralParser::JSONPPathEntryTypeDot: {
     771                    if (i == 0) {
     772                        PropertySlot slot(globalObject);
     773                        if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot))
     774                            return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName));
     775                        baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName);
     776                    } else
     777                        baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName);
     778                    if (callFrame->hadException())
     779                        return jsUndefined();
     780                    continue;
     781                }
     782                case LiteralParser::JSONPPathEntryTypeLookup: {
     783                    baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex);
     784                    if (callFrame->hadException())
     785                        return jsUndefined();
     786                    continue;
     787                }
     788                default:
     789                    ASSERT_NOT_REACHED();
     790                    return jsUndefined();
     791                }
     792            }
     793            PutPropertySlot slot;
     794            switch (JSONPPath.last().m_type) {
     795            case LiteralParser::JSONPPathEntryTypeDot: {
     796                baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot);
     797                if (callFrame->hadException())
     798                    return jsUndefined();
     799                break;
     800            }
     801            case LiteralParser::JSONPPathEntryTypeLookup: {
     802                baseObject.put(callFrame, JSONPPath.last().m_pathIndex, JSONPValue);
     803                if (callFrame->hadException())
     804                    return jsUndefined();
     805                break;
     806            }
     807            default:
     808                ASSERT_NOT_REACHED();
     809                    return jsUndefined();
     810            }
     811            result = JSONPValue;
     812        }
     813        return result;
     814    }
    747815
    748816    JSObject* error = program->compile(callFrame, scopeChain);
Note: See TracChangeset for help on using the changeset viewer.