[strong] Implement static restrictions on binding/assignment to 'undefined'
authorconradw <conradw@chromium.org>
Fri, 10 Apr 2015 12:04:51 +0000 (05:04 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 10 Apr 2015 12:04:55 +0000 (12:04 +0000)
identifier. Delete unused (and now incorrect) function IsValidStrictVariable.

Implements the strong mode proposal's static restrictions on the use of the
identifier 'undefined'. Assumes these restrictions are intended to be identical
to the restrictions on the use of 'eval' and 'arguments' in strict mode. The
AllowEvalOrArgumentsAsIdentifier enum has been renamed to
AllowRestrictedIdentifiers as logic involving it is now also used for this case.

BUG=v8:3956

LOG=N

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

Cr-Commit-Position: refs/heads/master@{#27744}

src/ast-value-factory.h
src/messages.js
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
test/cctest/test-parsing.cc
test/mjsunit/strong/undefined.js [new file with mode: 0644]

index cb69579..2178585 100644 (file)
@@ -265,6 +265,7 @@ class AstValue : public ZoneObject {
   F(this, "this")                                                          \
   F(throw_iterator_result_not_an_object, "ThrowIteratorResultNotAnObject") \
   F(to_string, "ToString")                                                 \
+  F(undefined, "undefined")                                                \
   F(use_asm, "use asm")                                                    \
   F(use_strong, "use strong")                                              \
   F(use_strict, "use strict")                                              \
index e366b9b..b176ba8 100644 (file)
@@ -167,6 +167,7 @@ var kMessages = {
   strict_caller:                 ["Illegal access to a strict mode caller function."],
   strong_ellision:               ["In strong mode, arrays with holes are deprecated, use maps instead"],
   strong_arguments:              ["In strong mode, 'arguments' is deprecated, use '...args' instead"],
+  strong_undefined:              ["In strong mode, binding or assigning to 'undefined' is deprecated"],
   strong_equal:                  ["In strong mode, '==' and '!=' are deprecated, use '===' and '!==' instead"],
   strong_delete:                 ["In strong mode, 'delete' is deprecated, use maps or sets instead"],
   strong_var:                    ["In strong mode, 'var' is deprecated, use 'let' or 'const' instead"],
index fc4138d..285ddf3 100644 (file)
@@ -465,6 +465,9 @@ bool ParserTraits::IsEvalOrArguments(const AstRawString* identifier) const {
   return IsEval(identifier) || IsArguments(identifier);
 }
 
+bool ParserTraits::IsUndefined(const AstRawString* identifier) const {
+  return identifier == parser_->ast_value_factory()->undefined_string();
+}
 
 bool ParserTraits::IsPrototype(const AstRawString* identifier) const {
   return identifier == parser_->ast_value_factory()->prototype_string();
@@ -1476,6 +1479,10 @@ ZoneList<ImportDeclaration*>* Parser::ParseNamedImports(int pos, bool* ok) {
       *ok = false;
       ReportMessage("strict_eval_arguments");
       return NULL;
+    } else if (is_strong(language_mode()) && IsUndefined(local_name)) {
+      *ok = false;
+      ReportMessage("strong_undefined");
+      return NULL;
     }
     VariableProxy* proxy = NewUnresolved(local_name, IMPORT);
     ImportDeclaration* declaration =
@@ -1525,7 +1532,7 @@ Statement* Parser::ParseImportDeclaration(bool* ok) {
   ImportDeclaration* import_default_declaration = NULL;
   if (tok != Token::MUL && tok != Token::LBRACE) {
     const AstRawString* local_name =
-        ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
+        ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
     VariableProxy* proxy = NewUnresolved(local_name, IMPORT);
     import_default_declaration = factory()->NewImportDeclaration(
         proxy, ast_value_factory()->default_string(), NULL, scope_, pos);
@@ -1540,7 +1547,7 @@ Statement* Parser::ParseImportDeclaration(bool* ok) {
         Consume(Token::MUL);
         ExpectContextualKeyword(CStrVector("as"), CHECK_OK);
         module_instance_binding =
-            ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
+            ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
         // TODO(ES6): Add an appropriate declaration.
         break;
       }
@@ -2033,11 +2040,12 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
   int pos = peek_position();
   Expect(Token::FUNCTION, CHECK_OK);
   // Allow "eval" or "arguments" for backward compatibility.
-  const AstRawString* name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+  const AstRawString* name =
+      ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
   Expect(Token::LPAREN, CHECK_OK);
   bool done = (peek() == Token::RPAREN);
   while (!done) {
-    ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+    ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
     done = (peek() == Token::RPAREN);
     if (!done) {
       Expect(Token::COMMA, CHECK_OK);
@@ -2319,7 +2327,7 @@ Block* Parser::ParseVariableDeclarations(
 
     // Parse variable name.
     if (nvars > 0) Consume(Token::COMMA);
-    name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
+    name = ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
     if (!first_name) first_name = name;
     Scanner::Location variable_loc = scanner()->location();
     if (fni_ != NULL) fni_->PushVariableName(name);
@@ -2671,7 +2679,7 @@ Statement* Parser::ParseContinueStatement(bool* ok) {
   if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
       tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
     // ECMA allows "eval" or "arguments" as labels even in strict mode.
-    label = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+    label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
   }
   IterationStatement* target = LookupContinueTarget(label, CHECK_OK);
   if (target == NULL) {
@@ -2701,7 +2709,7 @@ Statement* Parser::ParseBreakStatement(ZoneList<const AstRawString*>* labels,
   if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
       tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
     // ECMA allows "eval" or "arguments" as labels even in strict mode.
-    label = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+    label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
   }
   // Parse labeled break statements that target themselves into
   // empty statements, e.g. 'l1: l2: l3: break l2;'
@@ -2926,7 +2934,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
     Expect(Token::LPAREN, CHECK_OK);
     catch_scope = NewScope(scope_, CATCH_SCOPE);
     catch_scope->set_start_position(scanner()->location().beg_pos);
-    name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
+    name = ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
 
     Expect(Token::RPAREN, CHECK_OK);
 
@@ -3873,6 +3881,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
     Scanner::Location dupe_error_loc = Scanner::Location::invalid();
     Scanner::Location reserved_error_loc = Scanner::Location::invalid();
 
+    // Similarly for strong mode.
+    Scanner::Location undefined_error_loc = Scanner::Location::invalid();
+
     bool is_rest = false;
     bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
         (peek() == Token::RPAREN &&
@@ -3891,6 +3902,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
       if (!eval_args_error_loc.IsValid() && IsEvalOrArguments(param_name)) {
         eval_args_error_loc = scanner()->location();
       }
+      if (!undefined_error_loc.IsValid() && IsUndefined(param_name)) {
+        undefined_error_loc = scanner()->location();
+      }
       if (!reserved_error_loc.IsValid() && is_strict_reserved) {
         reserved_error_loc = scanner()->location();
       }
@@ -4009,8 +4023,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
                       CHECK_OK);
     const bool use_strict_params = is_rest || IsConciseMethod(kind);
     CheckFunctionParameterNames(language_mode(), use_strict_params,
-                                eval_args_error_loc, dupe_error_loc,
-                                reserved_error_loc, CHECK_OK);
+                                eval_args_error_loc, undefined_error_loc,
+                                dupe_error_loc, reserved_error_loc, CHECK_OK);
 
     if (is_strict(language_mode())) {
       CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
@@ -4264,6 +4278,11 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
     *ok = false;
     return NULL;
   }
+  if (is_strong(language_mode()) && IsUndefined(name)) {
+    ReportMessageAt(class_name_location, "strong_undefined");
+    *ok = false;
+    return NULL;
+  }
 
   // Create a block scope which is additionally tagged as class scope; this is
   // important for resolving variable references to the class name in the strong
@@ -4355,7 +4374,8 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
   int pos = peek_position();
   Expect(Token::MOD, CHECK_OK);
   // Allow "eval" or "arguments" for backward compatibility.
-  const AstRawString* name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+  const AstRawString* name = ParseIdentifier(kAllowRestrictedIdentifiers,
+                                             CHECK_OK);
   Scanner::Location spread_pos;
   ZoneList<Expression*>* args = ParseArguments(&spread_pos, CHECK_OK);
 
index e1cceaf..919b597 100644 (file)
@@ -569,6 +569,7 @@ class ParserTraits {
   bool IsEval(const AstRawString* identifier) const;
   bool IsArguments(const AstRawString* identifier) const;
   bool IsEvalOrArguments(const AstRawString* identifier) const;
+  bool IsUndefined(const AstRawString* identifier) const;
   V8_INLINE bool IsFutureStrictReserved(const AstRawString* identifier) const;
 
   // Returns true if the expression is of type "this.foo".
index 15fca26..d4515fc 100644 (file)
@@ -53,6 +53,9 @@ PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) {
   if (scanner->UnescapedLiteralMatches("arguments", 9)) {
     return PreParserIdentifier::Arguments();
   }
+  if (scanner->UnescapedLiteralMatches("undefined", 9)) {
+    return PreParserIdentifier::Undefined();
+  }
   if (scanner->LiteralMatches("prototype", 9)) {
     return PreParserIdentifier::Prototype();
   }
@@ -483,7 +486,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
   do {
     // Parse variable name.
     if (nvars > 0) Consume(Token::COMMA);
-    ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
+    ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
     Scanner::Location variable_loc = scanner()->location();
     nvars++;
     if (peek() == Token::ASSIGN || require_initializer ||
@@ -588,7 +591,7 @@ PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
       tok != Token::RBRACE &&
       tok != Token::EOS) {
     // ECMA allows "eval" or "arguments" as labels even in strict mode.
-    ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+    ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
   }
   ExpectSemicolon(CHECK_OK);
   return Statement::Default();
@@ -606,7 +609,7 @@ PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
       tok != Token::RBRACE &&
       tok != Token::EOS) {
     // ECMA allows "eval" or "arguments" as labels even in strict mode.
-    ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+    ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
   }
   ExpectSemicolon(CHECK_OK);
   return Statement::Default();
@@ -853,7 +856,7 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
   if (tok == Token::CATCH) {
     Consume(Token::CATCH);
     Expect(Token::LPAREN, CHECK_OK);
-    ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
+    ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
     Expect(Token::RPAREN, CHECK_OK);
     {
       Scope* with_scope = NewScope(scope_, WITH_SCOPE);
@@ -917,6 +920,9 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
   Scanner::Location dupe_error_loc = Scanner::Location::invalid();
   Scanner::Location reserved_error_loc = Scanner::Location::invalid();
 
+  // Similarly for strong mode.
+  Scanner::Location undefined_error_loc = Scanner::Location::invalid();
+
   bool is_rest = false;
   bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
       (peek() == Token::RPAREN &&
@@ -933,6 +939,9 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
     if (!eval_args_error_loc.IsValid() && param_name.IsEvalOrArguments()) {
       eval_args_error_loc = scanner()->location();
     }
+    if (!undefined_error_loc.IsValid() && param_name.IsUndefined()) {
+      undefined_error_loc = scanner()->location();
+    }
     if (!reserved_error_loc.IsValid() && is_strict_reserved) {
       reserved_error_loc = scanner()->location();
     }
@@ -976,8 +985,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
                     name_is_strict_reserved, function_name_location, CHECK_OK);
   const bool use_strict_params = is_rest || IsConciseMethod(kind);
   CheckFunctionParameterNames(language_mode(), use_strict_params,
-                              eval_args_error_loc, dupe_error_loc,
-                              reserved_error_loc, CHECK_OK);
+                              eval_args_error_loc, undefined_error_loc,
+                              dupe_error_loc, reserved_error_loc, CHECK_OK);
 
   if (is_strict(language_mode())) {
     int end_position = scanner()->location().end_pos;
@@ -1026,11 +1035,17 @@ PreParserExpression PreParser::ParseClassLiteral(
     *ok = false;
     return EmptyExpression();
   }
+  LanguageMode class_language_mode = language_mode();
+  if (is_strong(class_language_mode) && IsUndefined(name)) {
+    ReportMessageAt(class_name_location, "strong_undefined");
+    *ok = false;
+    return EmptyExpression();
+  }
 
   Scope* scope = NewScope(scope_, BLOCK_SCOPE);
   BlockState block_state(&scope_, scope);
   scope_->SetLanguageMode(
-      static_cast<LanguageMode>(scope_->language_mode() | STRICT_BIT));
+      static_cast<LanguageMode>(class_language_mode | STRICT_BIT));
   // TODO(marja): Make PreParser use scope names too.
   // scope_->SetScopeName(name);
 
@@ -1068,7 +1083,7 @@ PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
     return Expression::Default();
   }
   // Allow "eval" or "arguments" for backward compatibility.
-  ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+  ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
   Scanner::Location spread_pos;
   ParseArguments(&spread_pos, ok);
 
index 2b61853..986ad80 100644 (file)
@@ -151,9 +151,9 @@ class ParserBase : public Traits {
   void set_allow_strong_mode(bool allow) { allow_strong_mode_ = allow; }
 
  protected:
-  enum AllowEvalOrArgumentsAsIdentifier {
-    kAllowEvalOrArguments,
-    kDontAllowEvalOrArguments
+  enum AllowRestrictedIdentifiers {
+    kAllowRestrictedIdentifiers,
+    kDontAllowRestrictedIdentifiers
   };
 
   enum Mode {
@@ -484,6 +484,11 @@ class ParserBase : public Traits {
       *ok = false;
       return;
     }
+    if (is_strong(language_mode) && this->IsUndefined(function_name)) {
+      Traits::ReportMessageAt(function_name_loc, "strong_undefined");
+      *ok = false;
+      return;
+    }
   }
 
   // Checking the parameter names of a function literal. This has to be done
@@ -491,6 +496,7 @@ class ParserBase : public Traits {
   void CheckFunctionParameterNames(LanguageMode language_mode,
                                    bool strict_params,
                                    const Scanner::Location& eval_args_error_loc,
+                                   const Scanner::Location& undefined_error_loc,
                                    const Scanner::Location& dupe_error_loc,
                                    const Scanner::Location& reserved_loc,
                                    bool* ok) {
@@ -501,6 +507,11 @@ class ParserBase : public Traits {
       *ok = false;
       return;
     }
+    if (is_strong(language_mode) && undefined_error_loc.IsValid()) {
+      Traits::ReportMessageAt(eval_args_error_loc, "strong_undefined");
+      *ok = false;
+      return;
+    }
     // TODO(arv): When we add support for destructuring in setters we also need
     // to check for duplicate names.
     if (dupe_error_loc.IsValid()) {
@@ -552,9 +563,7 @@ class ParserBase : public Traits {
   // allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or
   // "arguments" as identifier even in strict mode (this is needed in cases like
   // "var foo = eval;").
-  IdentifierT ParseIdentifier(
-      AllowEvalOrArgumentsAsIdentifier,
-      bool* ok);
+  IdentifierT ParseIdentifier(AllowRestrictedIdentifiers, bool* ok);
   // Parses an identifier or a strict mode future reserved word, and indicate
   // whether it is strict mode future reserved.
   IdentifierT ParseIdentifierOrStrictReservedWord(
@@ -709,6 +718,9 @@ class PreParserIdentifier {
   static PreParserIdentifier Arguments() {
     return PreParserIdentifier(kArgumentsIdentifier);
   }
+  static PreParserIdentifier Undefined() {
+    return PreParserIdentifier(kUndefinedIdentifier);
+  }
   static PreParserIdentifier FutureReserved() {
     return PreParserIdentifier(kFutureReservedIdentifier);
   }
@@ -733,6 +745,7 @@ class PreParserIdentifier {
   bool IsEval() const { return type_ == kEvalIdentifier; }
   bool IsArguments() const { return type_ == kArgumentsIdentifier; }
   bool IsEvalOrArguments() const { return IsEval() || IsArguments(); }
+  bool IsUndefined() const { return type_ == kUndefinedIdentifier; }
   bool IsLet() const { return type_ == kLetIdentifier; }
   bool IsStatic() const { return type_ == kStaticIdentifier; }
   bool IsYield() const { return type_ == kYieldIdentifier; }
@@ -744,7 +757,6 @@ class PreParserIdentifier {
            type_ == kLetIdentifier || type_ == kStaticIdentifier ||
            type_ == kYieldIdentifier;
   }
-  bool IsValidStrictVariable() const { return type_ == kUnknownIdentifier; }
   V8_INLINE bool IsValidArrowParam() const {
     // A valid identifier can be an arrow function parameter
     // except for eval, arguments, yield, and reserved keywords.
@@ -769,6 +781,7 @@ class PreParserIdentifier {
     kYieldIdentifier,
     kEvalIdentifier,
     kArgumentsIdentifier,
+    kUndefinedIdentifier,
     kPrototypeIdentifier,
     kConstructorIdentifier
   };
@@ -1263,6 +1276,10 @@ class PreParserTraits {
     return identifier.IsEvalOrArguments();
   }
 
+  static bool IsUndefined(PreParserIdentifier identifier) {
+    return identifier.IsUndefined();
+  }
+
   static bool IsPrototype(PreParserIdentifier identifier) {
     return identifier.IsPrototype();
   }
@@ -1785,18 +1802,21 @@ void ParserBase<Traits>::ReportUnexpectedTokenAt(
 }
 
 
-template<class Traits>
+template <class Traits>
 typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier(
-    AllowEvalOrArgumentsAsIdentifier allow_eval_or_arguments,
-    bool* ok) {
+    AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) {
   Token::Value next = Next();
   if (next == Token::IDENTIFIER) {
     IdentifierT name = this->GetSymbol(scanner());
-    if (allow_eval_or_arguments == kDontAllowEvalOrArguments) {
+    if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) {
       if (is_strict(language_mode()) && this->IsEvalOrArguments(name)) {
         ReportMessage("strict_eval_arguments");
         *ok = false;
       }
+      if (is_strong(language_mode()) && this->IsUndefined(name)) {
+        ReportMessage("strong_undefined");
+        *ok = false;
+      }
     } else {
       if (is_strong(language_mode()) && this->IsArguments(name)) {
         ReportMessage("strong_arguments");
@@ -1817,7 +1837,6 @@ typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier(
   }
 }
 
-
 template <class Traits>
 typename ParserBase<Traits>::IdentifierT ParserBase<
     Traits>::ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved,
@@ -1956,7 +1975,7 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
     case Token::YIELD:
     case Token::FUTURE_STRICT_RESERVED_WORD: {
       // Using eval or arguments in this context is OK even in strict mode.
-      IdentifierT name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
+      IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
       result = this->ExpressionFromIdentifier(name, beg_pos, end_pos, scope_,
                                               factory());
       break;
@@ -3059,13 +3078,15 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(int start_pos,
     scope->set_end_position(scanner()->location().end_pos);
 
     // Arrow function *parameter lists* are always checked as in strict mode.
-    // TODO(arv): eval_args_error_loc and reserved_loc needs to be set by
-    // DeclareArrowParametersFromExpression.
+    // TODO(arv): eval_args_error_loc, undefined_error_loc, and reserved_loc
+    // needs to be set by DeclareArrowParametersFromExpression.
     Scanner::Location eval_args_error_loc = Scanner::Location::invalid();
+    Scanner::Location undefined_error_loc = Scanner::Location::invalid();
     Scanner::Location reserved_loc = Scanner::Location::invalid();
     const bool use_strict_params = true;
     this->CheckFunctionParameterNames(language_mode(), use_strict_params,
-        eval_args_error_loc, dupe_error_loc, reserved_loc, CHECK_OK);
+                                      eval_args_error_loc, undefined_error_loc,
+                                      dupe_error_loc, reserved_loc, CHECK_OK);
 
     // Validate strict mode.
     if (is_strict(language_mode())) {
@@ -3189,12 +3210,21 @@ typename ParserBase<Traits>::ExpressionT ParserBase<
     Traits>::CheckAndRewriteReferenceExpression(ExpressionT expression,
                                                 Scanner::Location location,
                                                 const char* message, bool* ok) {
-  if (is_strict(language_mode()) && this->IsIdentifier(expression) &&
-      this->IsEvalOrArguments(this->AsIdentifier(expression))) {
-    this->ReportMessageAt(location, "strict_eval_arguments", kSyntaxError);
-    *ok = false;
-    return this->EmptyExpression();
-  } else if (expression->IsValidReferenceExpression()) {
+  if (this->IsIdentifier(expression)) {
+    if (is_strict(language_mode()) &&
+        this->IsEvalOrArguments(this->AsIdentifier(expression))) {
+      this->ReportMessageAt(location, "strict_eval_arguments", kSyntaxError);
+      *ok = false;
+      return this->EmptyExpression();
+    }
+    if (is_strong(language_mode()) &&
+        this->IsUndefined(this->AsIdentifier(expression))) {
+      this->ReportMessageAt(location, "strong_undefined", kSyntaxError);
+      *ok = false;
+      return this->EmptyExpression();
+    }
+  }
+  if (expression->IsValidReferenceExpression()) {
     return expression;
   } else if (expression->IsCall()) {
     // If it is a call, make it a runtime error for legacy web compatibility.
index 6f57081..e1fe193 100644 (file)
@@ -5845,6 +5845,37 @@ TEST(StrongConstructorReturns) {
 }
 
 
+TEST(StrongUndefinedLocal) {
+  const char* context_data[][2] = {{"", ""}, {NULL}};
+
+  const char* data[] = {
+      "function undefined() {'use strong';}",
+      "function* undefined() {'use strong';}",
+      "(function undefined() {'use strong';});",
+      "{foo: (function undefined(){'use strong';})};",
+      "(function* undefined() {'use strong';})",
+      "{foo: (function* undefined(){'use strong';})};",
+      "function foo(a, b, undefined, c, d) {'use strong';}",
+      "function* foo(a, b, undefined, c, d) {'use strong';}",
+      "(function foo(a, b, undefined, c, d) {'use strong';})",
+      "{foo: (function foo(a, b, undefined, c, d) {'use strong';})};",
+      "(function* foo(a, b, undefined, c, d) {'use strong';})",
+      "{foo: (function* foo(a, b, undefined, c, d) {'use strong';})};",
+      "class C { foo(a, b, undefined, c, d) {'use strong';} }",
+      "class C { *foo(a, b, undefined, c, d) {'use strong';} }",
+      "({ foo(a, b, undefined, c, d) {'use strong';} });",
+      "{ *foo(a, b, undefined, c, d) {'use strong';} });",
+      "class undefined {'use strong'}",
+      "(class undefined {'use strong'});",
+      NULL};
+
+  static const ParserFlag always_flags[] = {kAllowStrongMode};
+
+  RunParserSyncTest(context_data, data, kError, NULL, 0,
+                    always_flags, arraysize(always_flags));
+}
+
+
 TEST(ArrowFunctionASIErrors) {
   const char* context_data[][2] = {{"'use strict';", ""}, {"", ""},
                                    {NULL, NULL}};
diff --git a/test/mjsunit/strong/undefined.js b/test/mjsunit/strong/undefined.js
new file mode 100644 (file)
index 0000000..8fc213d
--- /dev/null
@@ -0,0 +1,192 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --strong-mode --harmony-sloppy --harmony-arrow-functions
+
+// Repurposing the strict mode 'eval' and 'arguments' tests to test for correct
+// behaviour of 'undefined' as an identifier in strong mode.
+"use strict";
+
+function CheckStrongMode(code) {
+  let strictContexts = [
+    ["'use strict';", ""],
+    ["function outer() { 'use strict';", "}"],
+    ["function outer() { 'use strict'; function inner() {", "}}"],
+    ["class C { m() {", "} }"]
+  ]
+  let strongContexts = [
+    ["'use strong';", ""],
+    ["function outer() { 'use strong';", "}"],
+    ["function outer() { 'use strong'; function inner() {", "}}"],
+    ["class C { m() { 'use strong';", "} }"]
+  ]
+
+  for (let context of strictContexts) {
+    assertThrows(context[0] + code + context[1] + "; throw new TypeError();",
+                 TypeError);
+  }
+  for (let context of strongContexts) {
+    assertThrows(context[0] + code + context[1], SyntaxError);
+  }
+}
+
+// Binding 'undefined'
+CheckStrongMode("var undefined;");
+CheckStrongMode("let undefined;");
+CheckStrongMode("var undefined = 0;");
+CheckStrongMode("let undefined = 0;");
+CheckStrongMode("const undefined = 0;");
+CheckStrongMode("var x, y = 0, undefined;");
+CheckStrongMode("let x, y = 0, undefined;");
+
+// Function identifier is 'undefined'
+// Function declaration
+CheckStrongMode("function undefined() {}");
+assertThrows("function undefined() {'use strong';}", SyntaxError);
+
+// Generator function
+CheckStrongMode("function* undefined() {}");
+assertThrows("function* undefined() {'use strong';}", SyntaxError);
+
+// Function expression
+CheckStrongMode("(function undefined() {});");
+assertThrows("(function undefined() {'use strong';});", SyntaxError);
+CheckStrongMode("{foo: (function undefined(){})};");
+assertThrows("{foo: (function undefined(){'use strong';})};", SyntaxError);
+
+//Generator function expression
+CheckStrongMode("(function* undefined() {})");
+assertThrows("(function* undefined() {'use strong';})", SyntaxError);
+CheckStrongMode("{foo: (function* undefined(){})};");
+assertThrows("{foo: (function* undefined(){'use strong';})};", SyntaxError);
+
+// Function parameter named 'undefined'
+// Function declaration
+CheckStrongMode("function foo(a, b, undefined, c, d) {}");
+assertThrows("function foo(a, b, undefined, c, d) {'use strong';}",
+             SyntaxError);
+
+// Generator function declaration
+CheckStrongMode("function* foo(a, b, undefined, c, d) {}");
+assertThrows("function* foo(a, b, undefined, c, d) {'use strong';}",
+             SyntaxError);
+
+// Function expression
+CheckStrongMode("(function foo(a, b, undefined, c, d) {});");
+assertThrows("(function foo(a, b, undefined, c, d) {'use strong';})",
+             SyntaxError);
+CheckStrongMode("{foo: (function foo(a, b, undefined, c, d) {})};");
+assertThrows("{foo: (function foo(a, b, undefined, c, d) {'use strong';})};",
+             SyntaxError);
+
+// Generator function expression
+CheckStrongMode("(function* foo(a, b, undefined, c, d) {});");
+assertThrows("(function* foo(a, b, undefined, c, d) {'use strong';})",
+             SyntaxError);
+CheckStrongMode("{foo: (function* foo(a, b, undefined, c, d) {})};");
+assertThrows("{foo: (function* foo(a, b, undefined, c, d) {'use strong';})};",
+             SyntaxError);
+
+// Method parameter named 'undefined'
+// Class method
+CheckStrongMode("class C { foo(a, b, undefined, c, d) {} }");
+assertThrows("class C { foo(a, b, undefined, c, d) {'use strong';} }",
+             SyntaxError);
+
+//Class generator method
+CheckStrongMode("class C { *foo(a, b, undefined, c, d) {} }");
+assertThrows("class C { *foo(a, b, undefined, c, d) {'use strong';} }",
+             SyntaxError);
+
+//Object literal method
+CheckStrongMode("({ foo(a, b, undefined, c, d) {} });");
+assertThrows("({ foo(a, b, undefined, c, d) {'use strong';} });", SyntaxError);
+
+//Object literal generator method
+CheckStrongMode("({ *foo(a, b, undefined, c, d) {} });");
+assertThrows("({ *foo(a, b, undefined, c, d) {'use strong';} });", SyntaxError);
+
+// Arrow function expression with 'undefined' param
+// TODO(conradw): Checking arrow function heads is hard to modify just now
+// CheckStrongMode("(undefined => {})");
+// assertThrows("(undefined => {'use strong';})");
+
+// Class declaration named 'undefined'
+CheckStrongMode("class undefined {}");
+assertThrows("class undefined {'use strong'}", SyntaxError);
+
+// Class expression named 'undefined'
+CheckStrongMode("(class undefined {});");
+assertThrows("(class undefined {'use strong'});", SyntaxError);
+
+// Binding/assigning to 'undefined' in for
+CheckStrongMode("for(undefined = 0;false;);");
+CheckStrongMode("for(var undefined = 0;false;);");
+CheckStrongMode("for(let undefined = 0;false;);");
+CheckStrongMode("for(const undefined = 0;false;);");
+
+// Binding/assigning to 'undefined' in for-in
+CheckStrongMode("for(undefined in {});");
+CheckStrongMode("for(var undefined in {});");
+CheckStrongMode("for(let undefined in {});");
+CheckStrongMode("for(const undefined in {});");
+
+// Binding/assigning to 'undefined' in for-of
+CheckStrongMode("for(undefined of []);");
+CheckStrongMode("for(var undefined of []);");
+CheckStrongMode("for(let undefined of []);");
+CheckStrongMode("for(const undefined of []);");
+
+// Property accessor parameter named 'undefined'.
+CheckStrongMode("let o = { set foo(undefined) {} }");
+assertThrows("let o = { set foo(undefined) {'use strong';} }", SyntaxError);
+
+// catch(undefined)
+CheckStrongMode("try {} catch(undefined) {};");
+
+// Assignment to undefined
+CheckStrongMode("undefined = 0;");
+CheckStrongMode("print(undefined = 0);");
+CheckStrongMode("let x = undefined = 0;");
+
+// Compound assignment to undefined
+CheckStrongMode("undefined *= 0;");
+CheckStrongMode("undefined /= 0;");
+CheckStrongMode("print(undefined %= 0);");
+CheckStrongMode("let x = undefined += 0;");
+CheckStrongMode("let x = undefined -= 0;");
+CheckStrongMode("undefined <<= 0;");
+CheckStrongMode("undefined >>= 0;");
+CheckStrongMode("print(undefined >>>= 0);");
+CheckStrongMode("print(undefined &= 0);");
+CheckStrongMode("let x = undefined ^= 0;");
+CheckStrongMode("let x = undefined |= 0;");
+
+// Postfix increment with undefined
+CheckStrongMode("undefined++;");
+CheckStrongMode("print(undefined++);");
+CheckStrongMode("let x = undefined++;");
+
+// Postfix decrement with undefined
+CheckStrongMode("undefined--;");
+CheckStrongMode("print(undefined--);");
+CheckStrongMode("let x = undefined--;");
+
+// Prefix increment with undefined
+CheckStrongMode("++undefined;");
+CheckStrongMode("print(++undefined);");
+CheckStrongMode("let x = ++undefined;");
+
+// Prefix decrement with undefined
+CheckStrongMode("--undefined;");
+CheckStrongMode("print(--undefined);");
+CheckStrongMode("let x = --undefined;");
+
+// Function constructor: 'undefined' parameter name
+assertDoesNotThrow(function() {
+  Function("undefined", "");
+});
+assertThrows(function() {
+  Function("undefined", "'use strong';");
+}, SyntaxError);