Parser needs to restore unary stack state when backtracking
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Jan 2020 19:04:13 +0000 (19:04 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Jan 2020 19:04:13 +0000 (19:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=206972

Reviewed by Saam Barati.

JSTests:

* stress/parser-save-state-remove-stale-entries.js: Added.
* stress/parser-syntax-checker-assignments-are-not-resolve-expressions.js: Added.
(foo):

Source/JavaScriptCore:

Previously we would try to parse possibly stale unary operator
stack entries after backtracking from a parse error.  This would
cause us to think one token was a different token while reparsing
after backtracking. Additionally, this patch fixes an issue where
the syntax checker would think assignment expressions were resolve
expressions. Intrestingly, this was not tested in test262.

Lastly, I tried adding some assertions to improve help diagnose
when our source text locations are incorrect.

* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::emitExpressionInfo):
* bytecompiler/NodesCodegen.cpp:
(JSC::ThisNode::emitBytecode):
(JSC::ResolveNode::emitBytecode):
(JSC::EmptyVarExpression::emitBytecode):
(JSC::EmptyLetExpression::emitBytecode):
(JSC::ForInNode::emitLoopHeader):
(JSC::ForOfNode::emitBytecode):
(JSC::DefineFieldNode::emitBytecode):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::unaryTokenStackDepth const):
(JSC::ASTBuilder::setUnaryTokenStackDepth):
* parser/Lexer.cpp:
(JSC::Lexer<T>::Lexer):
* parser/Lexer.h:
(JSC::Lexer::setLineNumber):
* parser/Nodes.cpp:
(JSC::FunctionMetadataNode::operator== const):
* parser/Nodes.h:
(JSC::ThrowableExpressionData::ThrowableExpressionData):
(JSC::ThrowableExpressionData::setExceptionSourceCode):
(JSC::ThrowableExpressionData::checkConsistency const):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::isArrowFunctionParameters):
(JSC::Parser<LexerType>::parseSourceElements):
(JSC::Parser<LexerType>::parseModuleSourceElements):
(JSC::Parser<LexerType>::parseStatementListItem):
(JSC::Parser<LexerType>::parseAssignmentElement):
(JSC::Parser<LexerType>::parseForStatement):
(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>::parseMemberExpression):
(JSC::Parser<LexerType>::parseUnaryExpression):
* parser/Parser.h:
(JSC::Parser::lexCurrentTokenAgainUnderCurrentContext):
(JSC::Parser::internalSaveParserState):
(JSC::Parser::restoreParserState):
(JSC::Parser::internalSaveState):
(JSC::Parser::swapSavePointForError):
(JSC::Parser::createSavePoint):
(JSC::Parser::internalRestoreState):
(JSC::Parser::restoreSavePointWithError):
(JSC::Parser::restoreSavePoint):
(JSC::Parser::createSavePointForError): Deleted.
* parser/ParserTokens.h:
(JSC::JSTextPosition::JSTextPosition):
(JSC::JSTextPosition::checkConsistency):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::operatorStackPop):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@255440 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
JSTests/ChangeLog
JSTests/stress/parser-save-state-remove-stale-entries.js [new file with mode: 0644]
JSTests/stress/parser-syntax-checker-assignments-are-not-resolve-expressions.js [new file with mode: 0644]
LayoutTests/js/function-toString-parentheses-expected.txt
LayoutTests/js/script-tests/function-toString-parentheses.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/Lexer.h
Source/JavaScriptCore/parser/Nodes.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/parser/SyntaxChecker.h

index 9e24baa..7fce68b 100644 (file)
@@ -1,3 +1,14 @@
+2020-01-30  Keith Miller  <keith_miller@apple.com>
+
+        Parser needs to restore unary stack state when backtracking
+        https://bugs.webkit.org/show_bug.cgi?id=206972
+
+        Reviewed by Saam Barati.
+
+        * stress/parser-save-state-remove-stale-entries.js: Added.
+        * stress/parser-syntax-checker-assignments-are-not-resolve-expressions.js: Added.
+        (foo):
+
 2020-01-29  Mark Lam  <mark.lam@apple.com>
 
         Fix bad assertion in InternalFunctionAllocationProfile::createAllocationStructureFromBase().
diff --git a/JSTests/stress/parser-save-state-remove-stale-entries.js b/JSTests/stress/parser-save-state-remove-stale-entries.js
new file mode 100644 (file)
index 0000000..5ecef4e
--- /dev/null
@@ -0,0 +1,10 @@
+// This test ensures we properly remove unary expression stack entries when backtracing in the parser.
+
+try {
+    eval("delete((a=++(b=b)) => {})");
+} catch { }
+
+try {
+    eval("!(({a=(++((t)=((e))))})=>{})");
+} catch { }
+
diff --git a/JSTests/stress/parser-syntax-checker-assignments-are-not-resolve-expressions.js b/JSTests/stress/parser-syntax-checker-assignments-are-not-resolve-expressions.js
new file mode 100644 (file)
index 0000000..6171e06
--- /dev/null
@@ -0,0 +1,4 @@
+//@ requireOptions("--exception=SyntaxError") && runDefault
+"use strict";
+
+function foo() { (b=b) = b; }
index 8f4a088..4e1f0cb 100644 (file)
@@ -211,7 +211,7 @@ PASS compileAndSerialize('a || b || c') is 'a || b || c'
 PASS compileAndSerialize('(a || b) || c') is '(a || b) || c'
 PASS compileAndSerialize('a || (b || c)') is 'a || (b || c)'
 PASS compileAndSerialize('a = b = c') is 'a = b = c'
