[es6] Make assignment to new.target an early ReferenceError
authoradamk <adamk@chromium.org>
Thu, 13 Aug 2015 18:06:04 +0000 (11:06 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 13 Aug 2015 18:06:13 +0000 (18:06 +0000)
In doing so, fix calls CheckAndRewriteReferenceExpression to take proper
start and end positions (instead of just pointing at the first token in
the LHS expression).

BUG=v8:4370
LOG=n

Review URL: https://codereview.chromium.org/1290013002

Cr-Commit-Position: refs/heads/master@{#30166}

13 files changed:
src/ast.h
src/parser.cc
src/preparser.cc
src/preparser.h
test/message/new-target-assignment.js [new file with mode: 0644]
test/message/new-target-assignment.out [new file with mode: 0644]
test/message/new-target-for-loop.js [new file with mode: 0644]
test/message/new-target-for-loop.out [new file with mode: 0644]
test/message/new-target-postfix-op.js [new file with mode: 0644]
test/message/new-target-postfix-op.out [new file with mode: 0644]
test/message/new-target-prefix-op.js [new file with mode: 0644]
test/message/new-target-prefix-op.out [new file with mode: 0644]
test/mjsunit/harmony/new-target.js

index 9bd7c26..1366041 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1639,7 +1639,9 @@ class VariableProxy final : public Expression {
  public:
   DECLARE_NODE_TYPE(VariableProxy)
 
-  bool IsValidReferenceExpression() const override { return !is_this(); }
+  bool IsValidReferenceExpression() const override {
+    return !is_this() && !is_new_target();
+  }
 
   bool IsArguments() const { return is_resolved() && var()->is_arguments(); }
 
@@ -1670,6 +1672,11 @@ class VariableProxy final : public Expression {
     bit_field_ = IsResolvedField::update(bit_field_, true);
   }
 
+  bool is_new_target() const { return IsNewTargetField::decode(bit_field_); }
+  void set_is_new_target() {
+    bit_field_ = IsNewTargetField::update(bit_field_, true);
+  }
+
   int end_position() const { return end_position_; }
 
   // Bind this proxy to the variable var.
@@ -1705,6 +1712,7 @@ class VariableProxy final : public Expression {
   class IsThisField : public BitField8<bool, 0, 1> {};
   class IsAssignedField : public BitField8<bool, 1, 1> {};
   class IsResolvedField : public BitField8<bool, 2, 1> {};
+  class IsNewTargetField : public BitField8<bool, 3, 1> {};
 
   // Start with 16-bit (or smaller) field, which should get packed together
   // with Expression's trailing 16-bit field.
index e53352d..5b5e613 100644 (file)
@@ -789,9 +789,11 @@ Expression* ParserTraits::NewTargetExpression(Scope* scope,
                                               AstNodeFactory* factory,
                                               int pos) {
   static const int kNewTargetStringLength = 10;
-  return scope->NewUnresolved(
+  auto proxy = scope->NewUnresolved(
       factory, parser_->ast_value_factory()->new_target_string(),
       Variable::NORMAL, pos, pos + kNewTargetStringLength);
+  proxy->set_is_new_target();
+  return proxy;
 }
 
 
@@ -3679,8 +3681,9 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
             CHECK_OK);
       }
     } else {
-      Scanner::Location lhs_location = scanner()->peek_location();
+      int lhs_beg_pos = peek_position();
       Expression* expression = ParseExpression(false, CHECK_OK);
+      int lhs_end_pos = scanner()->location().end_pos;
       ForEachStatement::VisitMode mode;
       bool accept_OF = expression->IsVariableProxy();
       is_let_identifier_expression =
@@ -3691,8 +3694,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
       if (CheckInOrOf(accept_OF, &mode, ok)) {
         if (!*ok) return nullptr;
         expression = this->CheckAndRewriteReferenceExpression(
-            expression, lhs_location, MessageTemplate::kInvalidLhsInFor,
-            CHECK_OK);
+            expression, lhs_beg_pos, lhs_end_pos,
+            MessageTemplate::kInvalidLhsInFor, CHECK_OK);
 
         ForEachStatement* loop =
             factory()->NewForEachStatement(mode, labels, stmt_pos);
@@ -3711,8 +3714,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
         return loop;
 
       } else {
-        init =
-            factory()->NewExpressionStatement(expression, lhs_location.beg_pos);
+        init = factory()->NewExpressionStatement(expression, lhs_beg_pos);
       }
     }
   }
