Changeset 243948 in webkit
- Timestamp:
- Apr 5, 2019, 2:58:32 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 7 edited
-
JSTests/ChangeLog (modified) (1 diff)
-
JSTests/stress/arrow-function-and-use-strict-directive.js (added)
-
JSTests/stress/arrow-function-syntax.js (added)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/parser/ASTBuilder.h (modified) (1 diff)
-
Source/JavaScriptCore/parser/Lexer.cpp (modified) (8 diffs)
-
Source/JavaScriptCore/parser/Lexer.h (modified) (5 diffs)
-
Source/JavaScriptCore/parser/Parser.cpp (modified) (15 diffs)
-
Source/JavaScriptCore/parser/Parser.h (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r243943 r243948 1 2019-04-05 Yusuke Suzuki <ysuzuki@apple.com> 2 3 SIGSEGV in JSC::BytecodeGenerator::addStringConstant 4 https://bugs.webkit.org/show_bug.cgi?id=196486 5 6 Reviewed by Saam Barati. 7 8 * stress/arrow-function-and-use-strict-directive.js: Added. 9 * stress/arrow-function-syntax.js: Added. Checking EOF token handling. 10 (checkSyntax): 11 (checkSyntaxError): Currently not using it. But it is useful for testing more things related to arrow function syntax. 12 1 13 2019-04-05 Caitlin Potter <caitp@igalia.com> 2 14 -
trunk/Source/JavaScriptCore/ChangeLog
r243943 r243948 1 2019-04-05 Yusuke Suzuki <ysuzuki@apple.com> 2 3 SIGSEGV in JSC::BytecodeGenerator::addStringConstant 4 https://bugs.webkit.org/show_bug.cgi?id=196486 5 6 Reviewed by Saam Barati. 7 8 When parsing a FunctionExpression / FunctionDeclaration etc., we use SyntaxChecker for the body of the function because we do not have any interest on the nodes of the body at that time. 9 The nodes will be parsed with the ASTBuilder when the function itself is parsed for code generation. This works well previously because all the function ends with "}" previously. 10 SyntaxChecker lexes this "}" token, and parser restores the context back to ASTBuilder and continues parsing. 11 12 But now, we have ArrowFunctionExpression without braces `arrow => expr`. Let's consider the following code. 13 14 arrow => expr 15 "string!" 16 17 We parse arrow function's body with SyntaxChecker. At that time, we lex "string!" token under the SyntaxChecker context. But this means that we may not build string content for this token 18 since SyntaxChecker may not have interest on string content itself in certain case. After the parser is back to ASTBuilder, we parse "string!" as ExpressionStatement with string constant, 19 generate StringNode with non-built identifier (nullptr), and we accidentally create StringNode with nullptr. 20 21 This patch fixes this problem. The root cause of this problem is that the last token lexed in the previous context is used. We add lexCurrentTokenAgainUnderCurrentContext which will re-lex 22 the current token under the current context (may be ASTBuilder). This should be done only when the caller's context is different from SyntaxChecker, which avoids unnecessary lexing. 23 We leverage existing SavePoint mechanism to implement lexCurrentTokenAgainUnderCurrentContext cleanly. 24 25 And we also fix the bug in the existing SavePoint mechanism, which is shown in the attached test script. When we save LexerState, we do not save line terminator status. This patch also introduces 26 lexWithoutClearingLineTerminator, which lex the token without clearing line terminator status. 27 28 * parser/ASTBuilder.h: 29 (JSC::ASTBuilder::createString): 30 * parser/Lexer.cpp: 31 (JSC::Lexer<T>::parseMultilineComment): 32 (JSC::Lexer<T>::lexWithoutClearingLineTerminator): EOF token also should record offset information. This offset information is correctly handled in Lexer::setOffset too. 33 (JSC::Lexer<T>::lex): Deleted. 34 * parser/Lexer.h: 35 (JSC::Lexer::hasLineTerminatorBeforeToken const): 36 (JSC::Lexer::setHasLineTerminatorBeforeToken): 37 (JSC::Lexer<T>::lex): 38 (JSC::Lexer::prevTerminator const): Deleted. 39 (JSC::Lexer::setTerminator): Deleted. 40 * parser/Parser.cpp: 41 (JSC::Parser<LexerType>::allowAutomaticSemicolon): 42 (JSC::Parser<LexerType>::parseSingleFunction): 43 (JSC::Parser<LexerType>::parseStatementListItem): 44 (JSC::Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement): 45 (JSC::Parser<LexerType>::parseFunctionInfo): 46 (JSC::Parser<LexerType>::parseClass): 47 (JSC::Parser<LexerType>::parseExportDeclaration): 48 (JSC::Parser<LexerType>::parseAssignmentExpression): 49 (JSC::Parser<LexerType>::parseYieldExpression): 50 (JSC::Parser<LexerType>::parseProperty): 51 (JSC::Parser<LexerType>::parsePrimaryExpression): 52 (JSC::Parser<LexerType>::parseMemberExpression): 53 * parser/Parser.h: 54 (JSC::Parser::nextWithoutClearingLineTerminator): 55 (JSC::Parser::lexCurrentTokenAgainUnderCurrentContext): 56 (JSC::Parser::internalSaveLexerState): 57 (JSC::Parser::restoreLexerState): 58 1 59 2019-04-05 Caitlin Potter <caitp@igalia.com> 2 60 -
trunk/Source/JavaScriptCore/parser/ASTBuilder.h
r237241 r243948 242 242 ExpressionNode* createString(const JSTokenLocation& location, const Identifier* string) 243 243 { 244 ASSERT(string); 244 245 incConstants(); 245 246 return new (m_parserArena) StringNode(location, *string); -
trunk/Source/JavaScriptCore/parser/Lexer.cpp
r241751 r243948 1692 1692 if (isLineTerminator(m_current)) { 1693 1693 shiftLineTerminator(); 1694 m_ terminator= true;1694 m_hasLineTerminatorBeforeToken = true; 1695 1695 } else 1696 1696 shift(); … … 1771 1771 1772 1772 template <typename T> 1773 JSTokenType Lexer<T>::lex (JSToken* tokenRecord, unsigned lexerFlags, bool strictMode)1773 JSTokenType Lexer<T>::lexWithoutClearingLineTerminator(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode) 1774 1774 { 1775 1775 JSTokenData* tokenData = &tokenRecord->m_data; … … 1782 1782 1783 1783 JSTokenType token = ERRORTOK; 1784 m_terminator = false;1785 1784 1786 1785 start: 1787 1786 skipWhitespace(); 1788 1787 1789 if (atEnd())1790 return EOFTOK;1791 1792 1788 tokenLocation->startOffset = currentOffset(); 1793 1789 ASSERT(currentOffset() >= currentLineStartOffset()); 1794 1790 tokenRecord->m_startPosition = currentPosition(); 1791 1792 if (atEnd()) { 1793 token = EOFTOK; 1794 goto returnToken; 1795 } 1795 1796 1796 1797 CharacterType type; … … 1903 1904 if (m_current == '+') { 1904 1905 shift(); 1905 token = (!m_ terminator) ? PLUSPLUS : AUTOPLUSPLUS;1906 token = (!m_hasLineTerminatorBeforeToken) ? PLUSPLUS : AUTOPLUSPLUS; 1906 1907 break; 1907 1908 } … … 1917 1918 if (m_current == '-') { 1918 1919 shift(); 1919 if ((m_atLineStart || m_ terminator) && m_current == '>') {1920 if ((m_atLineStart || m_hasLineTerminatorBeforeToken) && m_current == '>') { 1920 1921 if (m_scriptMode == JSParserScriptMode::Classic) { 1921 1922 shift(); … … 1923 1924 } 1924 1925 } 1925 token = (!m_ terminator) ? MINUSMINUS : AUTOMINUSMINUS;1926 token = (!m_hasLineTerminatorBeforeToken) ? MINUSMINUS : AUTOMINUSMINUS; 1926 1927 break; 1927 1928 } … … 2294 2295 shiftLineTerminator(); 2295 2296 m_atLineStart = true; 2296 m_ terminator= true;2297 m_hasLineTerminatorBeforeToken = true; 2297 2298 m_lineStart = m_code; 2298 2299 goto start; … … 2334 2335 2335 2336 while (!isLineTerminator(m_current)) { 2336 if (atEnd()) 2337 return EOFTOK; 2337 if (atEnd()) { 2338 token = EOFTOK; 2339 fillTokenInfo(tokenRecord, token, lineNumber, endOffset, lineStartOffset, endPosition); 2340 return token; 2341 } 2338 2342 shift(); 2339 2343 } 2340 2344 shiftLineTerminator(); 2341 2345 m_atLineStart = true; 2342 m_ terminator= true;2346 m_hasLineTerminatorBeforeToken = true; 2343 2347 m_lineStart = m_code; 2344 2348 if (!lastTokenWasRestrKeyword()) -
trunk/Source/JavaScriptCore/parser/Lexer.h
r241645 r243948 66 66 67 67 JSTokenType lex(JSToken*, unsigned, bool strictMode); 68 JSTokenType lexWithoutClearingLineTerminator(JSToken*, unsigned, bool strictMode); 68 69 bool nextTokenIsColon(); 69 70 int lineNumber() const { return m_lineNumber; } … … 78 79 void setLastLineNumber(int lastLineNumber) { m_lastLineNumber = lastLineNumber; } 79 80 int lastLineNumber() const { return m_lastLineNumber; } 80 bool prevTerminator() const { return m_terminator; }81 bool hasLineTerminatorBeforeToken() const { return m_hasLineTerminatorBeforeToken; } 81 82 JSTokenType scanRegExp(JSToken*, UChar patternPrefix = 0); 82 83 enum class RawStringsBuildMode { BuildRawStrings, DontBuildRawStrings }; … … 111 112 m_lineNumber = line; 112 113 } 113 void set Terminator(bool terminator)114 void setHasLineTerminatorBeforeToken(bool terminator) 114 115 { 115 m_ terminator= terminator;116 m_hasLineTerminatorBeforeToken = terminator; 116 117 } 117 118 … … 203 204 Vector<UChar> m_buffer16; 204 205 Vector<UChar> m_bufferForRawTemplateString16; 205 bool m_ terminator;206 bool m_hasLineTerminatorBeforeToken; 206 207 int m_lastToken; 207 208 … … 404 405 } 405 406 407 template <typename T> 408 ALWAYS_INLINE JSTokenType Lexer<T>::lex(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode) 409 { 410 m_hasLineTerminatorBeforeToken = false; 411 return lexWithoutClearingLineTerminator(tokenRecord, lexerFlags, strictMode); 412 } 413 406 414 } // namespace JSC -
trunk/Source/JavaScriptCore/parser/Parser.cpp
r242193 r243948 346 346 bool Parser<LexerType>::allowAutomaticSemicolon() 347 347 { 348 return match(CLOSEBRACE) || match(EOFTOK) || m_lexer-> prevTerminator();348 return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->hasLineTerminatorBeforeToken(); 349 349 } 350 350 … … 626 626 if (*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped) { 627 627 next(); 628 failIfFalse(match(FUNCTION) && !m_lexer-> prevTerminator(), "Cannot parse the async function");628 failIfFalse(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken(), "Cannot parse the async function"); 629 629 statement = parseAsyncFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition); 630 630 break; … … 697 697 SavePoint savePoint = createSavePoint(); 698 698 next(); 699 if (UNLIKELY(match(FUNCTION) && !m_lexer-> prevTerminator())) {699 if (UNLIKELY(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken())) { 700 700 result = parseAsyncFunctionDeclaration(context); 701 701 break; … … 2027 2027 SavePoint savePoint = createSavePoint(); 2028 2028 next(); 2029 if (match(FUNCTION) && !m_lexer-> prevTerminator()) {2029 if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) { 2030 2030 const bool isAsync = true; 2031 2031 result = parseFunctionDeclarationStatement(context, isAsync, parentAllowsFunctionDeclarationAsStatement); … … 2422 2422 matchOrFail(ARROWFUNCTION, "Expected a '=>' after arrow function parameter declaration"); 2423 2423 2424 if (m_lexer-> prevTerminator())2424 if (m_lexer->hasLineTerminatorBeforeToken()) 2425 2425 failDueToUnexpectedToken(); 2426 2426 … … 2613 2613 newInfo = SourceProviderCacheItem::create(parameters); 2614 2614 } 2615 2616 bool functionScopeWasStrictMode = functionScope->strictMode(); 2615 2617 2616 2618 popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); … … 2619 2621 matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); 2620 2622 next(); 2623 } else { 2624 // We need to lex the last token again because the last token is lexed under the different context because of the following possibilities. 2625 // 1. which may have different strict mode. 2626 // 2. which may not build strings for tokens. 2627 // But (1) is not possible because we do not recognize the string literal in ArrowFunctionBodyExpression as directive and this is correct in terms of the spec (`value => "use strict"`). 2628 // So we only check TreeBuilder's type here. 2629 ASSERT_UNUSED(functionScopeWasStrictMode, functionScopeWasStrictMode == currentScope()->strictMode()); 2630 if (!std::is_same<TreeBuilder, SyntaxChecker>::value) 2631 lexCurrentTokenAgainUnderCurrentContext(); 2621 2632 } 2622 2633 … … 2877 2888 ident = m_token.m_data.ident; 2878 2889 next(); 2879 if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer-> prevTerminator())2890 if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken()) 2880 2891 break; 2881 2892 if (UNLIKELY(consume(TIMES))) … … 3397 3408 SavePoint savePoint = createSavePoint(); 3398 3409 next(); 3399 if (match(FUNCTION) && !m_lexer-> prevTerminator()) {3410 if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) { 3400 3411 next(); 3401 3412 if (match(IDENT)) … … 3542 3553 if (*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped) { 3543 3554 next(); 3544 semanticFailIfFalse(match(FUNCTION) && !m_lexer-> prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");3555 semanticFailIfFalse(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator"); 3545 3556 DepthManager statementDepth(&m_statementDepth); 3546 3557 m_statementDepth = 1; … … 3660 3671 if (matchContextualKeyword(m_vm->propertyNames->async)) { 3661 3672 next(); 3662 isAsyncArrow = !m_lexer-> prevTerminator();3673 isAsyncArrow = !m_lexer->hasLineTerminatorBeforeToken(); 3663 3674 } 3664 3675 } … … 3777 3788 SavePoint savePoint = createSavePoint(); 3778 3789 next(); 3779 if (m_lexer-> prevTerminator())3790 if (m_lexer->hasLineTerminatorBeforeToken()) 3780 3791 return context.createYield(location); 3781 3792 … … 3937 3948 } 3938 3949 3939 failIfTrue(m_lexer-> prevTerminator(), "Expected a property name following keyword 'async'");3950 failIfTrue(m_lexer->hasLineTerminatorBeforeToken(), "Expected a property name following keyword 'async'"); 3940 3951 if (UNLIKELY(consume(TIMES))) 3941 3952 parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode; … … 4486 4497 JSTokenLocation location(tokenLocation()); 4487 4498 next(); 4488 if (match(FUNCTION) && !m_lexer-> prevTerminator())4499 if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) 4489 4500 return parseAsyncFunctionExpression(context); 4490 4501 … … 4752 4763 base = parsePrimaryExpression(context); 4753 4764 failIfFalse(base, "Cannot parse base expression"); 4754 if (UNLIKELY(isAsync && context.isResolve(base) && !m_lexer-> prevTerminator())) {4765 if (UNLIKELY(isAsync && context.isResolve(base) && !m_lexer->hasLineTerminatorBeforeToken())) { 4755 4766 if (matchSpecIdentifier()) { 4756 4767 // AsyncArrowFunction -
trunk/Source/JavaScriptCore/parser/Parser.h
r241645 r243948 1364 1364 } 1365 1365 1366 ALWAYS_INLINE void nextWithoutClearingLineTerminator(unsigned lexerFlags = 0) 1367 { 1368 int lastLine = m_token.m_location.line; 1369 int lastTokenEnd = m_token.m_location.endOffset; 1370 int lastTokenLineStart = m_token.m_location.lineStartOffset; 1371 m_lastTokenEndPosition = JSTextPosition(lastLine, lastTokenEnd, lastTokenLineStart); 1372 m_lexer->setLastLineNumber(lastLine); 1373 m_token.m_type = m_lexer->lexWithoutClearingLineTerminator(&m_token, lexerFlags, strictMode()); 1374 } 1375 1366 1376 ALWAYS_INLINE void nextExpectIdentifier(unsigned lexerFlags = 0) 1367 1377 { … … 1372 1382 m_lexer->setLastLineNumber(lastLine); 1373 1383 m_token.m_type = m_lexer->lexExpectIdentifier(&m_token, lexerFlags, strictMode()); 1384 } 1385 1386 ALWAYS_INLINE void lexCurrentTokenAgainUnderCurrentContext() 1387 { 1388 auto savePoint = createSavePoint(); 1389 restoreSavePoint(savePoint); 1374 1390 } 1375 1391 … … 1763 1779 unsigned oldLastLineNumber; 1764 1780 unsigned oldLineNumber; 1781 bool hasLineTerminatorBeforeToken; 1765 1782 }; 1766 1783 … … 1776 1793 result.oldLastLineNumber = m_lexer->lastLineNumber(); 1777 1794 result.oldLineNumber = m_lexer->lineNumber(); 1795 result.hasLineTerminatorBeforeToken = m_lexer->hasLineTerminatorBeforeToken(); 1778 1796 ASSERT(static_cast<unsigned>(result.startOffset) >= result.oldLineStartOffset); 1779 1797 return result; … … 1785 1803 m_lexer->setOffset(lexerState.startOffset, lexerState.oldLineStartOffset); 1786 1804 m_lexer->setLineNumber(lexerState.oldLineNumber); 1787 next(); 1805 m_lexer->setHasLineTerminatorBeforeToken(lexerState.hasLineTerminatorBeforeToken); 1806 nextWithoutClearingLineTerminator(); 1788 1807 m_lexer->setLastLineNumber(lexerState.oldLastLineNumber); 1789 1808 }
Note:
See TracChangeset
for help on using the changeset viewer.