Commit 26ffd5bf authored by Caitlin Potter's avatar Caitlin Potter Committed by Commit Bot

[parser] report early error when assigning to a tagged template call

Previously, Function("++f`...`) would not throw an exception until the
created function was called. Now, it throws an early ReferenceError.

This change matches the behaviour in JavaScriptCore and SpiderMonkey.

Ordinary calls such as Function("++f()") are still thrown at runtime,
also compatible with JavaScriptCore and SpiderMonkey.

BUG=v8:4480, v8:6910
R=marja@chromium.org, littledan@chromium.org

Change-Id: If31c6d360a0464744eff5d8dd377ebff184ae00e
Reviewed-on: https://chromium-review.googlesource.com/712794
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48553}
parent d953b2ab
......@@ -1662,6 +1662,10 @@ class Call final : public Expression {
return IsPossiblyEvalField::decode(bit_field_);
}
bool is_tagged_template() const {
return IsTaggedTemplateField::decode(bit_field_);
}
bool only_last_arg_is_spread() {
return !arguments_->is_empty() && arguments_->last()->IsSpread();
}
......@@ -1685,6 +1689,8 @@ class Call final : public Expression {
// Helpers to determine how to handle the call.
CallType GetCallType() const;
enum class TaggedTemplateTag { kTrue };
private:
friend class AstNodeFactory;
......@@ -1694,11 +1700,21 @@ class Call final : public Expression {
expression_(expression),
arguments_(arguments) {
bit_field_ |=
IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL);
IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL) |
IsTaggedTemplateField::encode(false);
}
Call(Expression* expression, ZoneList<Expression*>* arguments, int pos,
TaggedTemplateTag tag)
: Expression(pos, kCall), expression_(expression), arguments_(arguments) {
bit_field_ |= IsPossiblyEvalField::encode(false) |
IsTaggedTemplateField::encode(true);
}
class IsPossiblyEvalField
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
class IsTaggedTemplateField
: public BitField<bool, IsPossiblyEvalField::kNext, 1> {};
FeedbackSlot ic_slot_;
Expression* expression_;
......@@ -3198,6 +3214,12 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) Call(expression, arguments, pos, possibly_eval);
}
Call* NewTaggedTemplate(Expression* expression,
ZoneList<Expression*>* arguments, int pos) {
return new (zone_)
Call(expression, arguments, pos, Call::TaggedTemplateTag::kTrue);
}
CallNew* NewCallNew(Expression* expression,
ZoneList<Expression*>* arguments,
int pos) {
......
......@@ -4656,7 +4656,7 @@ ParserBase<Impl>::CheckAndRewriteReferenceExpression(
if (expression->IsValidReferenceExpression()) {
return expression;
}
if (expression->IsCall()) {
if (expression->IsCall() && !expression->AsCall()->is_tagged_template()) {
// If it is a call, make it a runtime error for legacy web compatibility.
// Bug: https://bugs.chromium.org/p/v8/issues/detail?id=4480
// Rewrite `expr' to `expr[throw ReferenceError]'.
......
......@@ -3469,7 +3469,7 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
new (zone()) ZoneList<Expression*>(expressions->length() + 1, zone());
call_args->Add(template_object, zone());
call_args->AddAll(*expressions, zone());
return factory()->NewCall(tag, call_args, pos);
return factory()->NewTaggedTemplate(tag, call_args, pos);
}
}
......
......@@ -192,6 +192,17 @@ class PreParserExpression {
ExpressionTypeField::encode(kCallEvalExpression));
}
static PreParserExpression CallTaggedTemplate() {
return PreParserExpression(
TypeField::encode(kExpression) |
ExpressionTypeField::encode(kCallTaggedTemplateExpression));
}
bool is_tagged_template() const {
DCHECK(IsCall());
return ExpressionTypeField::decode(code_) == kCallTaggedTemplateExpression;
}
static PreParserExpression SuperCallReference() {
return PreParserExpression(
TypeField::encode(kExpression) |
......@@ -255,7 +266,13 @@ class PreParserExpression {
bool IsCall() const {
return TypeField::decode(code_) == kExpression &&
(ExpressionTypeField::decode(code_) == kCallExpression ||
ExpressionTypeField::decode(code_) == kCallEvalExpression);
ExpressionTypeField::decode(code_) == kCallEvalExpression ||
ExpressionTypeField::decode(code_) ==
kCallTaggedTemplateExpression);
}
PreParserExpression* AsCall() {
if (IsCall()) return this;
return nullptr;
}
bool IsSuperCallReference() const {
......@@ -304,6 +321,7 @@ class PreParserExpression {
kPropertyExpression,
kCallExpression,
kCallEvalExpression,
kCallTaggedTemplateExpression,
kSuperCallReference,
kAssignment
};
......@@ -624,6 +642,11 @@ class PreParserFactory {
}
return PreParserExpression::Call();
}
PreParserExpression NewTaggedTemplate(
PreParserExpression expression, const PreParserExpressionList& arguments,
int pos) {
return PreParserExpression::CallTaggedTemplate();
}
PreParserExpression NewCallNew(const PreParserExpression& expression,
const PreParserExpressionList& arguments,
int pos) {
......
......@@ -716,3 +716,22 @@ var global = this;
assertEquals(["a", "b"], result);
assertSame(result, f());
})();
(function testTaggedTemplateInvalidAssignmentTargetStrict() {
"use strict";
function f() {}
assertThrows(() => Function("++f`foo`"), ReferenceError);
assertThrows(() => Function("f`foo`++"), ReferenceError);
assertThrows(() => Function("--f`foo`"), ReferenceError);
assertThrows(() => Function("f`foo`--"), ReferenceError);
assertThrows(() => Function("f`foo` = 1"), ReferenceError);
})();
(function testTaggedTemplateInvalidAssignmentTargetSloppy() {
function f() {}
assertThrows(() => Function("++f`foo`"), ReferenceError);
assertThrows(() => Function("f`foo`++"), ReferenceError);
assertThrows(() => Function("--f`foo`"), ReferenceError);
assertThrows(() => Function("f`foo`--"), ReferenceError);
assertThrows(() => Function("f`foo` = 1"), ReferenceError);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment