Title: [198927] trunk
Revision
198927
Author
[email protected]
Date
2016-03-31 16:01:29 -0700 (Thu, 31 Mar 2016)

Log Message

parsing arrow function expressions slows down the parser by 8% lets recoup some loss
https://bugs.webkit.org/show_bug.cgi?id=155988

Reviewed by Benjamin Poulain.

Source/_javascript_Core:

We used to eagerly check if we're parsing an arrow function.
We did this inside parseAssignmentExpression(), and it was
very costly. The reason it was costly is that arrow functions
might start with an identifier. This means anytime we saw an
identifier we would have to do a lookahead, and then most likely
backtrack because more often than not, we wouldn't see "=>"
as the next token.

In this patch I implement a new approach. We just parse
the lhs of an assignment _expression_ eagerly without doing any
lookahead. Retroactively, if we see that we might have started
with an arrow function, and we don't have a valid lhs or the
next token is a "=>", we try to parse as an arrow function.

Here are a few examples motivating why this is valid:

`x => x`
In this example:
- "x" is a valid arrow function starting point.
- "x" also happens to be a valid lhs
- because we see "=>" as the next token, we parse as an arrow function and succeed.

`(x) => x`
In this example:
- "(" is a valid arrow function starting point.
- "(x)" also happens to be a valid lhs
- because we see "=>" as the next token, we parse as an arrow function and succeed.

`({x = 30}) => x;`
In this example:
- "(" is a valid arrow function starting point.
- "({x = 30})" is NOT a valid lhs. Because of this, we try to parse it as an arrow function and succeed.

There is one interesting implementation detail where we might
parse something that is both a valid LHS but happens
to actually be the arrow function parameters. The valid LHS
parsing might declare such variables as "uses" which would cause
weird capture analysis. This patch also introduces a mechanism
to backtrack on used variable analysis.

This is a 3.5%-4.5% octane code load speedup.

* parser/Lexer.h:
(JSC::Lexer::sawError):
(JSC::Lexer::setSawError):
(JSC::Lexer::getErrorMessage):
(JSC::Lexer::setErrorMessage):
(JSC::Lexer::sourceURL):
(JSC::Lexer::sourceMappingURL):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::isArrowFunctionParameters):
(JSC::Parser<LexerType>::parseAssignmentExpression):
(JSC::Parser<LexerType>::parsePrimaryExpression):
* parser/Parser.h:
(JSC::Scope::Scope):
(JSC::Scope::startSwitch):
(JSC::Scope::declareParameter):
(JSC::Scope::usedVariablesContains):
(JSC::Scope::useVariable):
(JSC::Scope::pushUsedVariableSet):
(JSC::Scope::currentUsedVariablesSize):
(JSC::Scope::revertToPreviousUsedVariables):
(JSC::Scope::setNeedsFullActivation):
(JSC::Scope::needsFullActivation):
(JSC::Scope::isArrowFunctionBoundary):
(JSC::Scope::setInnerArrowFunctionUsesEvalAndUseArgumentsIfNeeded):
(JSC::Scope::collectFreeVariables):
(JSC::Scope::fillParametersForSourceProviderCache):
(JSC::Scope::restoreFromSourceProviderCache):
(JSC::Scope::setIsModule):

LayoutTests:

* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:
(catch):

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (198926 => 198927)


--- trunk/LayoutTests/ChangeLog	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/LayoutTests/ChangeLog	2016-03-31 23:01:29 UTC (rev 198927)
@@ -1,3 +1,14 @@
+2016-03-31  Saam barati  <[email protected]>
+
+        parsing arrow function expressions slows down the parser by 8% lets recoup some loss
+        https://bugs.webkit.org/show_bug.cgi?id=155988
+
+        Reviewed by Benjamin Poulain.
+
+        * js/parser-syntax-check-expected.txt:
+        * js/script-tests/parser-syntax-check.js:
+        (catch):
+
 2016-03-31  Per Arne Vollan  <[email protected]>
 
         [Win] Skip INTL related tests.

Modified: trunk/LayoutTests/js/parser-syntax-check-expected.txt (198926 => 198927)


