From 1ea118d592ed5403ab01eb7fb008d41041a868f5 Mon Sep 17 00:00:00 2001 From: machenbach Date: Wed, 22 Apr 2015 04:04:25 -0700 Subject: [PATCH] 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} --- src/messages.js | 6 +- src/parser.cc | 91 +++++++++++----- src/parser.h | 11 +- src/preparser.cc | 72 ++++++++++-- src/preparser.h | 194 ++++++++++++++++++++++++++++----- test/cctest/test-parsing.cc | 129 +++++++++++++++++++--- test/mjsunit/strong/classes.js | 57 ++++++++++ 7 files changed, 469 insertions(+), 91 deletions(-) diff --git a/src/messages.js b/src/messages.js index 04e107dfdc8..f50e8e7f6d4 100644 --- a/src/messages.js +++ b/src/messages.js @@ -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"], diff --git a/src/parser.cc b/src/parser.cc index c73b6d841cd..2961a1aeb8e 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1210,25 +1210,28 @@ void* Parser::ParseStatementList(ZoneList* 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_); diff --git a/src/parser.h b/src/parser.h index 3b3144e0619..b4c628ebb29 100644 --- a/src/parser.h +++ b/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* ParseEagerFunctionBody( const AstRawString* name, int pos, Variable* fvar, @@ -1025,8 +1024,7 @@ class Parser : public ParserBase { // 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); } diff --git a/src/preparser.cc b/src/preparser.cc index 4a8c37def57..caa383a3cc2 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -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; diff --git a/src/preparser.h b/src/preparser.h index 0643c3040a7..0d71d939c01 100644 --- a/src/preparser.h +++ b/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 { 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::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::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::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::ParseMemberExpression(bool* ok) { } +template +typename ParserBase::ExpressionT +ParserBase::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 +typename ParserBase::ExpressionT +ParserBase::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 typename ParserBase::ExpressionT ParserBase::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::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::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::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::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); diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 86e042eced4..ba842587708 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -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)); } diff --git a/test/mjsunit/strong/classes.js b/test/mjsunit/strong/classes.js index 50fe5a18ee9..c329043ae16 100644 --- a/test/mjsunit/strong/classes.js +++ b/test/mjsunit/strong/classes.js @@ -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;")); +})();