index ec0ea2d..f186f8a 100644 (file)
@@ -918,6 +918,8 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
           lhs.IsIdentifier() && lhs.AsIdentifier().IsLet();
       if (CheckInOrOf(lhs.IsIdentifier(), &mode, ok)) {
         if (!*ok) return Statement::Default();
+        // TODO(adamk): Should call CheckAndRewriteReferenceExpression here
+        // to catch early errors if lhs is not a valid reference expression.
         ParseExpression(true, CHECK_OK);
         Expect(Token::RPAREN, CHECK_OK);
         ParseSubStatement(CHECK_OK);
index 61cb883..65951f3 100644 (file)
@@ -713,7 +713,7 @@ class ParserBase : public Traits {
   // left-hand side of assignments). Although ruled out by ECMA as early errors,
   // we allow calls for web compatibility and rewrite them to a runtime throw.
   ExpressionT CheckAndRewriteReferenceExpression(
-      ExpressionT expression, Scanner::Location location,
+      ExpressionT expression, int beg_pos, int end_pos,
       MessageTemplate::Template message, bool* ok);
 
   // Used to validate property names in object literals and class literals
@@ -2821,7 +2821,7 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
   //   YieldExpression
   //   LeftHandSideExpression AssignmentOperator AssignmentExpression
 
-  Scanner::Location lhs_location = scanner()->peek_location();
+  int lhs_beg_pos = peek_position();
 
   if (peek() == Token::YIELD && is_generator()) {
     return this->ParseYieldExpression(classifier, ok);
@@ -2841,13 +2841,13 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
     BindingPatternUnexpectedToken(classifier);
     ValidateArrowFormalParameters(&arrow_formals_classifier, expression,
                                   parenthesized_formals, CHECK_OK);
-    Scanner::Location loc(lhs_location.beg_pos, scanner()->location().end_pos);
+    Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos);
     Scope* scope =
         this->NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction);
     FormalParametersT parameters(scope);
     checkpoint.Restore(&parameters.materialized_literals_count);
 
-    scope->set_start_position(lhs_location.beg_pos);
+    scope->set_start_position(lhs_beg_pos);
     Scanner::Location duplicate_loc = Scanner::Location::invalid();
     this->ParseArrowFunctionFormalParameterList(&parameters, expression, loc,
                                                 &duplicate_loc, CHECK_OK);
@@ -2877,8 +2877,8 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
   }
 
   expression = this->CheckAndRewriteReferenceExpression(
-      expression, lhs_location, MessageTemplate::kInvalidLhsInAssignment,
-      CHECK_OK);
+      expression, lhs_beg_pos, scanner()->location().end_pos,
+      MessageTemplate::kInvalidLhsInAssignment, CHECK_OK);
   expression = this->MarkExpressionAsAssigned(expression);
 
   Token::Value op = Next();  // Get assignment operator.