--- trunk/LayoutTests/js/parser-syntax-check-expected.txt	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/LayoutTests/js/parser-syntax-check-expected.txt	2016-03-31 23:01:29 UTC (rev 198927)
@@ -1040,6 +1040,95 @@
 PASS Invalid: "function f() { 'use strict'; function foo(...yield) { } }"
 PASS Invalid: "function foo(...if) { }"
 PASS Invalid: "function f() { function foo(...if) { } }"
+Arrow function
+PASS Valid:   "var x = (x) => x;"
+PASS Valid:   "function f() { var x = (x) => x; }"
+PASS Valid:   "var x = (x, y, z) => x;"
+PASS Valid:   "function f() { var x = (x, y, z) => x; }"
+PASS Valid:   "var x = ({x}, [y], z) => x;"
+PASS Valid:   "function f() { var x = ({x}, [y], z) => x; }"
+PASS Valid:   "var x = ({x = 30}, [y], z) => x;"
+PASS Valid:   "function f() { var x = ({x = 30}, [y], z) => x; }"
+PASS Valid:   "var x = (x = 20) => x;"
+PASS Valid:   "function f() { var x = (x = 20) => x; }"
+PASS Valid:   "var x = ([x] = 20, y) => x;"
+PASS Valid:   "function f() { var x = ([x] = 20, y) => x; }"
+PASS Valid:   "var x = ([x = 20] = 20) => x;"
+PASS Valid:   "function f() { var x = ([x = 20] = 20) => x; }"
+PASS Valid:   "var x = foo => x;"
+PASS Valid:   "function f() { var x = foo => x; }"
+PASS Valid:   "var x = foo => x => x => x => x;"
+PASS Valid:   "function f() { var x = foo => x => x => x => x; }"
+PASS Valid:   "var x = foo => x => (x = 20) => (x = 20) => x;"
+PASS Valid:   "function f() { var x = foo => x => (x = 20) => (x = 20) => x; }"
+PASS Valid:   "var x = foo => x => x => x => {x};"
+PASS Valid:   "function f() { var x = foo => x => x => x => {x}; }"
+PASS Valid:   "var x = ([x = 25]) => x => x => ({x} = {});"
+PASS Valid:   "function f() { var x = ([x = 25]) => x => x => ({x} = {}); }"
+PASS Invalid: "var x = foo => x => x => {x} => x;"
+PASS Invalid: "function f() { var x = foo => x => x => {x} => x; }"
+PASS Invalid: "var x = {x} = 20 => x;"
+PASS Invalid: "function f() { var x = {x} = 20 => x; }"
+PASS Invalid: "var x = [x] = 20 => x;"
+PASS Invalid: "function f() { var x = [x] = 20 => x; }"
+PASS Invalid: "var x = [x = 25] = 20 => x;"
+PASS Invalid: "function f() { var x = [x = 25] = 20 => x; }"
+PASS Invalid: "var x = ([x = 25]) =>;"
+PASS Invalid: "function f() { var x = ([x = 25]) =>; }"
+PASS Invalid: "var x = ([x = 25]) => x =>;"
+PASS Invalid: "function f() { var x = ([x = 25]) => x =>; }"
+PASS Invalid: "var x = ([x = 25]) => x => x =>;"
+PASS Invalid: "function f() { var x = ([x = 25]) => x => x =>; }"
+PASS Invalid: "var x = ([x = 25]) => x => x => {;"
+PASS Invalid: "function f() { var x = ([x = 25]) => x => x => {; }"
+PASS Invalid: "var x ==> x;"
+PASS Invalid: "function f() { var x ==> x; }"
+PASS Invalid: "var x = x ==> x;"
+PASS Invalid: "function f() { var x = x ==> x; }"
+PASS Valid:   "foo((x) => x)" with ReferenceError
+PASS Valid:   "function f() { foo((x) => x) }"
+PASS Valid:   "foo((x, y, z) => x)" with ReferenceError
+PASS Valid:   "function f() { foo((x, y, z) => x) }"
+PASS Valid:   "foo(({x}, [y], z) => x)" with ReferenceError
+PASS Valid:   "function f() { foo(({x}, [y], z) => x) }"
+PASS Valid:   "foo(({x = 30}, [y], z) => x)" with ReferenceError
+PASS Valid:   "function f() { foo(({x = 30}, [y], z) => x) }"
+PASS Valid:   "foo((x = 20) => x)" with ReferenceError
+PASS Valid:   "function f() { foo((x = 20) => x) }"
+PASS Valid:   "foo(([x] = 20, y) => x)" with ReferenceError
+PASS Valid:   "function f() { foo(([x] = 20, y) => x) }"
+PASS Valid:   "foo(([x = 20] = 20) => x)" with ReferenceError
+PASS Valid:   "function f() { foo(([x = 20] = 20) => x) }"
+PASS Valid:   "foo(foo => x)" with ReferenceError
+PASS Valid:   "function f() { foo(foo => x) }"
+PASS Valid:   "foo(foo => x => x => x => x)" with ReferenceError
+PASS Valid:   "function f() { foo(foo => x => x => x => x) }"
+PASS Valid:   "foo(foo => x => (x = 20) => (x = 20) => x)" with ReferenceError
+PASS Valid:   "function f() { foo(foo => x => (x = 20) => (x = 20) => x) }"
+PASS Valid:   "foo(foo => x => x => x => {x})" with ReferenceError
+PASS Valid:   "function f() { foo(foo => x => x => x => {x}) }"
+PASS Valid:   "foo(([x = 25]) => x => x => ({x} = {}))" with ReferenceError
+PASS Valid:   "function f() { foo(([x = 25]) => x => x => ({x} = {})) }"
+PASS Invalid: "foo(foo => x => x => {x} => x)"
+PASS Invalid: "function f() { foo(foo => x => x => {x} => x) }"
+PASS Invalid: "foo({x} = 20 => x)"
+PASS Invalid: "function f() { foo({x} = 20 => x) }"
+PASS Invalid: "foo([x] = 20 => x)"
+PASS Invalid: "function f() { foo([x] = 20 => x) }"
+PASS Invalid: "foo([x = 25] = 20 => x)"
+PASS Invalid: "function f() { foo([x = 25] = 20 => x) }"
+PASS Invalid: "foo(([x = 25]) =>)"
+PASS Invalid: "function f() { foo(([x = 25]) =>) }"
+PASS Invalid: "foo(([x = 25]) => x =>)"
+PASS Invalid: "function f() { foo(([x = 25]) => x =>) }"
+PASS Invalid: "foo(([x = 25]) => x => x =>)"
+PASS Invalid: "function f() { foo(([x = 25]) => x => x =>) }"
+PASS Invalid: "foo(([x = 25]) => x => x => {)"
+PASS Invalid: "function f() { foo(([x = 25]) => x => x => {) }"
+PASS Invalid: "foo(x ==> x)"
+PASS Invalid: "function f() { foo(x ==> x) }"
+PASS Invalid: "foo(x = x ==> x)"
+PASS Invalid: "function f() { foo(x = x ==> x) }"
 PASS e.line is 1
 PASS foo is 'PASS'
 PASS bar is 'PASS'

