Revert of Revert of [strong] checking of this & super in constructors (patchset ...
authormachenbach <machenbach@chromium.org>
Wed, 22 Apr 2015 11:04:25 +0000 (04:04 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 22 Apr 2015 11:04:13 +0000 (11:04 +0000)
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
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
test/cctest/test-parsing.cc
test/mjsunit/strong/classes.js

index 04e107d..f50e8e7 100644 (file)
@@ -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"],
index c73b6d8..2961a1a 100644 (file)
@@ -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_);
index 3b3144e..b4c628e 100644 (file)
@@ -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);
 }
 
 
index 4a8c37d..caa383a 100644 (file)
@@ -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;
index 0643c30..0d71d93 100644 (file)
@@ -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;
       }
@@ -3005,9 +3020,141 @@ 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);
 
index 86e042e..ba84258 100644 (file)
@@ -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));
 }
 
 
index 50fe5a1..c329043 100644 (file)
@@ -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;"));
+})();