Title: [271265] trunk
Revision
271265
Author
[email protected]
Date
2021-01-07 15:17:44 -0800 (Thu, 07 Jan 2021)

Log Message

[JSC] New _expression_ and value function call should reserve function register if arguments include assignments
https://bugs.webkit.org/show_bug.cgi?id=220429
<rdar://problem/70598359>

Reviewed by Alexey Shvayka.

JSTests:

* stress/comma-value-func-call-resolve.js: Added.
(shouldBe):
(fn.x):
(fn):
* stress/construct-overwritten-variable.js:
(shouldThrow):
(new.x.x): Deleted.
* stress/construct-spread-overwritten-variable-2.js:
(shouldThrow):
(new.x.x): Deleted.
* stress/construct-spread-overwritten-variable.js:
(shouldThrow):
(new.x.x): Deleted.
* stress/destructuring-func-call-resolve.js: Added.
(shouldBe):
(fn.x):
(fn):
(fn2.x):
(fn2):
* stress/resolve-func-call-resolve.js: Added.
(shouldBe):
(fn.x):
(fn):
* stress/tagged-template-call-resolve.js: Added.
(shouldBe):
(fn.x):
(fn):
* test262/expectations.yaml:

Source/_javascript_Core:

If the following code is executed, we need to reserve |x| before evaluating arguments since arguments can override
local |x| variable before calling it.

    new x(x = 1)

We found there are two places we are not doing this.

    1. new _expression_
    2. function value call (it is checking `isLocation()`, but we can still use local variables for function if we use comma _expression_)

We introduced hasAssignment flag to ArgumentsNode, and reserve a function in a new temporary register if arguments include assignments.
We also need to increment assignmentCount in destructuring assignment.

* bytecompiler/NodesCodegen.cpp:
(JSC::NewExprNode::emitBytecode):
(JSC::FunctionCallValueNode::emitBytecode):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createArguments):
* parser/NodeConstructors.h:
(JSC::ArgumentsNode::ArgumentsNode):
* parser/Nodes.h:
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseDestructuringPattern):
(JSC::Parser<LexerType>::parseArguments):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createArguments):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (271264 => 271265)


--- trunk/JSTests/ChangeLog	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/JSTests/ChangeLog	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1,5 +1,42 @@
 2021-01-07  Yusuke Suzuki  <[email protected]>
 
+        [JSC] New _expression_ and value function call should reserve function register if arguments include assignments
+        https://bugs.webkit.org/show_bug.cgi?id=220429
+        <rdar://problem/70598359>
+
+        Reviewed by Alexey Shvayka.
+
+        * stress/comma-value-func-call-resolve.js: Added.
+        (shouldBe):
+        (fn.x):
+        (fn):
+        * stress/construct-overwritten-variable.js:
+        (shouldThrow):
+        (new.x.x): Deleted.
+        * stress/construct-spread-overwritten-variable-2.js:
+        (shouldThrow):
+        (new.x.x): Deleted.
+        * stress/construct-spread-overwritten-variable.js:
+        (shouldThrow):
+        (new.x.x): Deleted.
+        * stress/destructuring-func-call-resolve.js: Added.
+        (shouldBe):
+        (fn.x):
+        (fn):
+        (fn2.x):
+        (fn2):
+        * stress/resolve-func-call-resolve.js: Added.
+        (shouldBe):
+        (fn.x):
+        (fn):
+        * stress/tagged-template-call-resolve.js: Added.
+        (shouldBe):
+        (fn.x):
+        (fn):
+        * test262/expectations.yaml:
+
+2021-01-07  Yusuke Suzuki  <[email protected]>
+
         Unreviewed, check ICU header version instead of ICU version
         https://bugs.webkit.org/show_bug.cgi?id=220419
 

Added: trunk/JSTests/stress/comma-value-func-call-resolve.js (0 => 271265)