Modified: trunk/LayoutTests/js/script-tests/parser-syntax-check.js (198926 => 198927)


--- trunk/LayoutTests/js/script-tests/parser-syntax-check.js	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/LayoutTests/js/script-tests/parser-syntax-check.js	2016-03-31 23:01:29 UTC (rev 198927)
@@ -618,6 +618,51 @@
 invalid("'use strict'; function foo(...yield) { }");
 invalid("function foo(...if) { }");
 
+debug("Arrow function");
+valid("var x = (x) => x;");
+valid("var x = (x, y, z) => x;");
+valid("var x = ({x}, [y], z) => x;");
+valid("var x = ({x = 30}, [y], z) => x;");
+valid("var x = (x = 20) => x;");
+valid("var x = ([x] = 20, y) => x;");
+valid("var x = ([x = 20] = 20) => x;");
+valid("var x = foo => x;");
+valid("var x = foo => x => x => x => x;");
+valid("var x = foo => x => (x = 20) => (x = 20) => x;");
+valid("var x = foo => x => x => x => {x};");
+valid("var x = ([x = 25]) => x => x => ({x} = {});");
+invalid("var x = foo => x => x => {x} => x;");
+invalid("var x = {x} = 20 => x;");
+invalid("var x = [x] = 20 => x;");
+invalid("var x = [x = 25] = 20 => x;");
+invalid("var x = ([x = 25]) =>;");
+invalid("var x = ([x = 25]) => x =>;");
+invalid("var x = ([x = 25]) => x => x =>;");
+invalid("var x = ([x = 25]) => x => x => {;");
+invalid("var x ==> x;");
+invalid("var x = x ==> x;");
+valid("foo((x) => x)");
+valid("foo((x, y, z) => x)");
+valid("foo(({x}, [y], z) => x)");
+valid("foo(({x = 30}, [y], z) => x)");
+valid("foo((x = 20) => x)");
+valid("foo(([x] = 20, y) => x)");
+valid("foo(([x = 20] = 20) => x)");
+valid("foo(foo => x)");
+valid("foo(foo => x => x => x => x)");
+valid("foo(foo => x => (x = 20) => (x = 20) => x)");
+valid("foo(foo => x => x => x => {x})");
+valid("foo(([x = 25]) => x => x => ({x} = {}))");
+invalid("foo(foo => x => x => {x} => x)");
+invalid("foo({x} = 20 => x)");
+invalid("foo([x] = 20 => x)");
+invalid("foo([x = 25] = 20 => x)");
+invalid("foo(([x = 25]) =>)");
+invalid("foo(([x = 25]) => x =>)");
+invalid("foo(([x = 25]) => x => x =>)");
+invalid("foo(([x = 25]) => x => x => {)");
+invalid("foo(x ==> x)");
+invalid("foo(x = x ==> x)");
 
 
 try { eval("a.b.c = {};"); } catch(e1) { e=e1; shouldBe("e.line", "1") }

