parsing arrow function expressions slows down the parser by 8% lets recoup some loss
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Mar 2016 23:01:29 +0000 (23:01 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Mar 2016 23:01:29 +0000 (23:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155988

Reviewed by Benjamin Poulain.

Source/JavaScriptCore:

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):

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

LayoutTests/ChangeLog
LayoutTests/js/parser-syntax-check-expected.txt
LayoutTests/js/script-tests/parser-syntax-check.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Lexer.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h

index 112c51e..07ad4b4 100644 (file)
@@ -1,3 +1,14 @@
+2016-03-31  Saam barati  <sbarati@apple.com>
+
+        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  <peavo@outlook.com>
 
         [Win] Skip INTL related tests.
index 97ef09b..dabdd2f 100644 (file)
@@ -1040,6 +1040,95 @@ PASS Invalid: "'use strict'; function foo(...yield) { }"
 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'
index 845dae7..b45891b 100644 (file)
@@ -618,6 +618,51 @@ valid("function foo(...yield) { }");
 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") }
index 1a2514c..f25a16d 100644 (file)
@@ -1,3 +1,81 @@
+2016-03-31  Saam barati  <sbarati@apple.com>
+
+        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  <utatane.tea@gmail.com>
 
         Fails to build in Linux / PowerPC due to different ucontext_t definition
index c7b63b2..0b5ad96 100644 (file)
@@ -85,7 +85,9 @@ public:
 
     // 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();
index 7ffb75b..fd60d74 100644 (file)
@@ -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 @@ void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, Declara
 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 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
     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 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
         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());
index a96a031..342c199 100644 (file)
 #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;
@@ -191,6 +181,44 @@ public:
         , 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++; }
@@ -462,13 +490,29 @@ public:
         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 @@ public:
         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 @@ public:
             m_usesEval = true;
 
         {
-            for (UniquedStringImpl* impl : nestedScope->m_usedVariables) {
-                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;
-
-                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);
+            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;
+
+                    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 @@ public:
         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 @@ public:
         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 @@ private:
         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 @@ private:
     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;