-PASS compileAndSerialize('(a = b) = c') is '(a = b) = c'
+PASS compileAndSerialize("(a = b) = c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b = c)') is 'a = (b = c)'
 PASS compileAndSerialize('a = b + c') is 'a = b + c'
 PASS compileAndSerialize('(a = b) + c') is '(a = b) + c'
@@ -220,10 +220,10 @@ PASS compileAndSerialize('a + b = c') threw exception SyntaxError: Left hand sid
 PASS compileAndSerialize('(a + b) = c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b = c)') is 'a + (b = c)'
 PASS compileAndSerialize('a *= b *= c') is 'a *= b *= c'
-PASS compileAndSerialize('(a *= b) *= c') is '(a *= b) *= c'
+PASS compileAndSerialize("(a *= b) *= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a *= (b *= c)') is 'a *= (b *= c)'
 PASS compileAndSerialize('a = b *= c') is 'a = b *= c'
-PASS compileAndSerialize('(a = b) *= c') is '(a = b) *= c'
+PASS compileAndSerialize("(a = b) *= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b *= c)') is 'a = (b *= c)'
 PASS compileAndSerialize('a *= b + c') is 'a *= b + c'
 PASS compileAndSerialize('(a *= b) + c') is '(a *= b) + c'
@@ -232,10 +232,10 @@ PASS compileAndSerialize('a + b *= c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) *= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b *= c)') is 'a + (b *= c)'
 PASS compileAndSerialize('a /= b /= c') is 'a /= b /= c'
-PASS compileAndSerialize('(a /= b) /= c') is '(a /= b) /= c'
+PASS compileAndSerialize("(a /= b) /= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a /= (b /= c)') is 'a /= (b /= c)'
 PASS compileAndSerialize('a = b /= c') is 'a = b /= c'
-PASS compileAndSerialize('(a = b) /= c') is '(a = b) /= c'
+PASS compileAndSerialize("(a = b) /= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b /= c)') is 'a = (b /= c)'
 PASS compileAndSerialize('a /= b + c') is 'a /= b + c'
 PASS compileAndSerialize('(a /= b) + c') is '(a /= b) + c'
@@ -244,10 +244,10 @@ PASS compileAndSerialize('a + b /= c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) /= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b /= c)') is 'a + (b /= c)'
 PASS compileAndSerialize('a %= b %= c') is 'a %= b %= c'
-PASS compileAndSerialize('(a %= b) %= c') is '(a %= b) %= c'
+PASS compileAndSerialize("(a %= b) %= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a %= (b %= c)') is 'a %= (b %= c)'
 PASS compileAndSerialize('a = b %= c') is 'a = b %= c'
-PASS compileAndSerialize('(a = b) %= c') is '(a = b) %= c'
+PASS compileAndSerialize("(a = b) %= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b %= c)') is 'a = (b %= c)'
 PASS compileAndSerialize('a %= b + c') is 'a %= b + c'
 PASS compileAndSerialize('(a %= b) + c') is '(a %= b) + c'
@@ -256,10 +256,10 @@ PASS compileAndSerialize('a + b %= c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) %= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b %= c)') is 'a + (b %= c)'
 PASS compileAndSerialize('a += b += c') is 'a += b += c'
-PASS compileAndSerialize('(a += b) += c') is '(a += b) += c'
+PASS compileAndSerialize("(a += b) += c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a += (b += c)') is 'a += (b += c)'
 PASS compileAndSerialize('a = b += c') is 'a = b += c'
-PASS compileAndSerialize('(a = b) += c') is '(a = b) += c'
+PASS compileAndSerialize("(a = b) += c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b += c)') is 'a = (b += c)'
 PASS compileAndSerialize('a += b + c') is 'a += b + c'
 PASS compileAndSerialize('(a += b) + c') is '(a += b) + c'
@@ -268,10 +268,10 @@ PASS compileAndSerialize('a + b += c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) += c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b += c)') is 'a + (b += c)'
 PASS compileAndSerialize('a -= b -= c') is 'a -= b -= c'
-PASS compileAndSerialize('(a -= b) -= c') is '(a -= b) -= c'
+PASS compileAndSerialize("(a -= b) -= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a -= (b -= c)') is 'a -= (b -= c)'
 PASS compileAndSerialize('a = b -= c') is 'a = b -= c'
-PASS compileAndSerialize('(a = b) -= c') is '(a = b) -= c'
+PASS compileAndSerialize("(a = b) -= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b -= c)') is 'a = (b -= c)'
 PASS compileAndSerialize('a -= b + c') is 'a -= b + c'
 PASS compileAndSerialize('(a -= b) + c') is '(a -= b) + c'
@@ -280,10 +280,10 @@ PASS compileAndSerialize('a + b -= c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) -= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b -= c)') is 'a + (b -= c)'
 PASS compileAndSerialize('a <<= b <<= c') is 'a <<= b <<= c'
-PASS compileAndSerialize('(a <<= b) <<= c') is '(a <<= b) <<= c'
+PASS compileAndSerialize("(a <<= b) <<= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a <<= (b <<= c)') is 'a <<= (b <<= c)'
 PASS compileAndSerialize('a = b <<= c') is 'a = b <<= c'
-PASS compileAndSerialize('(a = b) <<= c') is '(a = b) <<= c'
+PASS compileAndSerialize("(a = b) <<= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b <<= c)') is 'a = (b <<= c)'
 PASS compileAndSerialize('a <<= b + c') is 'a <<= b + c'
 PASS compileAndSerialize('(a <<= b) + c') is '(a <<= b) + c'
@@ -292,10 +292,10 @@ PASS compileAndSerialize('a + b <<= c') threw exception SyntaxError: Left hand s
 PASS compileAndSerialize('(a + b) <<= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b <<= c)') is 'a + (b <<= c)'
 PASS compileAndSerialize('a >>= b >>= c') is 'a >>= b >>= c'
-PASS compileAndSerialize('(a >>= b) >>= c') is '(a >>= b) >>= c'
+PASS compileAndSerialize("(a >>= b) >>= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a >>= (b >>= c)') is 'a >>= (b >>= c)'
 PASS compileAndSerialize('a = b >>= c') is 'a = b >>= c'
-PASS compileAndSerialize('(a = b) >>= c') is '(a = b) >>= c'
+PASS compileAndSerialize("(a = b) >>= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b >>= c)') is 'a = (b >>= c)'
 PASS compileAndSerialize('a >>= b + c') is 'a >>= b + c'
 PASS compileAndSerialize('(a >>= b) + c') is '(a >>= b) + c'
@@ -304,10 +304,10 @@ PASS compileAndSerialize('a + b >>= c') threw exception SyntaxError: Left hand s
 PASS compileAndSerialize('(a + b) >>= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b >>= c)') is 'a + (b >>= c)'
 PASS compileAndSerialize('a >>>= b >>>= c') is 'a >>>= b >>>= c'
-PASS compileAndSerialize('(a >>>= b) >>>= c') is '(a >>>= b) >>>= c'
+PASS compileAndSerialize("(a >>>= b) >>>= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a >>>= (b >>>= c)') is 'a >>>= (b >>>= c)'
 PASS compileAndSerialize('a = b >>>= c') is 'a = b >>>= c'
-PASS compileAndSerialize('(a = b) >>>= c') is '(a = b) >>>= c'
+PASS compileAndSerialize("(a = b) >>>= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b >>>= c)') is 'a = (b >>>= c)'
 PASS compileAndSerialize('a >>>= b + c') is 'a >>>= b + c'
 PASS compileAndSerialize('(a >>>= b) + c') is '(a >>>= b) + c'
@@ -316,10 +316,10 @@ PASS compileAndSerialize('a + b >>>= c') threw exception SyntaxError: Left hand
 PASS compileAndSerialize('(a + b) >>>= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b >>>= c)') is 'a + (b >>>= c)'
 PASS compileAndSerialize('a &= b &= c') is 'a &= b &= c'
-PASS compileAndSerialize('(a &= b) &= c') is '(a &= b) &= c'
+PASS compileAndSerialize("(a &= b) &= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a &= (b &= c)') is 'a &= (b &= c)'
 PASS compileAndSerialize('a = b &= c') is 'a = b &= c'
-PASS compileAndSerialize('(a = b) &= c') is '(a = b) &= c'
+PASS compileAndSerialize("(a = b) &= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b &= c)') is 'a = (b &= c)'
 PASS compileAndSerialize('a &= b + c') is 'a &= b + c'
 PASS compileAndSerialize('(a &= b) + c') is '(a &= b) + c'
@@ -328,10 +328,10 @@ PASS compileAndSerialize('a + b &= c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) &= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b &= c)') is 'a + (b &= c)'
 PASS compileAndSerialize('a ^= b ^= c') is 'a ^= b ^= c'
-PASS compileAndSerialize('(a ^= b) ^= c') is '(a ^= b) ^= c'
+PASS compileAndSerialize("(a ^= b) ^= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a ^= (b ^= c)') is 'a ^= (b ^= c)'
 PASS compileAndSerialize('a = b ^= c') is 'a = b ^= c'
-PASS compileAndSerialize('(a = b) ^= c') is '(a = b) ^= c'
+PASS compileAndSerialize("(a = b) ^= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b ^= c)') is 'a = (b ^= c)'
 PASS compileAndSerialize('a ^= b + c') is 'a ^= b + c'
 PASS compileAndSerialize('(a ^= b) + c') is '(a ^= b) + c'
@@ -340,10 +340,10 @@ PASS compileAndSerialize('a + b ^= c') threw exception SyntaxError: Left hand si
 PASS compileAndSerialize('(a + b) ^= c') threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a + (b ^= c)') is 'a + (b ^= c)'
 PASS compileAndSerialize('a |= b |= c') is 'a |= b |= c'
-PASS compileAndSerialize('(a |= b) |= c') is '(a |= b) |= c'
+PASS compileAndSerialize("(a |= b) |= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a |= (b |= c)') is 'a |= (b |= c)'
 PASS compileAndSerialize('a = b |= c') is 'a = b |= c'
-PASS compileAndSerialize('(a = b) |= c') is '(a = b) |= c'
+PASS compileAndSerialize("(a = b) |= c") threw exception SyntaxError: Left side of assignment is not a reference..
 PASS compileAndSerialize('a = (b |= c)') is 'a = (b |= c)'
 PASS compileAndSerialize('a |= b + c') is 'a |= b + c'
 PASS compileAndSerialize('(a |= b) + c') is '(a |= b) + c'
index 81ce13f..b0fd322 100644 (file)
@@ -47,10 +47,13 @@ function testLeftAssociativeSame(opA, opB)
     testKeepParentheses("a " + opA + " (b " + opB + " c)");
 }
 
-function testRightAssociativeSame(opA, opB)
+function testRightAssociativeSame(opA, opB, leftParensThrows = false)
 {
     testKeepParentheses("a " + opA + " b " + opB + " c");
-    testKeepParentheses("(a " + opA + " b) " + opB + " c");
+    if (leftParensThrows)
+        shouldThrow(`compileAndSerialize("(a ${opA} b) ${opB} c")`);
+    else
+        testKeepParentheses("(a " + opA + " b) " + opB + " c");
     testOptionalParentheses("a " + opA + " (b " + opB + " c)");
 }
 
@@ -99,9 +102,9 @@ var assignmentOperators = [ "=", "*=", "/=" , "%=", "+=", "-=", "<<=", ">>=", ">
 
 for (i = 0; i < assignmentOperators.length; ++i) {
     var op = assignmentOperators[i];
-    testRightAssociativeSame(op, op);
+    testRightAssociativeSame(op, op, true);
     if (i != 0)
-        testRightAssociativeSame("=", op);
+        testRightAssociativeSame("=", op, true);
     testLowerFirst(op, "+");
     shouldThrow("compileAndSerialize('a + b " + op + " c')");
     shouldThrow("compileAndSerialize('(a + b) " + op + " c')");
index ade9201..1868f9e 100644 (file)
@@ -1,3 +1,76 @@
+2020-01-30  Keith Miller  <keith_miller@apple.com>
+
+        Parser needs to restore unary stack state when backtracking
+        https://bugs.webkit.org/show_bug.cgi?id=206972
+
+        Reviewed by Saam Barati.
+
+        Previously we would try to parse possibly stale unary operator
+        stack entries after backtracking from a parse error.  This would
+        cause us to think one token was a different token while reparsing
+        after backtracking. Additionally, this patch fixes an issue where
+        the syntax checker would think assignment expressions were resolve
+        expressions. Intrestingly, this was not tested in test262.
+
+        Lastly, I tried adding some assertions to improve help diagnose
+        when our source text locations are incorrect.
+
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::emitExpressionInfo):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ThisNode::emitBytecode):
+        (JSC::ResolveNode::emitBytecode):
+        (JSC::EmptyVarExpression::emitBytecode):
+        (JSC::EmptyLetExpression::emitBytecode):
+        (JSC::ForInNode::emitLoopHeader):
+        (JSC::ForOfNode::emitBytecode):
+        (JSC::DefineFieldNode::emitBytecode):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::unaryTokenStackDepth const):
+        (JSC::ASTBuilder::setUnaryTokenStackDepth):
+        * parser/Lexer.cpp:
+        (JSC::Lexer<T>::Lexer):
+        * parser/Lexer.h:
+        (JSC::Lexer::setLineNumber):
+        * parser/Nodes.cpp:
+        (JSC::FunctionMetadataNode::operator== const):
+        * parser/Nodes.h:
+        (JSC::ThrowableExpressionData::ThrowableExpressionData):
+        (JSC::ThrowableExpressionData::setExceptionSourceCode):
+        (JSC::ThrowableExpressionData::checkConsistency const):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::isArrowFunctionParameters):
+        (JSC::Parser<LexerType>::parseSourceElements):
+        (JSC::Parser<LexerType>::parseModuleSourceElements):
+        (JSC::Parser<LexerType>::parseStatementListItem):
+        (JSC::Parser<LexerType>::parseAssignmentElement):
+        (JSC::Parser<LexerType>::parseForStatement):
+        (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>::parseMemberExpression):
+        (JSC::Parser<LexerType>::parseUnaryExpression):
+        * parser/Parser.h:
+        (JSC::Parser::lexCurrentTokenAgainUnderCurrentContext):
+        (JSC::Parser::internalSaveParserState):
+        (JSC::Parser::restoreParserState):
+        (JSC::Parser::internalSaveState):
+        (JSC::Parser::swapSavePointForError):
+        (JSC::Parser::createSavePoint):
+        (JSC::Parser::internalRestoreState):
+        (JSC::Parser::restoreSavePointWithError):
+        (JSC::Parser::restoreSavePoint):
+        (JSC::Parser::createSavePointForError): Deleted.
+        * parser/ParserTokens.h:
+        (JSC::JSTextPosition::JSTextPosition):
+        (JSC::JSTextPosition::checkConsistency):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::operatorStackPop):
+
 2020-01-29  Mark Lam  <mark.lam@apple.com>
 
         Fix bad assertion in InternalFunctionAllocationProfile::createAllocationStructureFromBase().
index 42c6302..148aeb6 100644 (file)
@@ -593,6 +593,9 @@ namespace JSC {
             ASSERT(divot.offset >= divotStart.offset);
             ASSERT(divotEnd.offset >= divot.offset);
 
+            if (m_isBuiltinFunction)
+                return;
+
             int sourceOffset = m_scopeNode->source().startOffset();
             unsigned firstLine = m_scopeNode->source().firstLine().oneBasedInt();
 
@@ -616,8 +619,7 @@ namespace JSC {
             unsigned column = divotOffset - lineStart;
 
             unsigned instructionOffset = instructions().size();
-            if (!m_isBuiltinFunction)
-                m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column);
+            m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column);
         }
 
 
index ab96d95..d383a7c 100644 (file)
@@ -174,8 +174,8 @@ RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst
         return 0;
 
     RegisterID* result = generator.move(dst, generator.thisRegister());
-    static constexpr unsigned thisLength = 4;
-    generator.emitProfileType(generator.thisRegister(), position(), JSTextPosition(-1, position().offset + thisLength, -1));
+    static const unsigned thisLength = strlen("this");
+    generator.emitProfileType(generator.thisRegister(), position(), position() + thisLength);
     return result;
 }
 
@@ -258,7 +258,7 @@ RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
         if (dst == generator.ignoredResult())
             return nullptr;
 
-        generator.emitProfileType(local, var, m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1));
+        generator.emitProfileType(local, var, m_position, m_position + m_ident.length());
         return generator.move(dst, local);
     }
     
@@ -270,7 +270,7 @@ RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
     generator.emitGetFromScope(uncheckedResult.get(), scope.get(), var, ThrowIfNotFound);
     generator.emitTDZCheckIfNecessary(var, uncheckedResult.get(), nullptr);
     generator.move(finalDest, uncheckedResult.get());
-    generator.emitProfileType(finalDest, var, m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1));
+    generator.emitProfileType(finalDest, var, m_position, m_position + m_ident.length());
     return finalDest;
 }
 
@@ -3088,11 +3088,11 @@ RegisterID* EmptyVarExpression::emitBytecode(BytecodeGenerator& generator, Regis
 
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local())
-        generator.emitProfileType(local, var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1));
+        generator.emitProfileType(local, var, position(), position() + m_ident.length());
     else {
         RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
         RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, DoNotThrowIfNotFound);
-        generator.emitProfileType(value.get(), var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1));
+        generator.emitProfileType(value.get(), var, position(), position() + m_ident.length());
     }
 
     return nullptr;
@@ -3107,12 +3107,12 @@ RegisterID* EmptyLetExpression::emitBytecode(BytecodeGenerator& generator, Regis
     Variable var = generator.variable(m_ident);
     if (RegisterID* local = var.local()) {
         generator.emitLoad(local, jsUndefined());
-        generator.emitProfileType(local, var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1));
+        generator.emitProfileType(local, var, position(), position() + m_ident.length());
     } else {
         RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
         RefPtr<RegisterID> value = generator.emitLoad(nullptr, jsUndefined());
         generator.emitPutToScope(scope.get(), var, value.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound, InitializationMode::Initialization);
-        generator.emitProfileType(value.get(), var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1)); 
+        generator.emitProfileType(value.get(), var, position(), position() + m_ident.length()); 
     }
 
     generator.liftTDZCheckIfPossible(var);
@@ -3329,7 +3329,7 @@ void ForInNode::emitLoopHeader(BytecodeGenerator& generator, RegisterID* propert
             generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
             generator.emitPutToScope(scope.get(), var, propertyName, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound, InitializationMode::NotInitialization);
         }
-        generator.emitProfileType(propertyName, var, m_lexpr->position(), JSTextPosition(-1, m_lexpr->position().offset + ident.length(), -1));
+        generator.emitProfileType(propertyName, var, m_lexpr->position(), m_lexpr->position() + ident.length());
     };
 
     if (m_lexpr->isResolveNode()) {
@@ -3586,7 +3586,7 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
                 generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
                 generator.emitPutToScope(scope.get(), var, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound, InitializationMode::NotInitialization);
             }
-            generator.emitProfileType(value, var, m_lexpr->position(), JSTextPosition(-1, m_lexpr->position().offset + ident.length(), -1));
+            generator.emitProfileType(value, var, m_lexpr->position(), m_lexpr->position() + ident.length());
         } else if (m_lexpr->isDotAccessorNode()) {
             DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr);
             const Identifier& ident = assignNode->identifier();
@@ -4350,7 +4350,7 @@ void DefineFieldNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
         RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), var);
         RefPtr<RegisterID> privateName = generator.newTemporary();
         generator.emitGetFromScope(privateName.get(), scope.get(), var, ThrowIfNotFound);
-        generator.emitProfileType(privateName.get(), var, m_position, JSTextPosition(-1, m_position.offset + m_ident->length(), -1));
+        generator.emitProfileType(privateName.get(), var, m_position, m_position + m_ident->length());
         generator.emitCallDefineProperty(generator.thisRegister(), privateName.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position);
         break;
     }
index dbd53ae..4c61730 100644 (file)
@@ -926,6 +926,16 @@ public:
         tokenStackDepth--;
         m_unaryTokenStack.removeLast();
     }
+
+    int unaryTokenStackDepth() const
+    {
+        return m_unaryTokenStack.size();
+    }
+
+    void setUnaryTokenStackDepth(int oldDepth)
+    {
+        m_unaryTokenStack.shrink(oldDepth);
+    }
     
     void assignmentStackAppend(int& assignmentStackDepth, ExpressionNode* node, const JSTextPosition& start, const JSTextPosition& divot, int assignmentCount, Operator op)
     {
index c341f49..97cabe1 100644 (file)
@@ -494,7 +494,8 @@ static constexpr const LChar singleCharacterEscapeValuesForASCII[128] = {
 
 template <typename T>
 Lexer<T>::Lexer(VM& vm, JSParserBuiltinMode builtinMode, JSParserScriptMode scriptMode)
-    : m_isReparsingFunction(false)
+    : m_positionBeforeLastNewline(0,0,0)
+    , m_isReparsingFunction(false)
     , m_vm(vm)
     , m_parsingBuiltinFunction(builtinMode == JSParserBuiltinMode::Builtin)
     , m_scriptMode(scriptMode)
index fd82580..effca4a 100644 (file)
@@ -109,6 +109,7 @@ public:
     }
     void setLineNumber(int line)
     {
+        ASSERT(line >= 0);
         m_lineNumber = line;
     }
     void setHasLineTerminatorBeforeToken(bool terminator)
index dd8edb6..9d1cfa4 100644 (file)
@@ -254,24 +254,24 @@ void FunctionMetadataNode::setEndPosition(JSTextPosition position)
 
 bool FunctionMetadataNode::operator==(const FunctionMetadataNode& other) const
 {
-    return m_parseMode== other.m_parseMode
+    return m_parseMode == other.m_parseMode
         && m_isInStrictContext == other.m_isInStrictContext
         && m_superBinding == other.m_superBinding
         && m_constructorKind == other.m_constructorKind
         && m_isArrowFunctionBodyExpression == other.m_isArrowFunctionBodyExpression
         && m_ident == other.m_ident
         && m_ecmaName == other.m_ecmaName
-        && m_functionMode== other.m_functionMode
-        && m_startColumn== other.m_startColumn
-        && m_endColumn== other.m_endColumn
-        && m_functionKeywordStart== other.m_functionKeywordStart
-        && m_functionNameStart== other.m_functionNameStart
-        && m_parametersStart== other.m_parametersStart
-        && m_source== other.m_source
-        && m_classSource== other.m_classSource
-        && m_startStartOffset== other.m_startStartOffset
-        && m_parameterCount== other.m_parameterCount
-        && m_lastLine== other.m_lastLine
+        && m_functionMode == other.m_functionMode
+        && m_startColumn == other.m_startColumn
+        && m_endColumn == other.m_endColumn
+        && m_functionKeywordStart == other.m_functionKeywordStart
+        && m_functionNameStart == other.m_functionNameStart
+        && m_parametersStart == other.m_parametersStart
+        && m_source == other.m_source
+        && m_classSource == other.m_classSource
+        && m_startStartOffset == other.m_startStartOffset
+        && m_parameterCount == other.m_parameterCount
+        && m_lastLine == other.m_lastLine
         && m_position == other.m_position;
 }
 
index 2a83646..49aac47 100644 (file)
@@ -372,37 +372,36 @@ namespace JSC {
 
     class ThrowableExpressionData {
     public:
-        ThrowableExpressionData()
-            : m_divot(-1, -1, -1)
-            , m_divotStart(-1, -1, -1)
-            , m_divotEnd(-1, -1, -1)
-        {
-        }
+        ThrowableExpressionData() = default;
         
         ThrowableExpressionData(const JSTextPosition& divot, const JSTextPosition& start, const JSTextPosition& end)
             : m_divot(divot)
             , m_divotStart(start)
             , m_divotEnd(end)
         {
-            ASSERT(m_divot.offset >= m_divot.lineStartOffset);
-            ASSERT(m_divotStart.offset >= m_divotStart.lineStartOffset);
-            ASSERT(m_divotEnd.offset >= m_divotEnd.lineStartOffset);
+            checkConsistency();
         }
 
         void setExceptionSourceCode(const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
         {
-            ASSERT(divot.offset >= divot.lineStartOffset);
-            ASSERT(divotStart.offset >= divotStart.lineStartOffset);
-            ASSERT(divotEnd.offset >= divotEnd.lineStartOffset);
             m_divot = divot;
             m_divotStart = divotStart;
             m_divotEnd = divotEnd;
+            checkConsistency();
         }
 
         const JSTextPosition& divot() const { return m_divot; }
         const JSTextPosition& divotStart() const { return m_divotStart; }
         const JSTextPosition& divotEnd() const { return m_divotEnd; }
 
+        void checkConsistency() const
+        {
+            ASSERT(m_divot.offset >= m_divot.lineStartOffset);
+            ASSERT(m_divotStart.offset >= m_divotStart.lineStartOffset);
+            ASSERT(m_divotEnd.offset >= m_divotEnd.lineStartOffset);
+            ASSERT(m_divot.offset >= m_divotStart.offset);
+            ASSERT(m_divotEnd.offset >= m_divot.offset);
+        }
     protected:
         RegisterID* emitThrowReferenceError(BytecodeGenerator&, const String& message);
 
@@ -2133,7 +2132,7 @@ namespace JSC {
         SourceCode m_classSource;
         int m_startStartOffset;
         unsigned m_parameterCount;
-        int m_lastLine;
+        int m_lastLine { 0 };
     };
 
     class FunctionNode final : public ScopeNode {
index b81132b..c2fa607 100644 (file)
@@ -29,6 +29,7 @@
 #include "JSCInlines.h"
 #include "VM.h"
 #include <utility>
+#include <wtf/Scope.h>
 #include <wtf/SetForScope.h>
 #include <wtf/StringPrintStream.h>
 
@@ -306,10 +307,10 @@ Expected<typename Parser<LexerType>::ParseInnerResult, String> Parser<LexerType>
 }
 
 template <typename LexerType>
-bool Parser<LexerType>::isArrowFunctionParameters()
+template <class TreeBuilder> bool Parser<LexerType>::isArrowFunctionParameters(TreeBuilder& context)
 {
     if (match(OPENPAREN)) {
-        SavePoint saveArrowFunctionPoint = createSavePoint();
+        SavePoint saveArrowFunctionPoint = createSavePoint(context);
         next();
         bool isArrowFunction = false;
         if (match(CLOSEPAREN)) {
@@ -328,16 +329,16 @@ bool Parser<LexerType>::isArrowFunctionParameters()
             propagateError();
             popScope(fakeScope, syntaxChecker.NeedsFreeVariableInfo);
         }
-        restoreSavePoint(saveArrowFunctionPoint);
+        restoreSavePoint(context, saveArrowFunctionPoint);
         return isArrowFunction;
     }
 
     if (matchSpecIdentifier()) {
         semanticFailIfTrue(!m_parserState.allowAwait && match(AWAIT), "Cannot use 'await' as a parameter name in an async function");
-        SavePoint saveArrowFunctionPoint = createSavePoint();
+        SavePoint saveArrowFunctionPoint = createSavePoint(context);
         next();
         bool isArrowFunction = match(ARROWFUNCTION);
-        restoreSavePoint(saveArrowFunctionPoint);
+        restoreSavePoint(context, saveArrowFunctionPoint);
         return isArrowFunction;
     }
 
@@ -357,7 +358,7 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceEl
     TreeSourceElements sourceElements = context.createSourceElements();
     const Identifier* directive = 0;
     unsigned directiveLiteralLength = 0;
-    auto savePoint = createSavePoint();
+    auto savePoint = createSavePoint(context);
     bool shouldCheckForUseStrict = mode == CheckForStrictMode;
     
     while (TreeStatement statement = parseStatementListItem(context, directive, &directiveLiteralLength)) {
@@ -382,7 +383,7 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceEl
                         semanticFailIfFalse(isValidStrictMode(), "Invalid parameters or function name in strict mode");
                     }
                     // Since strict mode is changed, restoring lexer state by calling next() may cause errors.
-                    restoreSavePoint(savePoint);
+                    restoreSavePoint(context, savePoint);
                     propagateError();
                     continue;
                 }
@@ -420,10 +421,10 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseModuleSo
             break;
 
         case IMPORT: {
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             next();
             bool isImportDeclaration = !match(OPENPAREN) && !match(DOT);
-            restoreSavePoint(savePoint);
+            restoreSavePoint(context, savePoint);
             if (isImportDeclaration) {
                 statement = parseImportDeclaration(context);
                 if (statement)
@@ -667,7 +668,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
     case LET: {
         bool shouldParseVariableDeclaration = true;
         if (!strictMode()) {
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             next();
             // Intentionally use `isIdentifierOrAnyContextualKeyword(m_token)` and don't use `matchSpecIdentifier()`.
             // We would like to fall into parseVariableDeclaration path even if "yield" is not treated as an Identifier.
@@ -676,7 +677,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
             // to raise consistent errors between "var", "const" and "let".
             if (!isIdentifierOrAnyContextualKeyword(m_token) && !match(OPENBRACE) && !match(OPENBRACKET))
                 shouldParseVariableDeclaration = false;
-            restoreSavePoint(savePoint);
+            restoreSavePoint(context, savePoint);
         }
         if (shouldParseVariableDeclaration)
             result = parseVariableDeclaration(context, DeclarationType::LetDeclaration);
@@ -697,13 +698,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
         if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) {
             // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
             // but could be mistakenly parsed as an AsyncFunctionExpression.
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             next();
             if (UNLIKELY(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken())) {
                 result = parseAsyncFunctionDeclaration(context);
                 break;
             }
-            restoreSavePoint(savePoint);
+            restoreSavePoint(context, savePoint);
         }
         FALLTHROUGH;
     case AWAIT:
@@ -1042,11 +1043,11 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseAs
     TreeDestructuringPattern assignmentTarget = 0;
 
     if (match(OPENBRACE) || match(OPENBRACKET)) {
-        SavePoint savePoint = createSavePoint();
+        SavePoint savePoint = createSavePoint(context);
         assignmentTarget = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth);
         if (assignmentTarget && !match(DOT) && !match(OPENBRACKET) && !match(OPENPAREN) && !match(BACKQUOTE))
             return assignmentTarget;
-        restoreSavePoint(savePoint);
+        restoreSavePoint(context, savePoint);
     }
 
     JSTextPosition startPosition = tokenStartPosition();
@@ -1431,14 +1432,14 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
     
     if (!match(SEMICOLON)) {
         if (match(OPENBRACE) || match(OPENBRACKET)) {
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             declsStart = tokenStartPosition();
             pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::DeclarationStatement);
             declsEnd = lastTokenEndPosition();
             if (pattern && (match(INTOKEN) || matchContextualKeyword(m_vm.propertyNames->of)))
                 goto enumerationLoop;
             pattern = TreeDestructuringPattern(0);
-            restoreSavePoint(savePoint);
+            restoreSavePoint(context, savePoint);
         }
         m_allowsIn = false;
         declsStart = tokenStartPosition();
@@ -2027,14 +2028,14 @@ template <typename LexerType>
 template <class TreeBuilder> bool Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement)
 {
     ASSERT(matchContextualKeyword(m_vm.propertyNames->async));
-    SavePoint savePoint = createSavePoint();
+    SavePoint savePoint = createSavePoint(context);
     next();
     if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) {
         const bool isAsync = true;
         result = parseFunctionDeclarationStatement(context, isAsync, parentAllowsFunctionDeclarationAsStatement);
         return true;
     }
-    restoreSavePoint(savePoint);
+    restoreSavePoint(context, savePoint);
     return false;
 }
 
@@ -2520,7 +2521,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     functionScope->setExpectedSuperBinding(expectedSuperBinding);
 
     m_parserState.lastFunctionName = lastFunctionName;
-    ParserState oldState = internalSaveParserState();
+    ParserState oldState = internalSaveParserState(context);
 
     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156962
     // This loop collects the set of capture candidates that aren't
@@ -2573,7 +2574,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     } else
         functionInfo.body = performParsingFunctionBody();
     
-    restoreParserState(oldState);
+    restoreParserState(context, oldState);
     failIfFalse(functionInfo.body, "Cannot parse the body of this ", stringForFunctionMode(mode));
     context.setEndOffset(functionInfo.body, m_lexer->currentOffset());
     if (functionScope->strictMode() && requirements != FunctionNameRequirements::Unnamed) {
@@ -2634,7 +2635,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
         // So we only check TreeBuilder's type here.
         ASSERT_UNUSED(functionScopeWasStrictMode, functionScopeWasStrictMode == currentScope()->strictMode());
         if (!std::is_same<TreeBuilder, SyntaxChecker>::value)
-            lexCurrentTokenAgainUnderCurrentContext();
+            lexCurrentTokenAgainUnderCurrentContext(context);
     }
 
     if (newInfo)
@@ -2864,11 +2865,11 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
         ClassElementTag tag = ClassElementTag::Instance;
         auto type = PropertyNode::Constant;
         if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm.propertyNames->staticKeyword) {
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             next();
             if (match(OPENPAREN)) {
                 // Reparse "static()" as a method named "static".
-                restoreSavePoint(savePoint);
+                restoreSavePoint(context, savePoint);
             } else
                 tag = ClassElementTag::Static;
         }
@@ -3494,7 +3495,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
 
         bool startsWithFunction = match(FUNCTION);
         if (startsWithFunction || match(CLASSTOKEN)) {
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             isFunctionOrClassDeclaration = true;
             next();
 
@@ -3503,9 +3504,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
                 next();
             if (match(IDENT))
                 localName = m_token.m_data.ident;
-            restoreSavePoint(savePoint);
+            restoreSavePoint(context, savePoint);
         } else if (matchContextualKeyword(m_vm.propertyNames->async)) {
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             next();
             if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) {
                 next();
@@ -3513,7 +3514,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
                     localName = m_token.m_data.ident;
                 isFunctionOrClassDeclaration = true;
             }
-            restoreSavePoint(savePoint);
+            restoreSavePoint(context, savePoint);
         }
 
         if (!localName)
@@ -3759,7 +3760,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
     // Whether spec identifier is will be validated by isArrowFunctionParameters().
     bool wasIdentifierOrKeyword = isIdentifierOrKeyword(m_token);
     bool maybeValidArrowFunctionStart = wasOpenParen || wasIdentifierOrKeyword;
-    SavePoint savePoint = createSavePoint();
+    SavePoint savePoint = createSavePoint(context);
     size_t usedVariablesSize = 0;
 
     if (wasOpenParen) {
@@ -3772,8 +3773,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
     if (maybeValidArrowFunctionStart && !match(EOFTOK)) {
         bool isArrowFunctionToken = match(ARROWFUNCTION);
         if (!lhs || isArrowFunctionToken) {
-            SavePointWithError errorRestorationSavePoint = createSavePointForError();
-            restoreSavePoint(savePoint);
+            SavePointWithError errorRestorationSavePoint = swapSavePointForError(context, savePoint);
             bool isAsyncArrow = false;
             if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) {
                 if (matchContextualKeyword(m_vm.propertyNames->async)) {
@@ -3781,14 +3781,14 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
                     isAsyncArrow = !m_lexer->hasLineTerminatorBeforeToken();
                 }
             }
-            if (isArrowFunctionParameters()) {
+            if (isArrowFunctionParameters(context)) {
                 if (wasOpenParen)
                     currentScope()->revertToPreviousUsedVariables(usedVariablesSize);
                 return parseArrowFunctionExpression(context, isAsyncArrow);
             }
             if (isArrowFunctionToken)
                 propagateError();
-            restoreSavePointWithError(errorRestorationSavePoint);
+            restoreSavePointWithError(context, errorRestorationSavePoint);
             if (isArrowFunctionToken)
                 failDueToUnexpectedToken();
         }
@@ -3798,11 +3798,10 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
         propagateError();
 
     if (maybeAssignmentPattern && (!lhs || (context.isObjectOrArrayLiteral(lhs) && match(EQUAL)))) {
-        SavePointWithError expressionErrorLocation = createSavePointForError();
-        restoreSavePoint(savePoint);
+        SavePointWithError expressionErrorLocation = swapSavePointForError(context, savePoint);
         auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression);
         if (classifier.indicatesPossiblePattern() && (!pattern || !match(EQUAL))) {
-            restoreSavePointWithError(expressionErrorLocation);
+            restoreSavePointWithError(context, expressionErrorLocation);
             return 0;
         }
         failIfFalse(pattern, "Cannot parse assignment pattern");
@@ -3867,9 +3866,6 @@ end:
     if (hadAssignment)
         m_parserState.nonLHSCount++;
     
-    if (!TreeBuilder::CreatesAST)
-        return lhs;
-    
     while (assignmentStack)
         lhs = context.createAssignment(location, assignmentStack, lhs, initialAssignmentCount, m_parserState.assignmentCount, lastTokenEndPosition());
     
@@ -3893,7 +3889,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseYieldExpress
     JSTokenLocation location(tokenLocation());
     JSTextPosition divotStart = tokenStartPosition();
     ASSERT(match(YIELD));
-    SavePoint savePoint = createSavePoint();
+    SavePoint savePoint = createSavePoint(context);
     next();
     if (m_lexer->hasLineTerminatorBeforeToken())
         return context.createYield(location);
@@ -3902,7 +3898,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseYieldExpress
     JSTextPosition argumentStart = tokenStartPosition();
     TreeExpression argument = parseAssignmentExpression(context);
     if (!argument) {
-        restoreSavePoint(savePoint);
+        restoreSavePoint(context, savePoint);
         next();
         return context.createYield(location);
     }
@@ -4057,11 +4053,11 @@ parseProperty:
     case IDENT:
         if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) {
             if (parseMode == SourceParseMode::MethodMode) {
-                SavePoint savePoint = createSavePoint();
+                SavePoint savePoint = createSavePoint(context);
                 next();
 
                 if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
-                    restoreSavePoint(savePoint);
+                    restoreSavePoint(context, savePoint);
                     wasIdent = true;
                     goto namedProperty;
                 }
@@ -4856,13 +4852,13 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
             optionalChainBase = base;
             optionalChainLocation = tokenLocation();
 
-            SavePoint savePoint = createSavePoint();
+            SavePoint savePoint = createSavePoint(context);
             next();
             if (match(OPENBRACKET) || match(OPENPAREN) || match(BACKQUOTE))
                 type = m_token.m_type;
             else {
                 type = DOT;
-                restoreSavePoint(savePoint);
+                restoreSavePoint(context, savePoint);
             }
         }
 
@@ -5042,19 +5038,15 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress
 
     JSTokenLocation location(tokenLocation());
 
+    int oldTokenStackDepth = context.unaryTokenStackDepth();
+    makeScopeExit([&] {
+        ASSERT_UNUSED(oldTokenStackDepth, oldTokenStackDepth <= context.unaryTokenStackDepth());
+    });
+
     while (isUnaryOp(m_token.m_type)) {
-        switch (m_token.m_type) {
-        case PLUSPLUS:
-        case MINUSMINUS:
-        case AUTOPLUSPLUS:
-        case AUTOMINUSMINUS:
-            semanticFailIfTrue(hasPrefixUpdateOp, "The ", operatorString(true, lastOperator), " operator requires a reference expression");
+        semanticFailIfTrue(hasPrefixUpdateOp, "The ", operatorString(true, lastOperator), " operator requires a reference expression");
+        if (isUpdateOp(m_token.m_type))
             hasPrefixUpdateOp = true;
-            break;
-        default:
-            semanticFailIfTrue(hasPrefixUpdateOp, "The ", operatorString(true, lastOperator), " operator requires a reference expression");
-            break;
-        }
         lastOperator = m_token.m_type;
         m_parserState.nonLHSCount++;
         context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStartPosition());
@@ -5069,6 +5061,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress
             failWithMessage("Cannot parse subexpression of ", operatorString(true, lastOperator), "operator");
         failWithMessage("Cannot parse member expression");
     }
+    if constexpr (std::is_same_v<TreeBuilder, ASTBuilder>)
+        ASSERT(oldTokenStackDepth + tokenStackDepth == context.unaryTokenStackDepth());
     if (isUpdateOp(static_cast<JSTokenType>(lastOperator))) {
         semanticFailIfTrue(context.isMetaProperty(expr), metaPropertyName(context, expr), " can't come after a prefix operator");
         semanticFailIfFalse(isSimpleAssignmentTarget(context, expr), "Prefix ", lastOperator == PLUSPLUS ? "++" : "--", " operator applied to value that is not a reference");
@@ -5108,7 +5102,9 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress
     
     JSTextPosition end = lastTokenEndPosition();
     while (tokenStackDepth) {
-        switch (context.unaryTokenStackLastType(tokenStackDepth)) {
+        subExprStart = context.unaryTokenStackLastStart(tokenStackDepth);
+        auto tokenType = context.unaryTokenStackLastType(tokenStackDepth);
+        switch (tokenType) {
         case EXCLAMATION:
             expr = context.createLogicalNot(location, expr);
             break;
@@ -5123,12 +5119,14 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress
             break;
         case PLUSPLUS:
         case AUTOPLUSPLUS:
-            expr = context.makePrefixNode(location, expr, OpPlusPlus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end);
+            ASSERT(isSimpleAssignmentTarget(context, expr));
+            expr = context.makePrefixNode(location, expr, OpPlusPlus, subExprStart, subExprStart + 2, end);
             m_parserState.assignmentCount++;
             break;
         case MINUSMINUS:
         case AUTOMINUSMINUS:
-            expr = context.makePrefixNode(location, expr, OpMinusMinus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end);
+            ASSERT(isSimpleAssignmentTarget(context, expr));
+            expr = context.makePrefixNode(location, expr, OpMinusMinus, subExprStart, subExprStart + 2, end);
             m_parserState.assignmentCount++;
             break;
         case TYPEOF:
@@ -5145,7 +5143,6 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress
             // If we get here something has gone horribly horribly wrong
             CRASH();
         }
-        subExprStart = context.unaryTokenStackLastStart(tokenStackDepth);
         context.unaryTokenStackRemoveLast(tokenStackDepth);
     }
     return expr;
index 7529e66..ae86319 100644 (file)
@@ -1417,10 +1417,11 @@ private:
         m_token.m_type = m_lexer->lexExpectIdentifier(&m_token, lexerFlags, strictMode());
     }
 
-    ALWAYS_INLINE void lexCurrentTokenAgainUnderCurrentContext()
+    template <class TreeBuilder>
+    ALWAYS_INLINE void lexCurrentTokenAgainUnderCurrentContext(TreeBuilder& context)
     {
-        auto savePoint = createSavePoint();
-        restoreSavePoint(savePoint);
+        auto savePoint = createSavePoint(context);
+        restoreSavePoint(context, savePoint);
     }
 
     ALWAYS_INLINE bool nextTokenIsColon()
@@ -1685,7 +1686,7 @@ private:
     enum class FunctionDefinitionType { Expression, Declaration, Method };
     template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType, Optional<int> functionConstructorParametersEndPosition = WTF::nullopt);
     
-    ALWAYS_INLINE bool isArrowFunctionParameters();
+    template <class TreeBuilder> ALWAYS_INLINE bool isArrowFunctionParameters(TreeBuilder&);
     
     template <class TreeBuilder, class FunctionInfoType> NEVER_INLINE typename TreeBuilder::FormalParameterList parseFunctionParameters(TreeBuilder&, SourceParseMode, FunctionInfoType&);
     template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::FormalParameterList createGeneratorParameters(TreeBuilder&, unsigned& parameterCount);
@@ -1791,6 +1792,7 @@ private:
         int assignmentCount { 0 };
         int nonLHSCount { 0 };
         int nonTrivialExpressionCount { 0 };
+        int unaryTokenStackDepth { 0 };
         FunctionParsePhase functionParsePhase { FunctionParsePhase::Body };
         const Identifier* lastIdentifier { nullptr };
         const Identifier* lastFunctionName { nullptr };
@@ -1800,14 +1802,19 @@ private:
 
     // If you're using this directly, you probably should be using
     // createSavePoint() instead.
-    ALWAYS_INLINE ParserState internalSaveParserState()
+    template <class TreeBuilder>
+    ALWAYS_INLINE ParserState internalSaveParserState(TreeBuilder& context)
     {
-        return m_parserState;
+        auto parserState = m_parserState;
+        parserState.unaryTokenStackDepth = context.unaryTokenStackDepth();
+        return parserState;
     }
 
-    ALWAYS_INLINE void restoreParserState(const ParserState& state)
+    template <class TreeBuilder>
+    ALWAYS_INLINE void restoreParserState(TreeBuilder& context, const ParserState& state)
     {
         m_parserState = state;
+        context.setUnaryTokenStackDepth(m_parserState.unaryTokenStackDepth);
     }
 
     struct LexerState {
@@ -1855,47 +1862,56 @@ private:
         String parserErrorMessage;
     };
 
-    ALWAYS_INLINE void internalSaveState(SavePoint& savePoint)
+    template <class TreeBuilder>
+    ALWAYS_INLINE void internalSaveState(TreeBuilder& context, SavePoint& savePoint)
     {
-        savePoint.parserState = internalSaveParserState();
+        savePoint.parserState = internalSaveParserState(context);
         savePoint.lexerState = internalSaveLexerState();
     }
     
-    ALWAYS_INLINE SavePointWithError createSavePointForError()
+    template <class TreeBuilder>
+    ALWAYS_INLINE SavePointWithError swapSavePointForError(TreeBuilder& context, SavePoint& oldSavePoint)
     {
         SavePointWithError savePoint;
-        internalSaveState(savePoint);
+        internalSaveState(context, savePoint);
         savePoint.lexerError = m_lexer->sawError();
         savePoint.lexerErrorMessage = m_lexer->getErrorMessage();
         savePoint.parserErrorMessage = m_errorMessage;
+        // Make sure we set our new savepoints unary stack to what oldSavePoint had as it currently may contain stale info.
+        savePoint.parserState.unaryTokenStackDepth = oldSavePoint.parserState.unaryTokenStackDepth;
+        restoreSavePoint(context, oldSavePoint);
         return savePoint;
     }
     
-    ALWAYS_INLINE SavePoint createSavePoint()
+    template <class TreeBuilder>
+    ALWAYS_INLINE SavePoint createSavePoint(TreeBuilder& context)
     {
         ASSERT(!hasError());
         SavePoint savePoint;
-        internalSaveState(savePoint);
+        internalSaveState(context, savePoint);
         return savePoint;
     }
 
-    ALWAYS_INLINE void internalRestoreState(const SavePoint& savePoint)
+    template <class TreeBuilder>
+    ALWAYS_INLINE void internalRestoreState(TreeBuilder& context, const SavePoint& savePoint)
     {
         restoreLexerState(savePoint.lexerState);
-        restoreParserState(savePoint.parserState);
+        restoreParserState(context, savePoint.parserState);
     }
 
-    ALWAYS_INLINE void restoreSavePointWithError(const SavePointWithError& savePoint)
+    template <class TreeBuilder>
+    ALWAYS_INLINE void restoreSavePointWithError(TreeBuilder& context, const SavePointWithError& savePoint)
     {
-        internalRestoreState(savePoint);
+        internalRestoreState(context, savePoint);
         m_lexer->setSawError(savePoint.lexerError);
         m_lexer->setErrorMessage(savePoint.lexerErrorMessage);
         m_errorMessage = savePoint.parserErrorMessage;
     }
 
-    ALWAYS_INLINE void restoreSavePoint(const SavePoint& savePoint)
+    template <class TreeBuilder>
+    ALWAYS_INLINE void restoreSavePoint(TreeBuilder& context, const SavePoint& savePoint)
     {
-        internalRestoreState(savePoint);
+        internalRestoreState(context, savePoint);
         m_errorMessage = String();
     }
 
index 30d2e2d..dbd6d65 100644 (file)
@@ -197,7 +197,13 @@ static_assert(static_cast<unsigned>(POW) <= 0x00ffffffU, "JSTokenType must be 24
 
 struct JSTextPosition {
     JSTextPosition() = default;
-    JSTextPosition(int _line, int _offset, int _lineStartOffset) : line(_line), offset(_offset), lineStartOffset(_lineStartOffset) { }
+    JSTextPosition(int _line, int _offset, int _lineStartOffset) 
+        : line(_line)
+        , offset(_offset)
+        , lineStartOffset(_lineStartOffset)
+    { 
+        checkConsistency();
+    }
 
     JSTextPosition operator+(int adjustment) const { return JSTextPosition(line, offset + adjustment, lineStartOffset); }
     JSTextPosition operator+(unsigned adjustment) const { return *this + static_cast<int>(adjustment); }
@@ -218,10 +224,18 @@ struct JSTextPosition {
     }
 
     int column() const { return offset - lineStartOffset; }
+    void checkConsistency()
+    {
+        // FIXME: We should test ASSERT(offset >= lineStartOffset); but that breaks a lot of tests.
+        ASSERT(line >= 0);
+        ASSERT(offset >= 0);
+        ASSERT(lineStartOffset >= 0);
+    }
 
-    int line { 0 };
-    int offset { 0 };
-    int lineStartOffset { 0 };
+    // FIXME: these should be unsigned.
+    int line { -1 };
+    int offset { -1 };
+    int lineStartOffset { -1 };
 };
 
 union JSTokenData {
index 656781c..f9bb07b 100644 (file)
@@ -334,9 +334,11 @@ public:
     int unaryTokenStackLastType(int&) { return m_topUnaryToken; }
     JSTextPosition unaryTokenStackLastStart(int&) { return JSTextPosition(0, 0, 0); }
     void unaryTokenStackRemoveLast(int& stackDepth) { stackDepth = 0; }
+    int unaryTokenStackDepth() const { return 0; }
+    void setUnaryTokenStackDepth(int) { }
     
-    void assignmentStackAppend(int, int, int, int, int, Operator) { }
-    int createAssignment(const JSTokenLocation&, int, int, int, int, int) { RELEASE_ASSERT_NOT_REACHED(); return AssignmentExpr; }
+    void assignmentStackAppend(int& assignmentStackDepth, int, int, int, int, Operator) { assignmentStackDepth = 1; }
+    int createAssignment(const JSTokenLocation&, int& assignmentStackDepth, int, int, int, int) { assignmentStackDepth = 0; return AssignmentExpr; }
     const Identifier* getName(const Property& property) const { return property.name; }
     PropertyNode::Type getType(const Property& property) const { return property.type; }
     bool isResolve(ExpressionType expr) const { return expr == ResolveExpr || expr == ResolveEvalExpr; }
@@ -435,7 +437,7 @@ public:
     int endOffset(int) { return 0; }
     void setStartOffset(int, int) { }
 
-    JSTextPosition breakpointLocation(int) { return JSTextPosition(-1, 0, 0); }
+    JSTextPosition breakpointLocation(int) { return { }; }
 
     void propagateArgumentsUse() { }