Modified: trunk/Source/_javascript_Core/ChangeLog (198926 => 198927)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-31 23:01:29 UTC (rev 198927)
@@ -1,3 +1,81 @@
+2016-03-31  Saam barati  <[email protected]>
+
+        parsing arrow function expressions slows down the parser by 8% lets recoup some loss
+        https://bugs.webkit.org/show_bug.cgi?id=155988
+
+        Reviewed by Benjamin Poulain.
+
+        We used to eagerly check if we're parsing an arrow function.
+        We did this inside parseAssignmentExpression(), and it was
+        very costly. The reason it was costly is that arrow functions
+        might start with an identifier. This means anytime we saw an
+        identifier we would have to do a lookahead, and then most likely
+        backtrack because more often than not, we wouldn't see "=>"
+        as the next token.
+
+        In this patch I implement a new approach. We just parse
+        the lhs of an assignment _expression_ eagerly without doing any
+        lookahead. Retroactively, if we see that we might have started
+        with an arrow function, and we don't have a valid lhs or the
+        next token is a "=>", we try to parse as an arrow function.
+
+        Here are a few examples motivating why this is valid:
+
+        `x => x`
+        In this example:
+        - "x" is a valid arrow function starting point.
+        - "x" also happens to be a valid lhs
+        - because we see "=>" as the next token, we parse as an arrow function and succeed.
+
+        `(x) => x`
+        In this example:
+        - "(" is a valid arrow function starting point.
+        - "(x)" also happens to be a valid lhs
+        - because we see "=>" as the next token, we parse as an arrow function and succeed.
+
+        `({x = 30}) => x;`
+        In this example:
+        - "(" is a valid arrow function starting point.
+        - "({x = 30})" is NOT a valid lhs. Because of this, we try to parse it as an arrow function and succeed.
+
+        There is one interesting implementation detail where we might
+        parse something that is both a valid LHS but happens
+        to actually be the arrow function parameters. The valid LHS
+        parsing might declare such variables as "uses" which would cause 
+        weird capture analysis. This patch also introduces a mechanism
+        to backtrack on used variable analysis.
+
+        This is a 3.5%-4.5% octane code load speedup.
+
+        * parser/Lexer.h:
+        (JSC::Lexer::sawError):
+        (JSC::Lexer::setSawError):
+        (JSC::Lexer::getErrorMessage):
+        (JSC::Lexer::setErrorMessage):
+        (JSC::Lexer::sourceURL):
+        (JSC::Lexer::sourceMappingURL):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::isArrowFunctionParameters):
+        (JSC::Parser<LexerType>::parseAssignmentExpression):
+        (JSC::Parser<LexerType>::parsePrimaryExpression):
+        * parser/Parser.h:
+        (JSC::Scope::Scope):
+        (JSC::Scope::startSwitch):
+        (JSC::Scope::declareParameter):
+        (JSC::Scope::usedVariablesContains):
+        (JSC::Scope::useVariable):
+        (JSC::Scope::pushUsedVariableSet):
+        (JSC::Scope::currentUsedVariablesSize):
+        (JSC::Scope::revertToPreviousUsedVariables):
+        (JSC::Scope::setNeedsFullActivation):
+        (JSC::Scope::needsFullActivation):
+        (JSC::Scope::isArrowFunctionBoundary):
+        (JSC::Scope::setInnerArrowFunctionUsesEvalAndUseArgumentsIfNeeded):
+        (JSC::Scope::collectFreeVariables):
+        (JSC::Scope::fillParametersForSourceProviderCache):
+        (JSC::Scope::restoreFromSourceProviderCache):
+        (JSC::Scope::setIsModule):
+
 2016-03-31  Yusuke Suzuki  <[email protected]>
 
         Fails to build in Linux / PowerPC due to different ucontext_t definition