--- trunk/JSTests/stress/comma-value-func-call-resolve.js	                        (rev 0)
+++ trunk/JSTests/stress/comma-value-func-call-resolve.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function fn() {
+  var ok = {};
+  var x = function() {
+    ok.foo = 42;
+  };
+
+  var result = (0, x)(x = 1);
+
+  shouldBe(x, 1);
+  shouldBe(ok.foo, 42);
+}
+
+fn();

Modified: trunk/JSTests/stress/construct-overwritten-variable.js (271264 => 271265)


--- trunk/JSTests/stress/construct-overwritten-variable.js	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/JSTests/stress/construct-overwritten-variable.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1,6 +1,21 @@
 //@ runDefault
 
-(function(){
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(function(){
     var x = 42;
     new x(x = function(){ });
-})();
+}, `TypeError: 42 is not a constructor (evaluating 'new x(x = function(){ })')`);

Modified: trunk/JSTests/stress/construct-spread-overwritten-variable-2.js (271264 => 271265)


--- trunk/JSTests/stress/construct-spread-overwritten-variable-2.js	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/JSTests/stress/construct-spread-overwritten-variable-2.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1,6 +1,21 @@
 //@ runDefault
 
-(function(){
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(function(){
     var x = 42;
     new x(...[x = function(){ }]);
-})();
+}, `TypeError: 42 is not a constructor (evaluating 'new x(...[x = function(){ }])')`);

Modified: trunk/JSTests/stress/construct-spread-overwritten-variable.js (271264 => 271265)


--- trunk/JSTests/stress/construct-spread-overwritten-variable.js	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/JSTests/stress/construct-spread-overwritten-variable.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1,7 +1,22 @@
 //@ runDefault
 
-(function(){
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(function(){
     var x = 42;
     var a = [1, 2, 3];
     new x(x = function(){ }, ...a);
-})();
+}, `TypeError: 42 is not a constructor (evaluating 'new x(x = function(){ }, ...a)')`);

Added: trunk/JSTests/stress/destructuring-func-call-resolve.js (0 => 271265)


--- trunk/JSTests/stress/destructuring-func-call-resolve.js	                        (rev 0)
+++ trunk/JSTests/stress/destructuring-func-call-resolve.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -0,0 +1,31 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function fn() {
+  var ok = {};
+  var x = function() {
+    ok.foo = 42;
+  };
+
+  var result = new x({ x } = { x: 1 });
+
+  shouldBe(x, 1);
+  shouldBe(ok.foo, 42);
+}
+
+function fn2() {
+  var ok = {};
+  var x = function() {
+    ok.foo = 42;
+  };
+
+  var result = new x([ x ] = [ 1 ]);
+
+  shouldBe(x, 1);
+  shouldBe(ok.foo, 42);
+}
+
+fn();
+fn2();

Added: trunk/JSTests/stress/resolve-func-call-resolve.js (0 => 271265)


--- trunk/JSTests/stress/resolve-func-call-resolve.js	                        (rev 0)
+++ trunk/JSTests/stress/resolve-func-call-resolve.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function fn() {
+  var ok = {};
+  var x = function() {
+    ok.foo = 42;
+  };
+
+  var result = x(x = 1);
+
+  shouldBe(x, 1);
+  shouldBe(ok.foo, 42);
+}
+
+fn();

Added: trunk/JSTests/stress/tagged-template-call-resolve.js (0 => 271265)


--- trunk/JSTests/stress/tagged-template-call-resolve.js	                        (rev 0)
+++ trunk/JSTests/stress/tagged-template-call-resolve.js	2021-01-07 23:17:44 UTC (rev 271265)
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function fn() {
+  var ok = {};
+  var x = function() {
+    ok.foo = 42;
+  };
+
+  var result = x`_${x = 1}_`
+
+  shouldBe(x, 1);
+  shouldBe(ok.foo, 42);
+}
+
+fn();

Modified: trunk/JSTests/test262/expectations.yaml (271264 => 271265)


--- trunk/JSTests/test262/expectations.yaml	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/JSTests/test262/expectations.yaml	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1630,12 +1630,6 @@
   default: 'Test262: This statement should not be evaluated.'
 test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-simple-lhs.js:
   default: 'Test262: This statement should not be evaluated.'