@@ -3094,11 +3094,11 @@ ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier,
   } else if (Token::IsCountOp(op)) {
     BindingPatternUnexpectedToken(classifier);
     op = Next();
-    Scanner::Location lhs_location = scanner()->peek_location();
+    int beg_pos = peek_position();
     ExpressionT expression = this->ParseUnaryExpression(classifier, CHECK_OK);
     expression = this->CheckAndRewriteReferenceExpression(
-        expression, lhs_location, MessageTemplate::kInvalidLhsInPrefixOp,
-        CHECK_OK);
+        expression, beg_pos, scanner()->location().end_pos,
+        MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK);
     this->MarkExpressionAsAssigned(expression);
 
     return factory()->NewCountOperation(op,
@@ -3119,7 +3119,7 @@ ParserBase<Traits>::ParsePostfixExpression(ExpressionClassifier* classifier,
   // PostfixExpression ::
   //   LeftHandSideExpression ('++' | '--')?
 
-  Scanner::Location lhs_location = scanner()->peek_location();
+  int lhs_beg_pos = peek_position();
   ExpressionT expression =
       this->ParseLeftHandSideExpression(classifier, CHECK_OK);
   if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
@@ -3127,8 +3127,8 @@ ParserBase<Traits>::ParsePostfixExpression(ExpressionClassifier* classifier,
     BindingPatternUnexpectedToken(classifier);
 
     expression = this->CheckAndRewriteReferenceExpression(
-        expression, lhs_location, MessageTemplate::kInvalidLhsInPostfixOp,
-        CHECK_OK);
+        expression, lhs_beg_pos, scanner()->location().end_pos,
+        MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK);
     expression = this->MarkExpressionAsAssigned(expression);
 
     Token::Value next = Next();
@@ -3936,8 +3936,9 @@ ParserBase<Traits>::ParseTemplateLiteral(ExpressionT tag, int start,
 template <typename Traits>
 typename ParserBase<Traits>::ExpressionT
 ParserBase<Traits>::CheckAndRewriteReferenceExpression(
-    ExpressionT expression, Scanner::Location location,
+    ExpressionT expression, int beg_pos, int end_pos,
     MessageTemplate::Template message, bool* ok) {
+  Scanner::Location location(beg_pos, end_pos);
   if (this->IsIdentifier(expression)) {
     if (is_strict(language_mode()) &&
         this->IsEvalOrArguments(this->AsIdentifier(expression))) {
diff --git a/test/message/new-target-assignment.js b/test/message/new-target-assignment.js
new file mode 100644 (file)
index 0000000..f257d1a
--- /dev/null
@@ -0,0 +1,7 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Flags: --harmony-new-target
+
+function f() { new.target = 5 }
diff --git a/test/message/new-target-assignment.out b/test/message/new-target-assignment.out
new file mode 100644 (file)
index 0000000..5431bd0
--- /dev/null
@@ -0,0 +1,4 @@
+*%(basename)s:7: ReferenceError: Invalid left-hand side in assignment
+function f() { new.target = 5 }
+               ^^^^^^^^^^
+ReferenceError: Invalid left-hand side in assignment
diff --git a/test/message/new-target-for-loop.js b/test/message/new-target-for-loop.js
new file mode 100644 (file)
index 0000000..76a8eec
--- /dev/null
@@ -0,0 +1,7 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Flags: --harmony-new-target
+
+function f() { for (new.target in {}); }
diff --git a/test/message/new-target-for-loop.out b/test/message/new-target-for-loop.out
new file mode 100644 (file)
index 0000000..30f1f75
--- /dev/null
@@ -0,0 +1,4 @@
+*%(basename)s:7: ReferenceError: Invalid left-hand side in for-loop
+function f() { for (new.target in {}); }
+                    ^^^^^^^^^^
+ReferenceError: Invalid left-hand side in for-loop
diff --git a/test/message/new-target-postfix-op.js b/test/message/new-target-postfix-op.js
new file mode 100644 (file)
index 0000000..573f3ae
--- /dev/null
@@ -0,0 +1,7 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Flags: --harmony-new-target
+
+function f() { new.target++ }
diff --git a/test/message/new-target-postfix-op.out b/test/message/new-target-postfix-op.out
new file mode 100644 (file)
index 0000000..17f2081
--- /dev/null
@@ -0,0 +1,4 @@
+*%(basename)s:7: ReferenceError: Invalid left-hand side expression in postfix operation
+function f() { new.target++ }
+               ^^^^^^^^^^
+ReferenceError: Invalid left-hand side expression in postfix operation
diff --git a/test/message/new-target-prefix-op.js b/test/message/new-target-prefix-op.js
new file mode 100644 (file)
index 0000000..ad2f98a
--- /dev/null
@@ -0,0 +1,7 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Flags: --harmony-new-target
+
+function f() { ++new.target }
diff --git a/test/message/new-target-prefix-op.out b/test/message/new-target-prefix-op.out
new file mode 100644 (file)
index 0000000..19c847f
--- /dev/null
@@ -0,0 +1,4 @@
+*%(basename)s:7: ReferenceError: Invalid left-hand side expression in prefix operation
+function f() { ++new.target }
+                 ^^^^^^^^^^
+ReferenceError: Invalid left-hand side expression in prefix operation
index fa5b372..a1f020b 100644 (file)
   function f6() { with ({'new.target': 42}) return new.target }
   assertSame(f6, new f6);
 })();
+
+
+(function TestEarlyErrors() {
+  assertThrows(function() { Function("new.target = 42"); }, ReferenceError);
+  assertThrows(function() { Function("var foo = 1; new.target = foo = 42"); }, ReferenceError);
+  assertThrows(function() { Function("var foo = 1; foo = new.target = 42"); }, ReferenceError);
+  assertThrows(function() { Function("new.target--"); }, ReferenceError);
+  assertThrows(function() { Function("--new.target"); }, ReferenceError);
+  assertThrows(function() { Function("(new.target)++"); }, ReferenceError);
+  assertThrows(function() { Function("++(new.target)"); }, ReferenceError);
+  assertThrows(function() { Function("for (new.target of {});"); }, ReferenceError);
+})();