[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). Review URL: https://codereview.chromium.org/1024063002 Cr-Commit-Position: refs/heads/master@{#27977}
This commit is contained in:
parent
3099c34851
commit
580d66bcda
@ -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_);
|
||||
|
11
src/parser.h
11
src/parser.h
@ -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;
|
||||
|
194
src/preparser.h
194
src/preparser.h
@ -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[] = {
|
||||
"class C extends Object { constructor() {} }",
|
||||
"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() { super(), super(); } }",
|
||||
"class C extends Object { constructor() { super(); super(); } }",
|
||||
"class C extends Object { constructor() { super(); (super()); } }",
|
||||
"class C extends Object { constructor() { super(); { super() } } }",
|
||||
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, 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));
|
||||
}
|
||||
|
||||
|
||||
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() { 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, 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;"));
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user