Ignore:
Timestamp:
Apr 5, 2019, 2:58:32 PM (6 years ago)
Author:
[email protected]
Message:

SIGSEGV in JSC::BytecodeGenerator::addStringConstant
https://bugs.webkit.org/show_bug.cgi?id=196486

Reviewed by Saam Barati.

JSTests:

  • stress/arrow-function-and-use-strict-directive.js: Added.
  • stress/arrow-function-syntax.js: Added. Checking EOF token handling.

(checkSyntax):
(checkSyntaxError): Currently not using it. But it is useful for testing more things related to arrow function syntax.

Source/JavaScriptCore:

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.
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.
SyntaxChecker lexes this "}" token, and parser restores the context back to ASTBuilder and continues parsing.

But now, we have ArrowFunctionExpression without braces arrow => expr. Let's consider the following code.

arrow => expr
"string!"

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
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,
generate StringNode with non-built identifier (nullptr), and we accidentally create StringNode with nullptr.

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
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.
We leverage existing SavePoint mechanism to implement lexCurrentTokenAgainUnderCurrentContext cleanly.

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
lexWithoutClearingLineTerminator, which lex the token without clearing line terminator status.

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createString):

  • parser/Lexer.cpp:

(JSC::Lexer<T>::parseMultilineComment):
(JSC::Lexer<T>::lexWithoutClearingLineTerminator): EOF token also should record offset information. This offset information is correctly handled in Lexer::setOffset too.
(JSC::Lexer<T>::lex): Deleted.

  • parser/Lexer.h:

(JSC::Lexer::hasLineTerminatorBeforeToken const):
(JSC::Lexer::setHasLineTerminatorBeforeToken):
(JSC::Lexer<T>::lex):
(JSC::Lexer::prevTerminator const): Deleted.
(JSC::Lexer::setTerminator): Deleted.

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::allowAutomaticSemicolon):
(JSC::Parser<LexerType>::parseSingleFunction):
(JSC::Parser<LexerType>::parseStatementListItem):
(JSC::Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseExportDeclaration):
(JSC::Parser<LexerType>::parseAssignmentExpression):
(JSC::Parser<LexerType>::parseYieldExpression):
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parsePrimaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression):

  • parser/Parser.h:

(JSC::Parser::nextWithoutClearingLineTerminator):
(JSC::Parser::lexCurrentTokenAgainUnderCurrentContext):
(JSC::Parser::internalSaveLexerState):
(JSC::Parser::restoreLexerState):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/Lexer.cpp

    r241751 r243948  
    16921692        if (isLineTerminator(m_current)) {
    16931693            shiftLineTerminator();
    1694             m_terminator = true;
     1694            m_hasLineTerminatorBeforeToken = true;
    16951695        } else
    16961696            shift();
     
    17711771
    17721772template <typename T>
    1773 JSTokenType Lexer<T>::lex(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode)
     1773JSTokenType Lexer<T>::lexWithoutClearingLineTerminator(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode)
    17741774{
    17751775    JSTokenData* tokenData = &tokenRecord->m_data;
     
    17821782
    17831783    JSTokenType token = ERRORTOK;
    1784     m_terminator = false;
    17851784
    17861785start:
    17871786    skipWhitespace();
    17881787
    1789     if (atEnd())
    1790         return EOFTOK;
    1791    
    17921788    tokenLocation->startOffset = currentOffset();
    17931789    ASSERT(currentOffset() >= currentLineStartOffset());
    17941790    tokenRecord->m_startPosition = currentPosition();
     1791
     1792    if (atEnd()) {
     1793        token = EOFTOK;
     1794        goto returnToken;
     1795    }
    17951796
    17961797    CharacterType type;
     
    19031904        if (m_current == '+') {
    19041905            shift();
    1905             token = (!m_terminator) ? PLUSPLUS : AUTOPLUSPLUS;
     1906            token = (!m_hasLineTerminatorBeforeToken) ? PLUSPLUS : AUTOPLUSPLUS;
    19061907            break;
    19071908        }
     
    19171918        if (m_current == '-') {
    19181919            shift();
    1919             if ((m_atLineStart || m_terminator) && m_current == '>') {
     1920            if ((m_atLineStart || m_hasLineTerminatorBeforeToken) && m_current == '>') {
    19201921                if (m_scriptMode == JSParserScriptMode::Classic) {
    19211922                    shift();
     
    19231924                }
    19241925            }
    1925             token = (!m_terminator) ? MINUSMINUS : AUTOMINUSMINUS;
     1926            token = (!m_hasLineTerminatorBeforeToken) ? MINUSMINUS : AUTOMINUSMINUS;
    19261927            break;
    19271928        }
     
    22942295        shiftLineTerminator();
    22952296        m_atLineStart = true;
    2296         m_terminator = true;
     2297        m_hasLineTerminatorBeforeToken = true;
    22972298        m_lineStart = m_code;
    22982299        goto start;
     
    23342335
    23352336        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            }
    23382342            shift();
    23392343        }
    23402344        shiftLineTerminator();
    23412345        m_atLineStart = true;
    2342         m_terminator = true;
     2346        m_hasLineTerminatorBeforeToken = true;
    23432347        m_lineStart = m_code;
    23442348        if (!lastTokenWasRestrKeyword())
Note: See TracChangeset for help on using the changeset viewer.