Modified: trunk/Source/_javascript_Core/parser/Lexer.h (198926 => 198927)


--- trunk/Source/_javascript_Core/parser/Lexer.h	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/Source/_javascript_Core/parser/Lexer.h	2016-03-31 23:01:29 UTC (rev 198927)
@@ -85,7 +85,9 @@
 
     // Functions for use after parsing.
     bool sawError() const { return m_error; }
+    void setSawError(bool sawError) { m_error = sawError; }
     String getErrorMessage() const { return m_lexErrorMessage; }
+    void setErrorMessage(const String& errorMessage) { m_lexErrorMessage = errorMessage; }
     String sourceURL() const { return m_sourceURLDirective; }
     String sourceMappingURL() const { return m_sourceMappingURLDirective; }
     void clear();

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (198926 => 198927)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2016-03-31 23:01:29 UTC (rev 198927)
@@ -60,7 +60,6 @@
 #define semanticFailIfTrue(cond, ...) do { if (cond) internalFailWithMessage(false, __VA_ARGS__); } while (0)
 #define semanticFailIfFalse(cond, ...) do { if (!(cond)) internalFailWithMessage(false, __VA_ARGS__); } while (0)
 #define regexFail(failure) do { setErrorMessage(failure); return 0; } while (0)
-#define restoreSavePointAndFail(savePoint, message) do { restoreSavePointWithError(savePoint, message); return 0; } while (0)
 #define failDueToUnexpectedToken() do {\
         logError(true);\
     return 0;\
