Changeset 89219 in webkit
- Timestamp:
- Jun 19, 2011 12:47:45 PM (13 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/JSValueRef.cpp
r89192 r89219 235 235 ExecState* exec = toJS(ctx); 236 236 APIEntryShim entryShim(exec); 237 LiteralParser parser(exec, string->ustring(), LiteralParser::StrictJSON); 237 UString str = string->ustring(); 238 LiteralParser parser(exec, str.characters(), str.length(), LiteralParser::StrictJSON); 238 239 return toRef(exec, parser.tryLiteralParse()); 239 240 } -
trunk/Source/JavaScriptCore/ChangeLog
r89192 r89219 1 2011-06-17 Oliver Hunt <oliver@apple.com> 2 3 Reviewed by Gavin Barraclough. 4 5 JSONP is unnecessarily slow 6 https://bugs.webkit.org/show_bug.cgi?id=62920 7 8 JSONP has unfortunately become a fairly common idiom online, yet 9 it triggers very poor performance in JSC as we end up doing codegen 10 for a large number of property accesses that will 11 * only be run once, so the vast amount of logic we dump to handle 12 caching of accesses is unnecessary. 13 * We are doing codegen that is directly proportional to just 14 creating the object in the first place. 15 16 This patch extends the use of the literal parser to JSONP-like structures 17 in global code, handling a number of different forms I have seen online. 18 In an extreme case this improves performance of JSONP by more than 2x 19 due to removal of code generation and execution time, and a few optimisations 20 that I made to the parser itself. 21 22 * API/JSValueRef.cpp: 23 (JSValueMakeFromJSONString): 24 * interpreter/Interpreter.cpp: 25 (JSC::Interpreter::callEval): 26 (JSC::Interpreter::execute): 27 * parser/Lexer.cpp: 28 (JSC::Lexer::isKeyword): 29 * parser/Lexer.h: 30 * runtime/JSGlobalObjectFunctions.cpp: 31 (JSC::globalFuncEval): 32 * runtime/JSONObject.cpp: 33 (JSC::JSONProtoFuncParse): 34 * runtime/LiteralParser.cpp: 35 (JSC::LiteralParser::tryJSONPParse): 36 (JSC::LiteralParser::makeIdentifier): 37 (JSC::LiteralParser::Lexer::lex): 38 (JSC::LiteralParser::Lexer::next): 39 (JSC::isSafeStringCharacter): 40 (JSC::LiteralParser::Lexer::lexString): 41 (JSC::LiteralParser::Lexer::lexNumber): 42 (JSC::LiteralParser::parse): 43 * runtime/LiteralParser.h: 44 (JSC::LiteralParser::LiteralParser): 45 (JSC::LiteralParser::tryLiteralParse): 46 (JSC::LiteralParser::Lexer::Lexer): 47 1 48 2011-06-18 Sheriff Bot <webkit.review.bot@gmail.com> 2 49 -
trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp
r89192 r89219 397 397 // FIXME: We can use the preparser in strict mode, we just need additional logic 398 398 // to prevent duplicates. 399 LiteralParser preparser(callFrame, programSource , LiteralParser::NonStrictJSON);399 LiteralParser preparser(callFrame, programSource.characters(), programSource.length(), LiteralParser::NonStrictJSON); 400 400 if (JSValue parsedObject = preparser.tryLiteralParse()) 401 401 return parsedObject; … … 745 745 746 746 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 } 747 815 748 816 JSObject* error = program->compile(callFrame, scopeChain); -
trunk/Source/JavaScriptCore/parser/Lexer.cpp
r89192 r89219 482 482 } 483 483 484 bool Lexer::isKeyword(const Identifier& ident) 485 { 486 return m_keywordTable.entry(m_globalData, ident); 487 } 488 484 489 template <bool shouldBuildStrings> ALWAYS_INLINE bool Lexer::parseString(JSTokenData* tokenData, bool strictMode) 485 490 { -
trunk/Source/JavaScriptCore/parser/Lexer.h
r89192 r89219 91 91 92 92 JSTokenType lexExpectIdentifier(JSTokenData*, JSTokenInfo*, unsigned, bool strictMode); 93 93 94 bool isKeyword(const Identifier&); 95 94 96 private: 95 97 friend class JSGlobalData; -
trunk/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
r89192 r89219 446 446 UString s = x.toString(exec); 447 447 448 LiteralParser preparser(exec, s , LiteralParser::NonStrictJSON);448 LiteralParser preparser(exec, s.characters(), s.length(), LiteralParser::NonStrictJSON); 449 449 if (JSValue parsedObject = preparser.tryLiteralParse()) 450 450 return JSValue::encode(parsedObject); -
trunk/Source/JavaScriptCore/runtime/JSONObject.cpp
r89192 r89219 817 817 818 818 LocalScope scope(exec->globalData()); 819 LiteralParser jsonParser(exec, source , LiteralParser::StrictJSON);819 LiteralParser jsonParser(exec, source.characters(), source.length(), LiteralParser::StrictJSON); 820 820 JSValue unfiltered = jsonParser.tryLiteralParse(); 821 821 if (!unfiltered) -
trunk/Source/JavaScriptCore/runtime/LiteralParser.cpp
r89192 r89219 43 43 } 44 44 45 LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) 45 bool 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 109 ALWAYS_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 128 template <LiteralParser::ParserMode mode> LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) 46 129 { 47 130 while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr)) … … 90 173 return TokColon; 91 174 case '"': 92 if (m_mode == StrictJSON) 93 return lexString<StrictJSON>(token); 94 return lexString<NonStrictJSON>(token); 175 return lexString<mode, '"'>(token); 95 176 case 't': 96 177 if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') { … … 116 197 return TokNull; 117 198 } 118 break; 199 break; 119 200 case '-': 120 201 case '0': … … 130 211 return lexNumber(token); 131 212 } 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 } 132 244 return TokError; 133 245 } 134 246 135 template <LiteralParser::ParserMode mode> static inline bool isSafeStringCharacter(UChar c) 136 { 137 return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != '"') || c == '\t'; 247 LiteralParser::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 256 template <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'; 138 259 } 139 260 140 261 // "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)262 template <LiteralParser::ParserMode mode, UChar terminator> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token) 142 263 { 143 264 ++m_ptr; 144 const UChar* runStart ;265 const UChar* runStart = m_ptr; 145 266 UStringBuilder builder; 146 267 do { 147 268 runStart = m_ptr; 148 while (m_ptr < m_end && isSafeStringCharacter<mode >(*m_ptr))269 while (m_ptr < m_end && isSafeStringCharacter<mode, terminator>(*m_ptr)) 149 270 ++m_ptr; 150 if ( runStart < m_ptr)271 if (builder.length()) 151 272 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); 153 276 ++m_ptr; 154 277 if (m_ptr >= m_end) … … 200 323 201 324 default: 325 if (*m_ptr == '\'' && mode != StrictJSON) { 326 builder.append('\''); 327 m_ptr++; 328 break; 329 } 202 330 return TokError; 203 331 } 204 332 } 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) 208 336 return TokError; 209 337 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 } 211 347 token.type = TokString; 212 348 token.end = ++m_ptr; … … 254 390 while (m_ptr < m_end && isASCIIDigit(*m_ptr)) 255 391 ++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; 256 408 } 257 409 … … 338 490 339 491 TokenType type = m_lexer.next(); 340 if (type == TokString ) {492 if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) { 341 493 Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); 342 494 … … 346 498 347 499 m_lexer.next(); 348 identifierStack.append( Identifier(m_exec, identifierToken.stringToken));500 identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength)); 349 501 stateStack.append(DoParseObjectEndExpression); 350 502 goto startParseExpression; 351 } else if (type != TokRBrace) 503 } 504 if (type != TokRBrace) 352 505 return JSValue(); 353 506 m_lexer.next(); … … 359 512 case DoParseObjectStartExpression: { 360 513 TokenType type = m_lexer.next(); 361 if (type != TokString )514 if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier)) 362 515 return JSValue(); 363 516 Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); … … 368 521 369 522 m_lexer.next(); 370 identifierStack.append( Identifier(m_exec, identifierToken.stringToken));523 identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength)); 371 524 stateStack.append(DoParseObjectEndExpression); 372 525 goto startParseExpression; … … 395 548 Lexer::LiteralParserToken stringToken = m_lexer.currentToken(); 396 549 m_lexer.next(); 397 lastValue = jsString(m_exec, stringToken.stringToken);550 lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken, stringToken.stringLength).ustring()); 398 551 break; 399 552 } -
trunk/Source/JavaScriptCore/runtime/LiteralParser.h
r89192 r89219 27 27 #define LiteralParser_h 28 28 29 #include "Identifier.h" 29 30 #include "JSGlobalObjectFunctions.h" 30 31 #include "JSValue.h" … … 35 36 class LiteralParser { 36 37 public: 37 typedef enum { StrictJSON, NonStrictJSON } ParserMode;38 LiteralParser(ExecState* exec, const U String& s, ParserMode mode)38 typedef enum { StrictJSON, NonStrictJSON, JSONP } ParserMode; 39 LiteralParser(ExecState* exec, const UChar* characters, unsigned length, ParserMode mode) 39 40 : m_exec(exec) 40 , m_lexer( s, mode)41 , m_lexer(characters, length, mode) 41 42 , m_mode(mode) 42 43 { … … 47 48 m_lexer.next(); 48 49 JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement); 50 if (m_lexer.currentToken().type == TokSemi) 51 m_lexer.next(); 49 52 if (m_lexer.currentToken().type != TokEnd) 50 53 return JSValue(); 51 54 return result; 52 55 } 56 57 enum JSONPPathEntryType { 58 JSONPPathEntryTypeDeclare, // var pathEntryName = JSON 59 JSONPPathEntryTypeDot, // <prior entries>.pathEntryName = JSON 60 JSONPPathEntryTypeLookup // <prior entries>[pathIndex] = JSON 61 }; 62 63 struct JSONPPathEntry { 64 JSONPPathEntryType m_type; 65 Identifier m_pathEntryName; 66 int m_pathIndex; 67 }; 68 69 struct JSONPData { 70 Vector<JSONPPathEntry> m_path; 71 Strong<Unknown> m_value; 72 }; 73 74 bool tryJSONPParse(Vector<JSONPData>&); 75 53 76 private: 54 77 enum ParserState { StartParseObject, StartParseArray, StartParseExpression, … … 59 82 TokString, TokIdentifier, TokNumber, TokColon, 60 83 TokLParen, TokRParen, TokComma, TokTrue, TokFalse, 61 TokNull, TokEnd, Tok Error };62 84 TokNull, TokEnd, TokDot, TokAssign, TokSemi, TokError }; 85 63 86 class Lexer { 64 87 public: … … 67 90 const UChar* start; 68 91 const UChar* end; 69 UString stringToken; 70 double numberToken; 92 UString stringBuffer; 93 union { 94 double numberToken; 95 struct { 96 const UChar* stringToken; 97 int stringLength; 98 }; 99 }; 71 100 }; 72 Lexer(const UString& s, ParserMode mode) 73 : m_string(s) 74 , m_mode(mode) 75 , m_ptr(s.characters()) 76 , m_end(s.characters() + s.length()) 101 Lexer(const UChar* characters, unsigned length, ParserMode mode) 102 : m_mode(mode) 103 , m_ptr(characters) 104 , m_end(characters + length) 77 105 { 78 106 } 79 107 80 TokenType next() 81 { 82 return lex(m_currentToken); 83 } 108 TokenType next(); 84 109 85 110 const LiteralParserToken& currentToken() … … 89 114 90 115 private: 91 TokenType lex(LiteralParserToken&);92 template <ParserMode mode >TokenType lexString(LiteralParserToken&);93 TokenType lexNumber(LiteralParserToken&);116 template <ParserMode mode> TokenType lex(LiteralParserToken&); 117 template <ParserMode mode, UChar terminator> ALWAYS_INLINE TokenType lexString(LiteralParserToken&); 118 ALWAYS_INLINE TokenType lexNumber(LiteralParserToken&); 94 119 LiteralParserToken m_currentToken; 95 120 UString m_string; … … 105 130 LiteralParser::Lexer m_lexer; 106 131 ParserMode m_mode; 132 static unsigned const MaximumCachableCharacter = 128; 133 FixedArray<Identifier, MaximumCachableCharacter> m_shortIdentifiers; 134 FixedArray<Identifier, MaximumCachableCharacter> m_recentIdentifiers; 135 ALWAYS_INLINE const Identifier makeIdentifier(const UChar* characters, size_t length); 107 136 }; 137 108 138 } 109 139
Note: See TracChangeset
for help on using the changeset viewer.