-test/language/expressions/new/ctorExpr-fn-ref-before-args-eval-fn-wrapup.js:
-  default: "TypeError: 1 is not a constructor (evaluating 'new x(x = 1)')"
-  strict mode: "TypeError: 1 is not a constructor (evaluating 'new x(x = 1)')"
-test/language/expressions/new/ctorExpr-isCtor-after-args-eval-fn-wrapup.js:
-  default: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
 test/language/expressions/new/non-ctor-err-realm.js:
   default: 'Test262Error: production including Arguments Expected a TypeError but got a TypeError'
   strict mode: 'Test262Error: production including Arguments Expected a TypeError but got a TypeError'

Modified: trunk/Source/_javascript_Core/ChangeLog (271264 => 271265)


--- trunk/Source/_javascript_Core/ChangeLog	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1,3 +1,38 @@
+2021-01-07  Yusuke Suzuki  <[email protected]>
+
+        [JSC] New _expression_ and value function call should reserve function register if arguments include assignments
+        https://bugs.webkit.org/show_bug.cgi?id=220429
+        <rdar://problem/70598359>
+
+        Reviewed by Alexey Shvayka.
+
+        If the following code is executed, we need to reserve |x| before evaluating arguments since arguments can override
+        local |x| variable before calling it.
+
+            new x(x = 1)
+
+        We found there are two places we are not doing this.
+
+            1. new _expression_
+            2. function value call (it is checking `isLocation()`, but we can still use local variables for function if we use comma _expression_)
+
+        We introduced hasAssignment flag to ArgumentsNode, and reserve a function in a new temporary register if arguments include assignments.
+        We also need to increment assignmentCount in destructuring assignment.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::NewExprNode::emitBytecode):
+        (JSC::FunctionCallValueNode::emitBytecode):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createArguments):
+        * parser/NodeConstructors.h:
+        (JSC::ArgumentsNode::ArgumentsNode):
+        * parser/Nodes.h:
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseDestructuringPattern):
+        (JSC::Parser<LexerType>::parseArguments):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createArguments):
+
 2021-01-07  Mark Lam  <[email protected]>
 
         The scratch register should be different from the target register when calling validateUntaggedPtr.

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (271264 => 271265)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2021-01-07 23:17:44 UTC (rev 271265)
@@ -979,7 +979,11 @@
         expectedFunction = generator.expectedFunctionForIdentifier(static_cast<ResolveNode*>(m_expr)->identifier());
     else
         expectedFunction = NoExpectedFunction;
-    RefPtr<RegisterID> func = generator.emitNode(m_expr);
+
+    RefPtr<RegisterID> func = nullptr;
+    if (m_args && m_args->hasAssignments())
+        func = generator.newTemporary();
+    func = generator.emitNode(func.get(), m_expr);
     RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get());
     CallArguments callArguments(generator, m_args);
     return generator.emitConstruct(returnValue.get(), func.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd());
@@ -1100,7 +1104,10 @@
         return ret;
     }
 
-    RefPtr<RegisterID> func = generator.emitNode(m_expr);
+    RefPtr<RegisterID> func = nullptr;
+    if (m_args && m_args->hasAssignments())
+        func = generator.newTemporary();
+    func = generator.emitNode(func.get(), m_expr);
     RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get());
     if (isOptionalChainBase())
         generator.emitOptionalCheck(func.get());

Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (271264 => 271265)


--- trunk/Source/_javascript_Core/parser/ASTBuilder.h	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h	2021-01-07 23:17:44 UTC (rev 271265)
@@ -477,7 +477,7 @@
     }
 
     ArgumentsNode* createArguments() { return new (m_parserArena) ArgumentsNode(); }
-    ArgumentsNode* createArguments(ArgumentListNode* args) { return new (m_parserArena) ArgumentsNode(args); }
+    ArgumentsNode* createArguments(ArgumentListNode* args, bool hasAssignments) { return new (m_parserArena) ArgumentsNode(args, hasAssignments); }
     ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ExpressionNode* arg) { return new (m_parserArena) ArgumentListNode(location, arg); }
     ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ArgumentListNode* args, ExpressionNode* arg) { return new (m_parserArena) ArgumentListNode(location, args, arg); }
 

Modified: trunk/Source/_javascript_Core/parser/NodeConstructors.h (271264 => 271265)


--- trunk/Source/_javascript_Core/parser/NodeConstructors.h	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/parser/NodeConstructors.h	2021-01-07 23:17:44 UTC (rev 271265)
@@ -364,8 +364,9 @@
     {
     }
 
-    inline ArgumentsNode::ArgumentsNode(ArgumentListNode* listNode)
+    inline ArgumentsNode::ArgumentsNode(ArgumentListNode* listNode, bool hasAssignments)
         : m_listNode(listNode)
+        , m_hasAssignments(hasAssignments)
     {
     }
 

Modified: trunk/Source/_javascript_Core/parser/Nodes.h (271264 => 271265)


--- trunk/Source/_javascript_Core/parser/Nodes.h	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/parser/Nodes.h	2021-01-07 23:17:44 UTC (rev 271265)
@@ -914,9 +914,13 @@
     class ArgumentsNode final : public ParserArenaFreeable {
     public:
         ArgumentsNode();
-        ArgumentsNode(ArgumentListNode*);
+        ArgumentsNode(ArgumentListNode*, bool hasAssignments);
 
+        bool hasAssignments() const { return m_hasAssignments; }
+
         ArgumentListNode* m_listNode;
+    private:
+        bool m_hasAssignments { false };
     };
 
     class NewExprNode final : public ExpressionNode, public ThrowableExpressionData {

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (271264 => 271265)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2021-01-07 23:17:44 UTC (rev 271265)
@@ -1126,6 +1126,7 @@
 template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
 {
     failIfStackOverflow();
+    m_parserState.assignmentCount++;
     int nonLHSCount = m_parserState.nonLHSCount;
     TreeDestructuringPattern pattern;
     switch (m_token.m_type) {
@@ -4836,6 +4837,7 @@
     auto argumentsStart = m_token.m_startPosition;
     auto argumentsDivot = m_token.m_endPosition;
 
+    int initialAssignments = m_parserState.assignmentCount;
     ArgumentType argType = ArgumentType::Normal;
     TreeExpression firstArg = parseArgument(context, argType);
     failIfFalse(firstArg, "Cannot parse function argument");
@@ -4867,10 +4869,10 @@
     handleProductionOrFail2(CLOSEPAREN, ")", "end", "argument list");
     if (hasSpread) {
         TreeExpression spreadArray = context.createSpreadExpression(location, context.createArray(location, context.createElementList(argList)), argumentsStart, argumentsDivot, m_lastTokenEndPosition);
-        return context.createArguments(context.createArgumentsList(location, spreadArray));
+        return context.createArguments(context.createArgumentsList(location, spreadArray), initialAssignments != m_parserState.assignmentCount);
     }
 
-    return context.createArguments(argList);
+    return context.createArguments(argList, initialAssignments != m_parserState.assignmentCount);
 }
 
 template <typename LexerType>

Modified: trunk/Source/_javascript_Core/parser/SyntaxChecker.h (271264 => 271265)


--- trunk/Source/_javascript_Core/parser/SyntaxChecker.h	2021-01-07 23:10:51 UTC (rev 271264)
+++ trunk/Source/_javascript_Core/parser/SyntaxChecker.h	2021-01-07 23:17:44 UTC (rev 271265)
@@ -200,7 +200,7 @@
     ExpressionType createMethodDefinition(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; }
     void setFunctionNameStart(int, int) { }
     int createArguments() { return ArgumentsResult; }
-    int createArguments(int) { return ArgumentsResult; }
+    int createArguments(int, bool) { return ArgumentsResult; }
     ExpressionType createSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return SpreadExpr; }
     ExpressionType createObjectSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return ObjectSpreadExpr; }
     TemplateString createTemplateString(const JSTokenLocation&, const Identifier*, const Identifier*) { return TemplateStringResult; }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to