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"],
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()) {
// ExpressionStatement[Yield] :
// [lookahead ∉ {{, function, class, let [}] Expression[In, ?Yield] ;
+ int pos = peek_position();
+
switch (peek()) {
case Token::FUNCTION:
case Token::LBRACE:
*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
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 &&
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,
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
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 =
}
-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_);
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,
// 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);
}
-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);
}
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;
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(
*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
}
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;
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_); }
// 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
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,
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();
}
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,
: 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),
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;
case Token::CLASS: {
Consume(Token::CLASS);
if (!allow_harmony_sloppy() && is_sloppy(language_mode())) {
- ReportMessage("sloppy_lexical", NULL);
+ ReportMessage("sloppy_lexical");
*ok = false;
break;
}
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();
// 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());
}
}
(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(
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);
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);
}
-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));
}
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));
}
"(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() {
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 {};"));
})();
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;"));
+})();