Title: [192603] trunk/Source/_javascript_Core
Revision
192603
Author
sbar...@apple.com
Date
2015-11-18 17:26:36 -0800 (Wed, 18 Nov 2015)

Log Message

There is a bug when default parameter values are mixed with destructuring parameter values
https://bugs.webkit.org/show_bug.cgi?id=151369

Reviewed by Geoffrey Garen and Mark Lam.

Relanding this after a rollout.

This patch changes our parser to no longer declare destructuring
parameters as "var"s. This is a weird bug that just happened
to work in a world without default parameter values. In a world with
default parameter values this is just completely wrong. It would
incorrectly transform this program:
```function foo(a = function() { b = 40; }, {b}) { a(); return b; }; foo(undefined, {b: 42}); // Should return 40```
into
```function foo(a = function() { b = 40; }, {b}) { var b; a(); return b; }; foo(undefined, {b:42}); // Returns 42, not 40.```
Which is wrong because we end up with two distinct bindings of "b" when
there should only be one.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseVariableDeclarationList):
(JSC::Parser<LexerType>::createBindingPattern):
(JSC::Parser<LexerType>::parseDestructuringPattern):
* parser/Parser.h:
(JSC::Scope::declareParameter):
(JSC::Scope::getUsedVariables):
(JSC::Parser::strictMode):
(JSC::Parser::isValidStrictMode):
(JSC::Parser::declareParameter):
(JSC::Parser::breakIsValid):
(JSC::Scope::declareBoundParameter): Deleted.
(JSC::Parser::declareBoundParameter): Deleted.
* tests/stress/es6-default-parameters.js:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (192602 => 192603)


--- trunk/Source/_javascript_Core/ChangeLog	2015-11-19 01:24:02 UTC (rev 192602)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-11-19 01:26:36 UTC (rev 192603)
@@ -1,3 +1,38 @@
+2015-11-18  Saam barati  <sbar...@apple.com>
+
+        There is a bug when default parameter values are mixed with destructuring parameter values
+        https://bugs.webkit.org/show_bug.cgi?id=151369
+
+        Reviewed by Geoffrey Garen and Mark Lam.
+
+        Relanding this after a rollout.
+
+        This patch changes our parser to no longer declare destructuring
+        parameters as "var"s. This is a weird bug that just happened
+        to work in a world without default parameter values. In a world with
+        default parameter values this is just completely wrong. It would
+        incorrectly transform this program:
+        ```function foo(a = function() { b = 40; }, {b}) { a(); return b; }; foo(undefined, {b: 42}); // Should return 40```
+        into
+        ```function foo(a = function() { b = 40; }, {b}) { var b; a(); return b; }; foo(undefined, {b:42}); // Returns 42, not 40.```
+        Which is wrong because we end up with two distinct bindings of "b" when
+        there should only be one.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseVariableDeclarationList):
+        (JSC::Parser<LexerType>::createBindingPattern):
+        (JSC::Parser<LexerType>::parseDestructuringPattern):
+        * parser/Parser.h:
+        (JSC::Scope::declareParameter):
+        (JSC::Scope::getUsedVariables):
+        (JSC::Parser::strictMode):
+        (JSC::Parser::isValidStrictMode):
+        (JSC::Parser::declareParameter):
+        (JSC::Parser::breakIsValid):
+        (JSC::Scope::declareBoundParameter): Deleted.
+        (JSC::Parser::declareBoundParameter): Deleted.
+        * tests/stress/es6-default-parameters.js:
+
 2015-11-18  Mark Lam  <mark....@apple.com>
 
         Snippefy op_mul for the baseline JIT.

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (192602 => 192603)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2015-11-19 01:24:02 UTC (rev 192602)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2015-11-19 01:26:36 UTC (rev 192603)
@@ -667,7 +667,7 @@
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier)
 {
     ASSERT(!name.isNull());
     
@@ -685,42 +685,23 @@
             failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'");
         }
     } else if (kind == DestructureToParameters) {
-        if (depth) {
-            auto bindingResult = declareBoundParameter(&name);
-            if (bindingResult == Scope::StrictBindingFailed && strictMode()) {
-                semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
-                if (m_lastFunctionName && name == *m_lastFunctionName)
-                    semanticFail("Cannot destructure to '", name.impl(), "' as it shadows the name of a strict mode function");
-                semanticFailureDueToKeyword("bound parameter name");
-                if (hasDeclaredParameter(name))
-                    semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared");
-                semanticFail("Cannot bind to a parameter named '", name.impl(), "' in strict mode");
-            }
-            if (bindingResult == Scope::BindingFailed) {
-                semanticFailureDueToKeyword("bound parameter name");
-                if (hasDeclaredParameter(name))
-                    semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared");
-                semanticFail("Cannot destructure to a parameter named '", name.impl(), "'");
-            }
-        } else {
-            DeclarationResultMask declarationResult = declareParameter(&name);
-            if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) {
-                semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
-                if (m_lastFunctionName && name == *m_lastFunctionName)
-                    semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function");
-                semanticFailureDueToKeyword("parameter name");
-                if (hasDeclaredParameter(name))
-                    semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared");
-                semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode");
-            }
-            if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) {
-                // It's not always an error to define a duplicate parameter.
-                // It's only an error when there are default parameter values or destructuring parameters.
-                // We note this value now so we can check it later.
-                if (duplicateIdentifier)
-                    *duplicateIdentifier = &name;
-            }
+        DeclarationResultMask declarationResult = declareParameter(&name);
+        if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) {
+            semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
+            if (m_lastFunctionName && name == *m_lastFunctionName)
+                semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function");
+            semanticFailureDueToKeyword("parameter name");
+            if (hasDeclaredParameter(name))
+                semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared");
+            semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode");
         }
