Commit 1ea118d5 authored by machenbach's avatar machenbach Committed by Commit bot

Revert of Revert of [strong] checking of this & super in constructors...

Revert of Revert of [strong] checking of this & super in constructors (patchset #1 id:1 of https://codereview.chromium.org/1105453002/)

Reason for revert:
Was an infrastructure problem.

Original issue's description:
> Revert of [strong] checking of this & super in constructors (patchset #7 id:110001 of https://codereview.chromium.org/1024063002/)
>
> Reason for revert:
> [Sheriff] Breaks mac gc stress:
> http://build.chromium.org/p/client.v8/builders/V8%20Mac%20GC%20Stress/builds/1024
>
> Original issue's description:
> > [strong] checking of this & super in constructors
> >
> > R=dslomov@chromium.org, marja@chromium.org
> > BUG=v8:3956
> > LOG=N
> >
> > Enforces for constructors that
> > - the only use of 'super' is the super constructor call
> > - the only use of 'this' is a property assignment
> > - both of these must happen at the top-level of the body
> > - 'this' may only be assigned after the 'super' call
> > - 'return' may only be used after the last assignment to 'this'
> >
> > Not yet working for arrow functions (there might be deeper bugs with those).
> >
> > Committed: https://crrev.com/580d66bcda66220d2f3062ac58daf925436df74c
> > Cr-Commit-Position: refs/heads/master@{#27977}
>
> TBR=dslomov@chromium.org,marja@chromium.org,conradw@chromium.org,rossberg@chromium.org
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=v8:3956

TBR=dslomov@chromium.org,marja@chromium.org,conradw@chromium.org,rossberg@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:3956

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

Cr-Commit-Position: refs/heads/master@{#28001}
parent 2631c9f5
......@@ -143,9 +143,11 @@ var kMessages = {
strong_unbound_global: ["In strong mode, using an undeclared global variable '", "%0", "' is not allowed"],
strong_super_call_missing: ["In strong mode, invoking the super constructor in a subclass is required"],
strong_super_call_duplicate: ["In strong mode, invoking the super constructor multiple times is deprecated"],
strong_super_call_nested: ["In strong mode, invoking the super constructor nested inside another statement or expression is deprecated"],
strong_super_call_misplaced: ["In strong mode, the super constructor must be invoked before any assignment to 'this'"],
strong_constructor_super: ["In strong mode, 'super' can only be used to invoke the super constructor, and cannot be nested inside another statement or expression"],
strong_constructor_this: ["In strong mode, 'this' can only be used to initialize properties, and cannot be nested inside another statement or expression"],
strong_constructor_return_value: ["In strong mode, returning a value from a constructor is deprecated"],
strong_constructor_return_misplaced: ["In strong mode, returning from a constructor before its super constructor invocation is deprecated"],
strong_constructor_return_misplaced: ["In strong mode, returning from a constructor before its super constructor invocation or all assignments to 'this' is deprecated"],
sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"],
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
......
......@@ -1210,25 +1210,28 @@ void* Parser::ParseStatementList(ZoneList<Statement*>* body, int end_token,
directive_prologue = false;
}
Token::Value token = peek();
Scanner::Location token_loc = scanner()->peek_location();
Scanner::Location old_super_loc = function_state_->super_call_location();
Scanner::Location old_this_loc = function_state_->this_location();
Scanner::Location old_super_loc = function_state_->super_location();
Statement* stat = ParseStatementListItem(CHECK_OK);
Scanner::Location super_loc = function_state_->super_call_location();
if (is_strong(language_mode()) &&
i::IsConstructor(function_state_->kind()) &&
!old_super_loc.IsValid() && super_loc.IsValid() &&
token != Token::SUPER) {
// TODO(rossberg): This is more permissive than spec'ed, it allows e.g.
// super(), 1;
// super() + "";
// super() = 0;
// That should still be safe, though, thanks to left-to-right evaluation.
// The proper check would be difficult to implement in the preparser.
ReportMessageAt(super_loc, "strong_super_call_nested");
*ok = false;
return NULL;
scope_->is_function_scope() &&
i::IsConstructor(function_state_->kind())) {
Scanner::Location this_loc = function_state_->this_location();
Scanner::Location super_loc = function_state_->super_location();
if (this_loc.beg_pos != old_this_loc.beg_pos &&
this_loc.beg_pos != token_loc.beg_pos) {
ReportMessageAt(this_loc, "strong_constructor_this");
*ok = false;
return nullptr;
}
if (super_loc.beg_pos != old_super_loc.beg_pos &&
super_loc.beg_pos != token_loc.beg_pos) {
ReportMessageAt(super_loc, "strong_constructor_super");
*ok = false;
return nullptr;
}
}
if (stat == NULL || stat->IsEmpty()) {
......@@ -2593,6 +2596,8 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
// ExpressionStatement[Yield] :
// [lookahead ∉ {{, function, class, let [}] Expression[In, ?Yield] ;
int pos = peek_position();
switch (peek()) {
case Token::FUNCTION:
case Token::LBRACE:
......@@ -2602,6 +2607,37 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
*ok = false;
return nullptr;
case Token::THIS:
case Token::SUPER:
if (is_strong(language_mode()) &&
i::IsConstructor(function_state_->kind())) {
bool is_this = peek() == Token::THIS;
Expression* expr;
if (is_this) {
expr = ParseStrongInitializationExpression(CHECK_OK);
} else {
expr = ParseStrongSuperCallExpression(CHECK_OK);
}
switch (peek()) {
case Token::SEMICOLON:
Consume(Token::SEMICOLON);
break;
case Token::RBRACE:
case Token::EOS:
break;
default:
if (!scanner()->HasAnyLineTerminatorBeforeNext()) {
ReportMessageAt(function_state_->this_location(),
is_this ? "strong_constructor_this"
: "strong_constructor_super");
*ok = false;
return nullptr;
}
}
return factory()->NewExpressionStatement(expr, pos);
}
break;
// TODO(arv): Handle `let [`
// https://code.google.com/p/v8/issues/detail?id=3847
......@@ -2609,7 +2645,6 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
break;
}
int pos = peek_position();
bool starts_with_idenfifier = peek_any_identifier();
Expression* expr = ParseExpression(true, CHECK_OK);
if (peek() == Token::COLON && starts_with_idenfifier && expr != NULL &&
......@@ -4003,7 +4038,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
parenthesized_function_ = false; // The bit was set for this function only.
if (is_lazily_parsed) {
SkipLazyFunctionBody(function_name, &materialized_literal_count,
SkipLazyFunctionBody(&materialized_literal_count,
&expected_property_count, CHECK_OK);
} else {
body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op,
......@@ -4011,6 +4046,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
materialized_literal_count = function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
handler_count = function_state.handler_count();
if (is_strong(language_mode()) && IsSubclassConstructor(kind)) {
if (!function_state.super_location().IsValid()) {
ReportMessageAt(function_name_location,
"strong_super_call_missing", kReferenceError);
*ok = false;
return nullptr;
}
}
}
// Validate name and parameter names. We can do this only after parsing the
......@@ -4025,18 +4069,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
if (is_strict(language_mode())) {
CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
CHECK_OK);
}
if (is_strict(language_mode())) {
CheckConflictingVarDeclarations(scope, CHECK_OK);
}
if (is_strong(language_mode()) && IsSubclassConstructor(kind)) {
if (!function_state.super_call_location().IsValid()) {
ReportMessageAt(function_name_location, "strong_super_call_missing",
kReferenceError);
*ok = false;
return nullptr;
}
}
}
FunctionLiteral::ParameterFlag duplicate_parameters =
......@@ -4060,8 +4094,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
}
void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
int* materialized_literal_count,
void Parser::SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count,
bool* ok) {
if (produce_cached_parse_data()) CHECK(log_);
......
......@@ -773,8 +773,7 @@ class ParserTraits {
bool name_is_strict_reserved, FunctionKind kind,
int function_token_position, FunctionLiteral::FunctionType type,
FunctionLiteral::ArityRestriction arity_restriction, bool* ok);
V8_INLINE void SkipLazyFunctionBody(const AstRawString* name,
int* materialized_literal_count,
V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count, bool* ok);
V8_INLINE ZoneList<Statement*>* ParseEagerFunctionBody(
const AstRawString* name, int pos, Variable* fvar,
......@@ -1025,8 +1024,7 @@ class Parser : public ParserBase<ParserTraits> {
// Skip over a lazy function, either using cached data if we have it, or
// by parsing the function with PreParser. Consumes the ending }.
void SkipLazyFunctionBody(const AstRawString* function_name,
int* materialized_literal_count,
void SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count,
bool* ok);
......@@ -1091,12 +1089,11 @@ const AstRawString* ParserTraits::EmptyIdentifierString() {
}
void ParserTraits::SkipLazyFunctionBody(const AstRawString* function_name,
int* materialized_literal_count,
void ParserTraits::SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count,
bool* ok) {
return parser_->SkipLazyFunctionBody(
function_name, materialized_literal_count, expected_property_count, ok);
materialized_literal_count, expected_property_count, ok);
}
......
......@@ -123,6 +123,15 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction(
if (is_strict(scope_->language_mode())) {
int end_pos = scanner()->location().end_pos;
CheckStrictOctalLiteral(start_position, end_pos, &ok);
if (!ok) return kPreParseSuccess;
if (is_strong(scope_->language_mode()) && IsSubclassConstructor(kind)) {
if (!function_state.super_location().IsValid()) {
ReportMessageAt(Scanner::Location(start_position, start_position + 1),
"strong_super_call_missing", kReferenceError);
return kPreParseSuccess;
}
}
}
}
return kPreParseSuccess;
......@@ -196,19 +205,31 @@ void PreParser::ParseStatementList(int end_token, bool* ok) {
if (directive_prologue && peek() != Token::STRING) {
directive_prologue = false;
}
Token::Value token = peek();
Scanner::Location old_super_loc = function_state_->super_call_location();
Scanner::Location token_loc = scanner()->peek_location();
Scanner::Location old_this_loc = function_state_->this_location();
Scanner::Location old_super_loc = function_state_->super_location();
Statement statement = ParseStatementListItem(ok);
if (!*ok) return;
Scanner::Location super_loc = function_state_->super_call_location();
if (is_strong(language_mode()) &&
i::IsConstructor(function_state_->kind()) &&
!old_super_loc.IsValid() && super_loc.IsValid() &&
token != Token::SUPER) {
ReportMessageAt(super_loc, "strong_super_call_nested");
*ok = false;
return;
scope_->is_function_scope() &&
i::IsConstructor(function_state_->kind())) {
Scanner::Location this_loc = function_state_->this_location();
Scanner::Location super_loc = function_state_->super_location();
if (this_loc.beg_pos != old_this_loc.beg_pos &&
this_loc.beg_pos != token_loc.beg_pos) {
ReportMessageAt(this_loc, "strong_constructor_this");
*ok = false;
return;
}
if (super_loc.beg_pos != old_super_loc.beg_pos &&
super_loc.beg_pos != token_loc.beg_pos) {
ReportMessageAt(super_loc, "strong_constructor_super");
*ok = false;
return;
}
}
if (directive_prologue) {
if (statement.IsUseStrictLiteral()) {
scope_->SetLanguageMode(
......@@ -532,6 +553,37 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
*ok = false;
return Statement::Default();
case Token::THIS:
case Token::SUPER:
if (is_strong(language_mode()) &&
i::IsConstructor(function_state_->kind())) {
bool is_this = peek() == Token::THIS;
Expression expr = Expression::Default();
if (is_this) {
expr = ParseStrongInitializationExpression(CHECK_OK);
} else {
expr = ParseStrongSuperCallExpression(CHECK_OK);
}
switch (peek()) {
case Token::SEMICOLON:
Consume(Token::SEMICOLON);
break;
case Token::RBRACE:
case Token::EOS:
break;
default:
if (!scanner()->HasAnyLineTerminatorBeforeNext()) {
ReportMessageAt(function_state_->this_location(),
is_this ? "strong_constructor_this"
: "strong_constructor_super");
*ok = false;
return Statement::Default();
}
}
return Statement::ExpressionStatement(expr);
}
break;
// TODO(arv): Handle `let [`
// https://code.google.com/p/v8/issues/detail?id=3847
......@@ -972,7 +1024,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
}
if (is_strong(language_mode()) && IsSubclassConstructor(kind)) {
if (!function_state.super_call_location().IsValid()) {
if (!function_state.super_location().IsValid()) {
ReportMessageAt(function_name_location, "strong_super_call_missing",
kReferenceError);
*ok = false;
......
......@@ -233,16 +233,18 @@ class ParserBase : public Traits {
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
Scanner::Location this_location() const { return this_location_; }
Scanner::Location super_location() const { return super_location_; }
Scanner::Location return_location() const { return return_location_; }
Scanner::Location super_call_location() const {
return super_call_location_;
void set_this_location(Scanner::Location location) {
this_location_ = location;
}
void set_super_location(Scanner::Location location) {
super_location_ = location;
}
void set_return_location(Scanner::Location location) {
return_location_ = location;
}
void set_super_call_location(Scanner::Location location) {
super_call_location_ = location;
}
bool is_generator() const { return IsGeneratorFunction(kind_); }
......@@ -274,11 +276,14 @@ class ParserBase : public Traits {
// Properties count estimation.
int expected_property_count_;
// Location of most recent use of 'this' (invalid if none).
Scanner::Location this_location_;
// Location of most recent 'return' statement (invalid if none).
Scanner::Location return_location_;
// Location of call to the "super" constructor (invalid if none).
Scanner::Location super_call_location_;
Scanner::Location super_location_;
FunctionKind kind_;
// For generators, this variable may hold the generator object. It variable
......@@ -627,6 +632,8 @@ class ParserBase : public Traits {
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool* ok);
void AddTemplateExpression(ExpressionT);
ExpressionT ParseSuperExpression(bool is_new, bool* ok);
ExpressionT ParseStrongInitializationExpression(bool* ok);
ExpressionT ParseStrongSuperCallExpression(bool* ok);
void ParseFormalParameter(FormalParameterScopeT* scope,
FormalParameterErrorLocations* locs, bool is_rest,
......@@ -1548,8 +1555,7 @@ class PreParserTraits {
return PreParserExpressionList();
}
V8_INLINE void SkipLazyFunctionBody(PreParserIdentifier function_name,
int* materialized_literal_count,
V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count, bool* ok) {
UNREACHABLE();
}
......@@ -1732,8 +1738,7 @@ class PreParser : public ParserBase<PreParserTraits> {
Expression ParseObjectLiteral(bool* ok);
Expression ParseV8Intrinsic(bool* ok);
V8_INLINE void SkipLazyFunctionBody(PreParserIdentifier function_name,
int* materialized_literal_count,
V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count, bool* ok);
V8_INLINE PreParserStatementList
ParseEagerFunctionBody(PreParserIdentifier function_name, int pos,
......@@ -1829,8 +1834,9 @@ ParserBase<Traits>::FunctionState::FunctionState(
: next_materialized_literal_index_(0),
next_handler_index_(0),
expected_property_count_(0),
this_location_(Scanner::Location::invalid()),
return_location_(Scanner::Location::invalid()),
super_call_location_(Scanner::Location::invalid()),
super_location_(Scanner::Location::invalid()),
kind_(kind),
generator_object_variable_(NULL),
function_state_stack_(function_state_stack),
......@@ -2045,6 +2051,15 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
switch (token) {
case Token::THIS: {
Consume(Token::THIS);
if (is_strong(language_mode())) {
// Constructors' usages of 'this' in strong mode are parsed separately.
// TODO(rossberg): this does not work with arrow functions yet.
if (i::IsConstructor(function_state_->kind())) {
ReportMessage("strong_constructor_this");
*ok = false;
break;
}
}
scope_->RecordThisUsage();
result = this->ThisExpression(scope_, factory(), beg_pos);
break;
......@@ -2117,7 +2132,7 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
case Token::CLASS: {
Consume(Token::CLASS);
if (!allow_harmony_sloppy() && is_sloppy(language_mode())) {
ReportMessage("sloppy_lexical", NULL);
ReportMessage("sloppy_lexical");
*ok = false;
break;
}
......@@ -3003,11 +3018,143 @@ ParserBase<Traits>::ParseMemberExpression(bool* ok) {
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseStrongInitializationExpression(bool* ok) {
// InitializationExpression :: (strong mode)
// 'this' '.' IdentifierName '=' AssignmentExpression
// 'this' '[' Expression ']' '=' AssignmentExpression
if (fni_ != NULL) fni_->Enter();
Consume(Token::THIS);
int pos = position();
function_state_->set_this_location(scanner()->location());
scope_->RecordThisUsage();
ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos);
ExpressionT left = this->EmptyExpression();
switch (peek()) {
case Token::LBRACK: {
Consume(Token::LBRACK);
int pos = position();
ExpressionT index = this->ParseExpression(true, CHECK_OK);
left = factory()->NewProperty(this_expr, index, pos);
if (fni_ != NULL) {
this->PushPropertyName(fni_, index);
}
Expect(Token::RBRACK, CHECK_OK);
break;
}
case Token::PERIOD: {
Consume(Token::PERIOD);
int pos = position();
IdentifierT name = ParseIdentifierName(CHECK_OK);
left = factory()->NewProperty(
this_expr, factory()->NewStringLiteral(name, pos), pos);
if (fni_ != NULL) {
this->PushLiteralName(fni_, name);
}
break;
}
default:
ReportMessage("strong_constructor_this");
*ok = false;
return this->EmptyExpression();
}
if (peek() != Token::ASSIGN) {
ReportMessageAt(function_state_->this_location(),
"strong_constructor_this");
*ok = false;
return this->EmptyExpression();
}
Consume(Token::ASSIGN);
left = this->MarkExpressionAsAssigned(left);
ExpressionT right = this->ParseAssignmentExpression(true, CHECK_OK);
this->CheckAssigningFunctionLiteralToProperty(left, right);
function_state_->AddProperty();
if (fni_ != NULL) {
// Check if the right hand side is a call to avoid inferring a
// name if we're dealing with "this.a = function(){...}();"-like
// expression.
if (!right->IsCall() && !right->IsCallNew()) {
fni_->Infer();
} else {
fni_->RemoveLastFunction();
}
fni_->Leave();
}
if (function_state_->return_location().IsValid()) {
ReportMessageAt(function_state_->return_location(),
"strong_constructor_return_misplaced");
*ok = false;
return this->EmptyExpression();
}
return factory()->NewAssignment(Token::ASSIGN, left, right, pos);
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseStrongSuperCallExpression(bool* ok) {
// SuperCallExpression :: (strong mode)
// 'super' '(' ExpressionList ')'
Consume(Token::SUPER);
int pos = position();
Scanner::Location super_loc = scanner()->location();
ExpressionT expr = this->SuperReference(scope_, factory());
if (peek() != Token::LPAREN) {
ReportMessage("strong_constructor_super");
*ok = false;
return this->EmptyExpression();
}
Scanner::Location spread_pos;
typename Traits::Type::ExpressionList args =
ParseArguments(&spread_pos, CHECK_OK);
// TODO(rossberg): This doesn't work with arrow functions yet.
if (!IsSubclassConstructor(function_state_->kind())) {
ReportMessage("unexpected_super");
*ok = false;
return this->EmptyExpression();
} else if (function_state_->super_location().IsValid()) {
ReportMessageAt(scanner()->location(), "strong_super_call_duplicate");
*ok = false;
return this->EmptyExpression();
} else if (function_state_->this_location().IsValid()) {
ReportMessageAt(scanner()->location(), "strong_super_call_misplaced");
*ok = false;
return this->EmptyExpression();
} else if (function_state_->return_location().IsValid()) {
ReportMessageAt(function_state_->return_location(),
"strong_constructor_return_misplaced");
*ok = false;
return this->EmptyExpression();
}
function_state_->set_super_location(super_loc);
if (spread_pos.IsValid()) {
args = Traits::PrepareSpreadArguments(args);
return Traits::SpreadCall(expr, args, pos);
} else {
return factory()->NewCall(expr, args, pos);
}
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseSuperExpression(bool is_new, bool* ok) {
Expect(Token::SUPER, CHECK_OK);
// TODO(wingo): Does this actually work with lazily compiled arrows?
FunctionState* function_state = function_state_;
while (IsArrowFunction(function_state->kind())) {
function_state = function_state->outer();
......@@ -3025,18 +3172,12 @@ ParserBase<Traits>::ParseSuperExpression(bool is_new, bool* ok) {
// super() is only allowed in derived constructor
if (!is_new && peek() == Token::LPAREN && IsSubclassConstructor(kind)) {
if (is_strong(language_mode())) {
if (function_state->super_call_location().IsValid()) {
ReportMessageAt(scanner()->location(), "strong_super_call_duplicate");
*ok = false;
return this->EmptyExpression();
} else if (function_state->return_location().IsValid()) {
ReportMessageAt(function_state->return_location(),
"strong_constructor_return_misplaced");
*ok = false;
return this->EmptyExpression();
}
// Super calls in strong mode are parsed separately.
ReportMessageAt(scanner()->location(), "strong_constructor_super");
*ok = false;
return this->EmptyExpression();
}
function_state->set_super_call_location(scanner()->location());
function_state->set_super_location(scanner()->location());
return this->SuperReference(scope_, factory());
}
}
......@@ -3233,8 +3374,7 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
(mode() == PARSE_LAZILY && scope_->AllowsLazyCompilation());
if (is_lazily_parsed) {
body = this->NewStatementList(0, zone());
this->SkipLazyFunctionBody(this->EmptyIdentifier(),
&materialized_literal_count,
this->SkipLazyFunctionBody(&materialized_literal_count,
&expected_property_count, CHECK_OK);
} else {
body = this->ParseEagerFunctionBody(
......@@ -3256,7 +3396,7 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
expected_property_count = function_state.expected_property_count();
handler_count = function_state.handler_count();
}
super_loc = function_state.super_call_location();
super_loc = function_state.super_location();
scope->set_end_position(scanner()->location().end_pos);
......@@ -3285,7 +3425,7 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
scope->start_position());
function_literal->set_function_token_position(scope->start_position());
if (super_loc.IsValid()) function_state_->set_super_call_location(super_loc);
if (super_loc.IsValid()) function_state_->set_super_location(super_loc);
if (fni_ != NULL) this->InferFunctionName(fni_, function_literal);
......
......@@ -5866,33 +5866,109 @@ TEST(StrongForIn) {
}
TEST(StrongSuperCalls) {
TEST(StrongConstructorThis) {
const char* sloppy_context_data[][2] = {{"", ""}, {NULL}};
const char* strict_context_data[][2] = {{"'use strict';", ""}, {NULL}};
const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}};
const char* data[] = {
const char* error_data[] = {
"class C { constructor() { this; } }",
"class C { constructor() { this.a; } }",
"class C { constructor() { this['a']; } }",
"class C { constructor() { (this); } }",
"class C { constructor() { this(); } }",
// TODO(rossberg): arrow functions not handled yet.
// "class C { constructor() { () => this; } }",
"class C { constructor() { this.a = 0, 0; } }",
"class C { constructor() { (this.a = 0); } }",
// "class C { constructor() { (() => this.a = 0)(); } }",
"class C { constructor() { { this.a = 0; } } }",
"class C { constructor() { if (1) this.a = 0; } }",
"class C { constructor() { label: this.a = 0; } }",
"class C { constructor() { this.a = this.b; } }",
"class C { constructor() { this.a = {b: 1}; this.a.b } }",
"class C { constructor() { this.a = {b: 1}; this.a.b = 0 } }",
"class C { constructor() { this.a = function(){}; this.a() } }",
NULL};
const char* success_data[] = {
"class C { constructor() { this.a = 0; } }",
"class C { constructor() { label: 0; this.a = 0; this.b = 6; } }",
NULL};
static const ParserFlag always_flags[] = {
kAllowStrongMode, kAllowHarmonyClasses, kAllowHarmonyObjectLiterals,
kAllowHarmonyArrowFunctions
};
RunParserSyncTest(sloppy_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(sloppy_context_data, success_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(StrongConstructorSuper) {
const char* sloppy_context_data[][2] = {{"", ""}, {NULL}};
const char* strict_context_data[][2] = {{"'use strict';", ""}, {NULL}};
const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}};
const char* error_data[] = {
"class C extends Object { constructor() {} }",
"class C extends Object { constructor() { super.a; } }",
"class C extends Object { constructor() { super['a']; } }",
"class C extends Object { constructor() { super.a = 0; } }",
"class C extends Object { constructor() { (super.a); } }",
// TODO(rossberg): arrow functions do not handle super yet.
// "class C extends Object { constructor() { () => super.a; } }",
"class C extends Object { constructor() { super(), 0; } }",
"class C extends Object { constructor() { (super()); } }",
"class C extends Object { constructor() { (() => super())(); } }",
// "class C extends Object { constructor() { (() => super())(); } }",
"class C extends Object { constructor() { { super(); } } }",
"class C extends Object { constructor() { if (1) super(); } }",
"class C extends Object { constructor() { label: super(); } }",
"class C extends Object { constructor() { super(), super(); } }",
"class C extends Object { constructor() { super(); super(); } }",
"class C extends Object { constructor() { super(); (super()); } }",
"class C extends Object { constructor() { super(); { super() } } }",
"class C extends Object { constructor() { this.a = 0, super(); } }",
"class C extends Object { constructor() { this.a = 0; super(); } }",
"class C extends Object { constructor() { super(this.a = 0); } }",
"class C extends Object { constructor() { super().a; } }",
NULL};
const char* success_data[] = {
"class C extends Object { constructor() { super(); } }",
"class C extends Object { constructor() { label: 66; super(); } }",
"class C extends Object { constructor() { super(3); this.x = 0; } }",
"class C extends Object { constructor() { 3; super(3); this.x = 0; } }",
NULL};
static const ParserFlag always_flags[] = {
kAllowStrongMode, kAllowHarmonyClasses, kAllowHarmonyObjectLiterals,
kAllowHarmonyArrowFunctions
};
RunParserSyncTest(sloppy_context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(strict_context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(strong_context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(sloppy_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(sloppy_context_data, success_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
......@@ -5901,24 +5977,45 @@ TEST(StrongConstructorReturns) {
const char* strict_context_data[][2] = {{"'use strict';", ""}, {NULL}};
const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}};
const char* data[] = {
const char* error_data[] = {
"class C extends Object { constructor() { super(); return {}; } }",
"class C extends Object { constructor() { super(); { return {}; } } }",
"class C extends Object { constructor() { super(); if (1) return {}; } }",
"class C extends Object { constructor() { return; super(); } }",
"class C extends Object { constructor() { { return; } super(); } }",
"class C extends Object { constructor() { if (0) return; super(); } }",
"class C { constructor() { return; this.a = 0; } }",
"class C { constructor() { { return; } this.a = 0; } }",
"class C { constructor() { if (0) return; this.a = 0; } }",
"class C { constructor() { this.a = 0; if (0) return; this.b = 0; } }",
NULL};
const char* success_data[] = {
"class C extends Object { constructor() { super(); return; } }",
"class C extends Object { constructor() { super(); { return } } }",
"class C extends Object { constructor() { super(); if (1) return; } }",
"class C { constructor() { this.a = 0; return; } }",
"class C { constructor() { this.a = 0; { return; } } }",
"class C { constructor() { this.a = 0; if (0) return; 65; } }",
"class C extends Array { constructor() { super(); this.a = 9; return } }",
NULL};
static const ParserFlag always_flags[] = {
kAllowStrongMode, kAllowHarmonyClasses, kAllowHarmonyObjectLiterals
};
RunParserSyncTest(sloppy_context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(strict_context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(strong_context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(sloppy_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(sloppy_context_data, success_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
......
......@@ -28,16 +28,32 @@ function constructor(body) {
"(class extends Object { constructor() { " + body + " } })";
}
(function NoSuperExceptCall() {
assertSyntaxError(constructor("super.a;"));
assertSyntaxError(constructor("super['a'];"));
assertSyntaxError(constructor("super.f();"));
assertSyntaxError(constructor("super.a;"));
assertSyntaxError(constructor("{ super.a }"));
assertSyntaxError(constructor("if (0) super.a;"));
// TODO(rossberg): arrow functions do not handle 'super' yet.
// assertSyntaxError(constructor("() => super.a;"));
// assertSyntaxError(constructor("() => () => super.a;"));
// assertSyntaxError(constructor("() => { () => if (0) { super.a; } }"));
})();
(function NoMissingSuper() {
assertReferenceError(constructor(""));
assertReferenceError(constructor("1"));
})();
(function NoNestedSuper() {
assertSyntaxError(constructor("super(), 0;"));
assertSyntaxError(constructor("(super());"));
assertSyntaxError(constructor("super().a;"));
assertSyntaxError(constructor("(() => super())();"));
assertSyntaxError(constructor("{ super(); }"));
assertSyntaxError(constructor("if (1) super();"));
assertSyntaxError(constructor("label: super();"));
})();
(function NoDuplicateSuper() {
......@@ -48,9 +64,19 @@ function constructor(body) {
assertSyntaxError(constructor("super(); (() => super())();"));
})();
(function NoSuperAfterThis() {
assertSyntaxError(constructor("this.a = 0, super();"));
assertSyntaxError(constructor("this.a = 0; super();"));
assertSyntaxError(constructor("this.a = 0; super(); this.b = 0;"));
assertSyntaxError(constructor("this.a = 0; (super());"));
assertSyntaxError(constructor("super(this.a = 0);"));
})();
(function NoReturnValue() {
assertSyntaxError(constructor("return {};"));
assertSyntaxError(constructor("return undefined;"));
assertSyntaxError(constructor("return this;"));
assertSyntaxError(constructor("return this.a = 0;"));
assertSyntaxError(constructor("{ return {}; }"));
assertSyntaxError(constructor("if (1) return {};"));
})();
......@@ -60,3 +86,34 @@ function constructor(body) {
assertSyntaxError(constructor("if (0) return; super();"));
assertSyntaxError(constructor("{ return; } super();"));
})();
(function NoReturnBeforeThis() {
assertSyntaxError(constructor("return; this.a = 0;"));
assertSyntaxError(constructor("if (0) return; this.a = 0;"));
assertSyntaxError(constructor("{ return; } this.a = 0;"));
})();
(function NoThisExceptInitialization() {
assertSyntaxError(constructor("this;"));
assertSyntaxError(constructor("this.a;"));
assertSyntaxError(constructor("this['a'];"));
assertSyntaxError(constructor("this();"));
assertSyntaxError(constructor("this.a();"));
assertSyntaxError(constructor("this.a.b = 0;"));
assertSyntaxError(constructor("{ this }"));
assertSyntaxError(constructor("if (0) this;"));
// TODO(rossberg): this does not handle arrow functions yet.
// assertSyntaxError(constructor("() => this;"));
// assertSyntaxError(constructor("() => () => this;"));
// assertSyntaxError(constructor("() => { () => if (0) { this; } }"));
})();
(function NoNestedThis() {
assertSyntaxError(constructor("(this.a = 0);"));
assertSyntaxError(constructor("{ this.a = 0; }"));
assertSyntaxError(constructor("if (0) this.a = 0;"));
// TODO(rossberg): this does not handle arrow functions yet.
// assertSyntaxError(constructor("() => this.a = 0;"));
// assertSyntaxError(constructor("() => { this.a = 0; }"));
assertSyntaxError(constructor("label: this.a = 0;"));
})();
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