@@ -359,17 +358,13 @@
 template <typename LexerType>
 bool Parser<LexerType>::isArrowFunctionParameters()
 {
-    bool isArrowFunction = false;
-
-    if (match(EOFTOK))
-        return false;
-    
     bool isOpenParen = match(OPENPAREN);
     bool isIdent = match(IDENT);
     
     if (!isOpenParen && !isIdent)
         return false;
 
+    bool isArrowFunction = false;
     SavePoint saveArrowFunctionPoint = createSavePoint();
         
     if (isIdent) {
@@ -2990,30 +2985,64 @@
     int initialAssignmentCount = m_parserState.assignmentCount;
     int initialNonLHSCount = m_parserState.nonLHSCount;
     bool maybeAssignmentPattern = match(OPENBRACE) || match(OPENBRACKET);
+#if ENABLE(ES6_ARROWFUNCTION_SYNTAX)
+    bool wasOpenParen = match(OPENPAREN);
+    bool isValidArrowFunctionStart = match(OPENPAREN) || match(IDENT);
     SavePoint savePoint = createSavePoint();
+    size_t usedVariablesSize;
+    if (wasOpenParen) {
+        usedVariablesSize = currentScope()->currentUsedVariablesSize();
+        currentScope()->pushUsedVariableSet();
+    }
+#endif
 
 #if ENABLE(ES6_GENERATORS)
     if (match(YIELD) && !isYIELDMaskedAsIDENT(currentScope()->isGenerator()))
         return parseYieldExpression(context);
 #endif
 
+    TreeExpression lhs = parseConditionalExpression(context);
+
 #if ENABLE(ES6_ARROWFUNCTION_SYNTAX)
-    if (isArrowFunctionParameters())
-        return parseArrowFunctionExpression(context);
+    if (isValidArrowFunctionStart && !match(EOFTOK)) {
+        bool isArrowFunctionToken = match(ARROWFUNCTION);
+        if (!lhs || isArrowFunctionToken) {
+            SavePoint errorRestorationSavePoint = createSavePointForError();
+            String oldErrorMessage = m_errorMessage;
+            String oldLexerErrorMessage = m_lexer->getErrorMessage();
+            bool hasLexerError = m_lexer->sawError();
+            restoreSavePoint(savePoint);
+            if (isArrowFunctionParameters()) {
+                if (wasOpenParen)
+                    currentScope()->revertToPreviousUsedVariables(usedVariablesSize);
+                return parseArrowFunctionExpression(context);
+            }
+            restoreSavePointWithError(errorRestorationSavePoint, oldErrorMessage);
+            m_lexer->setErrorMessage(oldLexerErrorMessage);
+            m_lexer->setSawError(hasLexerError);
+            if (isArrowFunctionToken)
+                failDueToUnexpectedToken();
+        }
+    }
+
 #endif
-    
-    TreeExpression lhs = parseConditionalExpression(context);
 
     if (!lhs && (!maybeAssignmentPattern || !classifier.indicatesPossiblePattern()))
         propagateError();
 
     if (maybeAssignmentPattern && (!lhs || (context.isObjectOrArrayLiteral(lhs) && match(EQUAL)))) {
         String expressionError = m_errorMessage;
+        String oldLexerErrorMessage = m_lexer->getErrorMessage();
+        bool hasLexerError = m_lexer->sawError();
         SavePoint expressionErrorLocation = createSavePointForError();
         restoreSavePoint(savePoint);
         auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression);
-        if (classifier.indicatesPossiblePattern() && (!pattern || !match(EQUAL)))
-            restoreSavePointAndFail(expressionErrorLocation, expressionError);
+        if (classifier.indicatesPossiblePattern() && (!pattern || !match(EQUAL))) {
+            restoreSavePointWithError(expressionErrorLocation, expressionError);
+            m_lexer->setErrorMessage(oldLexerErrorMessage);
+            m_lexer->setSawError(hasLexerError);
+            return 0;
+        }
         failIfFalse(pattern, "Cannot parse assignment pattern");
         consumeOrFail(EQUAL, "Expected '=' following assignment pattern");
         auto rhs = parseAssignmentExpression(context);
@@ -3666,6 +3695,8 @@
         const Identifier* ident = m_token.m_data.ident;
         JSTokenLocation location(tokenLocation());
         next();
+        if (UNLIKELY(match(ARROWFUNCTION)))
+            return 0;
         currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident);
         m_parserState.lastIdentifier = ident;
         return context.createResolve(location, *ident, start, lastTokenEndPosition());

Modified: trunk/Source/_javascript_Core/parser/Parser.h (198926 => 198927)


--- trunk/Source/_javascript_Core/parser/Parser.h	2016-03-31 22:54:48 UTC (rev 198926)
+++ trunk/Source/_javascript_Core/parser/Parser.h	2016-03-31 23:01:29 UTC (rev 198927)
@@ -43,17 +43,7 @@
 #include <wtf/SmallPtrSet.h>
 
 namespace JSC {
-struct Scope;
-}
 
-namespace WTF {
-template <> struct VectorTraits<JSC::Scope> : VectorTraitsBase</* is pod */ false, void> {
-    static const bool canMoveWithMemcpy = true;
-};
-}
-
-namespace JSC {
-
 class ExecState;
 class FunctionMetadataNode;
 class FunctionParameters;
@@ -191,8 +181,46 @@
         , m_switchDepth(0)
         , m_innerArrowFunctionFeatures(0)
     {
+        m_usedVariables.append(UniquedStringImplPtrSet());
     }
 
+    Scope(Scope&& other)
+        : m_vm(other.m_vm)
+        , m_shadowsArguments(other.m_shadowsArguments)
+        , m_usesEval(other.m_usesEval)
+        , m_needsFullActivation(other.m_needsFullActivation)
+        , m_hasDirectSuper(other.m_hasDirectSuper)
+        , m_needsSuperBinding(other.m_needsSuperBinding)
+        , m_allowsVarDeclarations(other.m_allowsVarDeclarations)
+        , m_allowsLexicalDeclarations(other.m_allowsLexicalDeclarations)
+        , m_strictMode(other.m_strictMode)
+        , m_isFunction(other.m_isFunction)
+        , m_isGenerator(other.m_isGenerator)
+        , m_isGeneratorBoundary(other.m_isGeneratorBoundary)
+        , m_isArrowFunction(other.m_isArrowFunction)
+        , m_isArrowFunctionBoundary(other.m_isArrowFunctionBoundary)
+        , m_isLexicalScope(other.m_isLexicalScope)
+        , m_isFunctionBoundary(other.m_isFunctionBoundary)
+        , m_isValidStrictMode(other.m_isValidStrictMode)
+        , m_hasArguments(other.m_hasArguments)
+        , m_isEvalContext(other.m_isEvalContext)
+        , m_constructorKind(other.m_constructorKind)
+        , m_expectedSuperBinding(other.m_expectedSuperBinding)
+        , m_loopDepth(other.m_loopDepth)
+        , m_switchDepth(other.m_switchDepth)
+        , m_innerArrowFunctionFeatures(other.m_innerArrowFunctionFeatures)
+        , m_labels(WTFMove(other.m_labels))
+        , m_declaredParameters(WTFMove(other.m_declaredParameters))
+        , m_declaredVariables(WTFMove(other.m_declaredVariables))
+        , m_lexicalVariables(WTFMove(other.m_lexicalVariables))
+        , m_usedVariables(WTFMove(other.m_usedVariables))
+        , m_closedVariableCandidates(WTFMove(other.m_closedVariableCandidates))
+        , m_writtenVariables(WTFMove(other.m_writtenVariables))
+        , m_moduleScopeData(WTFMove(other.m_moduleScopeData))
+        , m_functionDeclarations(WTFMove(other.m_functionDeclarations))
+    {
+    }
+
     void startSwitch() { m_switchDepth++; }
     void endSwitch() { m_switchDepth--; }
     void startLoop() { m_loopDepth++; }
@@ -462,13 +490,29 @@
         return result;
     }
     
-    bool usedVariablesContains(UniquedStringImpl* impl) const { return m_usedVariables.contains(impl); }
+    bool usedVariablesContains(UniquedStringImpl* impl) const
+    { 
+        for (const UniquedStringImplPtrSet& set : m_usedVariables) {
+            if (set.contains(impl))
+                return true;
+        }
+        return false;
+    }
     void useVariable(const Identifier* ident, bool isEval)
     {
+        useVariable(ident->impl(), isEval);
+    }
+    void useVariable(UniquedStringImpl* impl, bool isEval)
+    {
         m_usesEval |= isEval;
-        m_usedVariables.add(ident->impl());
+        m_usedVariables.last().add(impl);
     }
 
+    void pushUsedVariableSet() { m_usedVariables.append(UniquedStringImplPtrSet()); }
+    size_t currentUsedVariablesSize() { return m_usedVariables.size(); }
+
+    void revertToPreviousUsedVariables(size_t size) { m_usedVariables.resize(size); }
+
     void setNeedsFullActivation() { m_needsFullActivation = true; }
     bool needsFullActivation() const { return m_needsFullActivation; }
     bool isArrowFunctionBoundary() { return m_isArrowFunctionBoundary; }
@@ -504,7 +548,7 @@
         if (m_usesEval)
             setInnerArrowFunctionUsesEval();
         
-        if (m_usedVariables.contains(m_vm->propertyNames->arguments.impl()))
+        if (usedVariablesContains(m_vm->propertyNames->arguments.impl()))
             setInnerArrowFunctionUsesArguments();
     }
     
@@ -514,20 +558,23 @@
             m_usesEval = true;
 
         {
-            for (UniquedStringImpl* impl : nestedScope->m_usedVariables) {
-                if (nestedScope->m_declaredVariables.contains(impl) || nestedScope->m_lexicalVariables.contains(impl))
-                    continue;
+            UniquedStringImplPtrSet& destinationSet = m_usedVariables.last();
+            for (const UniquedStringImplPtrSet& usedVariablesSet : nestedScope->m_usedVariables) {
+                for (UniquedStringImpl* impl : usedVariablesSet) {
+                    if (nestedScope->m_declaredVariables.contains(impl) || nestedScope->m_lexicalVariables.contains(impl))
+                        continue;
 
-                // "arguments" reference should be resolved at function boudary.
-                if (nestedScope->isFunctionBoundary() && nestedScope->hasArguments() && impl == m_vm->propertyNames->arguments.impl() && !nestedScope->isArrowFunctionBoundary())
-                    continue;
+                    // "arguments" reference should be resolved at function boudary.
+                    if (nestedScope->isFunctionBoundary() && nestedScope->hasArguments() && impl == m_vm->propertyNames->arguments.impl() && !nestedScope->isArrowFunctionBoundary())
+                        continue;
 
-                m_usedVariables.add(impl);
-                // We don't want a declared variable that is used in an inner scope to be thought of as captured if
-                // that inner scope is both a lexical scope and not a function. Only inner functions and "catch" 
-                // statements can cause variables to be captured.
-                if (shouldTrackClosedVariables && (nestedScope->m_isFunctionBoundary || !nestedScope->m_isLexicalScope))
-                    m_closedVariableCandidates.add(impl);
+                    destinationSet.add(impl);
+                    // We don't want a declared variable that is used in an inner scope to be thought of as captured if
+                    // that inner scope is both a lexical scope and not a function. Only inner functions and "catch" 
+                    // statements can cause variables to be captured.
+                    if (shouldTrackClosedVariables && (nestedScope->m_isFunctionBoundary || !nestedScope->m_isLexicalScope))
+                        m_closedVariableCandidates.add(impl);
+                }
             }
         }
         // Propagate closed variable candidates downwards within the same function.
@@ -602,7 +649,8 @@
         parameters.needsFullActivation = m_needsFullActivation;
         parameters.innerArrowFunctionFeatures = m_innerArrowFunctionFeatures;
         copyCapturedVariablesToVector(m_writtenVariables, parameters.writtenVariables);
-        copyCapturedVariablesToVector(m_usedVariables, parameters.usedVariables);
+        for (const UniquedStringImplPtrSet& set : m_usedVariables)
+            copyCapturedVariablesToVector(set, parameters.usedVariables);
     }
 
     void restoreFromSourceProviderCache(const SourceProviderCacheItem* info)