+        if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) {
+            // It's not always an error to define a duplicate parameter.
+            // It's only an error when there are default parameter values or destructuring parameters.
+            // We note this value now so we can check it later.
+            if (duplicateIdentifier)
+                *duplicateIdentifier = &name;
+        }
     }
 
     if (exportType == ExportType::Exported) {
@@ -847,7 +828,7 @@
                 if (consume(COLON))
                     innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
                 else
-                    innerPattern = createBindingPattern(context, kind, exportType, *propertyName, depth + 1, identifierToken, bindingContext, duplicateIdentifier);
+                    innerPattern = createBindingPattern(context, kind, exportType, *propertyName, identifierToken, bindingContext, duplicateIdentifier);
             } else {
                 JSTokenType tokenType = m_token.m_type;
                 switch (m_token.m_type) {
@@ -903,7 +884,7 @@
             failWithMessage("Expected a parameter pattern or a ')' in parameter list");
         }
         failIfTrue(match(LET) && (kind == DestructureToLet || kind == DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration");
-        pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, depth, m_token, bindingContext, duplicateIdentifier);
+        pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, m_token, bindingContext, duplicateIdentifier);
         next();
         break;
     }

Modified: trunk/Source/_javascript_Core/parser/Parser.h (192602 => 192603)


--- trunk/Source/_javascript_Core/parser/Parser.h	2015-11-19 01:24:02 UTC (rev 192602)
+++ trunk/Source/_javascript_Core/parser/Parser.h	2015-11-19 01:26:36 UTC (rev 192603)
@@ -424,26 +424,6 @@
         return result;
     }
     
-    enum BindingResult {
-        BindingFailed,
-        StrictBindingFailed,
-        BindingSucceeded
-    };
-    BindingResult declareBoundParameter(const Identifier* ident)
-    {
-        bool isArgumentsIdent = isArguments(m_vm, ident);
-        auto addResult = m_declaredVariables.add(ident->impl());
-        addResult.iterator->value.setIsVar(); // Treat destructuring parameters as "var"s.
-        bool isValidStrictMode = addResult.isNewEntry && !isEval(m_vm, ident) && !isArgumentsIdent;
-        m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
-    
-        if (isArgumentsIdent)
-            m_shadowsArguments = true;
-        if (!addResult.isNewEntry)
-            return BindingFailed;
-        return isValidStrictMode ? BindingSucceeded : StrictBindingFailed;
-    }
-
     void getUsedVariables(IdentifierSet& usedVariables)
     {
         usedVariables.swap(m_usedVariables);
@@ -1054,7 +1034,6 @@
     bool strictMode() { return currentScope()->strictMode(); }
     bool isValidStrictMode() { return currentScope()->isValidStrictMode(); }
     DeclarationResultMask declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); }
-    Scope::BindingResult declareBoundParameter(const Identifier* ident) { return currentScope()->declareBoundParameter(ident); }
     bool breakIsValid()
     {
         ScopeRef current = currentScope();
@@ -1159,7 +1138,7 @@
     template <class TreeBuilder> TreeExpression parseVariableDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext, DeclarationType, ExportType, bool& forLoopConstDoesNotHaveInitializer);
     template <class TreeBuilder> TreeSourceElements parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder&);
     template <class TreeBuilder> TreeExpression parseArrowFunctionExpression(TreeBuilder&);
-    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier&, int depth, JSToken, AssignmentContext, const Identifier** duplicateIdentifier);
+    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier&, JSToken, AssignmentContext, const Identifier** duplicateIdentifier);
     template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier** duplicateIdentifier = nullptr, bool* hasDestructuringPattern = nullptr, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0);
     template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&, AssignmentContext);
     template <class TreeBuilder> NEVER_INLINE TreeExpression parseDefaultValueForDestructuringPattern(TreeBuilder&);

Modified: trunk/Source/_javascript_Core/tests/stress/es6-default-parameters.js (192602 => 192603)


--- trunk/Source/_javascript_Core/tests/stress/es6-default-parameters.js	2015-11-19 01:24:02 UTC (rev 192602)
+++ trunk/Source/_javascript_Core/tests/stress/es6-default-parameters.js	2015-11-19 01:26:36 UTC (rev 192603)
@@ -235,7 +235,39 @@
 })();
 
 
+// Test proper variable binding.
+;(function() {
+    function foo(a = function() { return b; }, {b}) {
+        assert(a() === 34);
+        assert(b === 34);
+        b = 50;
+        assert(a() === 50);
+        assert(b === 50);
+    }
+    function bar(a = function(x) { b = x; }, {b}) {
+        assert(b === 34);
+        a(50);
+        assert(b === 50);
+    }
+    function baz(f1 = function(x) { b = x; }, f2 = function() { return b; }, {b}) {
+        var b;
+        assert(b === 34);
+        assert(f2() === 34);
+        f1(50);
+        assert(b === 34);
+        assert(f2() === 50);
+    }
+    noInline(foo);
+    noInline(bar);
+    noInline(baz);
+    for (let i = 0; i < 1000; i++) {
+        foo(undefined, {b: 34});
+        bar(undefined, {b: 34});
+        baz(undefined, undefined, {b: 34});
+    }
+})();
 
+
 // Syntax errors.
 shouldThrowSyntaxError("function b(a = 20, a = 40) {}");
 shouldThrowSyntaxError("function b(aaaaa = 20,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v, aaaaa = 40) {}");
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to