Skip to content
Permalink
Browse files

2011-06-17 Oliver Hunt <oliver@apple.com>

        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):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@89219 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information...
oliver@apple.com
oliver@apple.com committed Jun 19, 2011
1 parent 7696550 commit cef0b13251f7a6f2af3861855e54a6f68f7ed805
@@ -234,7 +234,8 @@ JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
{
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
LiteralParser parser(exec, string->ustring(), LiteralParser::StrictJSON);
UString str = string->ustring();
LiteralParser parser(exec, str.characters(), str.length(), LiteralParser::StrictJSON);
return toRef(exec, parser.tryLiteralParse());
}

@@ -1,3 +1,50 @@
2011-06-17 Oliver Hunt <oliver@apple.com>

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):

2011-06-18 Sheriff Bot <webkit.review.bot@gmail.com>

Unreviewed, rolling out r89184.
@@ -396,7 +396,7 @@ NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* r
if (!codeBlock->isStrictMode()) {
// FIXME: We can use the preparser in strict mode, we just need additional logic
// to prevent duplicates.
LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON);
LiteralParser preparser(callFrame, programSource.characters(), programSource.length(), LiteralParser::NonStrictJSON);
if (JSValue parsedObject = preparser.tryLiteralParse())
return parsedObject;
}
@@ -744,6 +744,74 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, S
return checkedReturn(throwStackOverflowError(callFrame));

DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get());
LiteralParser literalParser(callFrame, program->source().data(), program->source().length(), LiteralParser::JSONP);
Vector<LiteralParser::JSONPData> JSONPData;
if (literalParser.tryJSONPParse(JSONPData)) {
JSGlobalObject* globalObject = scopeChain->globalObject.get();
JSValue result;
for (unsigned entry = 0; entry < JSONPData.size(); entry++) {
Vector<LiteralParser::JSONPPathEntry> JSONPPath;
JSONPPath.swap(JSONPData[entry].m_path);
JSValue JSONPValue = JSONPData[entry].m_value.get();
if (JSONPPath.size() == 1 && JSONPPath[0].m_type == LiteralParser::JSONPPathEntryTypeDeclare) {
if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) {
PutPropertySlot slot;
globalObject->put(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot);
} else
globalObject->putWithAttributes(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete);
// var declarations return undefined
result = jsUndefined();
continue;
}
JSValue baseObject(globalObject);
for (unsigned i = 0; i < JSONPPath.size() - 1; i++) {
ASSERT(JSONPPath[i].m_type != LiteralParser::JSONPPathEntryTypeDeclare);
switch (JSONPPath[i].m_type) {
case LiteralParser::JSONPPathEntryTypeDot: {
if (i == 0) {
PropertySlot slot(globalObject);
if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot))
return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName));
baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName);
} else
baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName);
if (callFrame->hadException())
return jsUndefined();
continue;
}
case LiteralParser::JSONPPathEntryTypeLookup: {
baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex);
if (callFrame->hadException())
return jsUndefined();
continue;
}
default:
ASSERT_NOT_REACHED();
return jsUndefined();
}
}
PutPropertySlot slot;
switch (JSONPPath.last().m_type) {
case LiteralParser::JSONPPathEntryTypeDot: {
baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot);
if (callFrame->hadException())
return jsUndefined();
break;
}
case LiteralParser::JSONPPathEntryTypeLookup: {
baseObject.put(callFrame, JSONPPath.last().m_pathIndex, JSONPValue);
if (callFrame->hadException())
return jsUndefined();
break;
}
default:
ASSERT_NOT_REACHED();
return jsUndefined();
}
result = JSONPValue;
}
return result;
}

JSObject* error = program->compile(callFrame, scopeChain);
if (error)
@@ -481,6 +481,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer::parseIde
return IDENT;
}

bool Lexer::isKeyword(const Identifier& ident)
{
return m_keywordTable.entry(m_globalData, ident);
}

template <bool shouldBuildStrings> ALWAYS_INLINE bool Lexer::parseString(JSTokenData* tokenData, bool strictMode)
{
int stringQuoteCharacter = m_current;
@@ -90,7 +90,9 @@ namespace JSC {
SourceProvider* sourceProvider() const { return m_source->provider(); }

JSTokenType lexExpectIdentifier(JSTokenData*, JSTokenInfo*, unsigned, bool strictMode);


bool isKeyword(const Identifier&);

private:
friend class JSGlobalData;

@@ -445,7 +445,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec)

UString s = x.toString(exec);

LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON);
LiteralParser preparser(exec, s.characters(), s.length(), LiteralParser::NonStrictJSON);
if (JSValue parsedObject = preparser.tryLiteralParse())
return JSValue::encode(parsedObject);

@@ -816,7 +816,7 @@ EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec)
return JSValue::encode(jsNull());

LocalScope scope(exec->globalData());
LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
LiteralParser jsonParser(exec, source.characters(), source.length(), LiteralParser::StrictJSON);
JSValue unfiltered = jsonParser.tryLiteralParse();
if (!unfiltered)
return throwVMError(exec, createSyntaxError(exec, "Unable to parse JSON string"));
Oops, something went wrong.

0 comments on commit cef0b13

Please sign in to comment.
You can’t perform that action at this time.