@@ -612,8 +660,9 @@
         m_strictMode = info->strictMode;
         m_innerArrowFunctionFeatures = info->innerArrowFunctionFeatures;
         m_needsFullActivation = info->needsFullActivation;
+        UniquedStringImplPtrSet& destSet = m_usedVariables.last();
         for (unsigned i = 0; i < info->usedVariablesCount; ++i)
-            m_usedVariables.add(info->usedVariables()[i]);
+            destSet.add(info->usedVariables()[i]);
         for (unsigned i = 0; i < info->writtenVariablesCount; ++i)
             m_writtenVariables.add(info->writtenVariables()[i]);
     }
@@ -657,9 +706,6 @@
         m_moduleScopeData = ModuleScopeData::create();
     }
 
-    // All the fields in Scope must be able to use memcpy as their
-    // move operation. If you add a field that violates this, make sure
-    // to remove this comment and update WTF::VectorTraits<JSC::Scope>.
     const VM* m_vm;
     bool m_shadowsArguments;
     bool m_usesEval;
@@ -690,7 +736,7 @@
     UniquedStringImplPtrSet m_declaredParameters;
     VariableEnvironment m_declaredVariables;
     VariableEnvironment m_lexicalVariables;
-    UniquedStringImplPtrSet m_usedVariables;
+    Vector<UniquedStringImplPtrSet, 6> m_usedVariables;
     IdentifierSet m_closedVariableCandidates;
     UniquedStringImplPtrSet m_writtenVariables;
     RefPtr<ModuleScopeData> m_moduleScopeData;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to