[es6] Make assignment to new.target an early ReferenceError

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}
This commit is contained in:
adamk 2015-08-13 11:06:04 -07:00 committed by Commit bot
parent 316b1e758b
commit ef52836cd8
13 changed files with 89 additions and 20 deletions

View File

@ -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.

View 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);
}
}
}

View 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);

View 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))) {

View File

@ -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 }

View File

@ -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

View File

@ -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 {}); }

View File

@ -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

View File

@ -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++ }

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -384,3 +384,15 @@
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);
})();