[strong] Implement static restrictions on binding/assignment to 'undefined'

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}
This commit is contained in:
conradw 2015-04-10 05:04:51 -07:00 committed by Commit bot
parent fd0556efa0
commit 8ef7159582
8 changed files with 332 additions and 41 deletions

View File

@ -265,6 +265,7 @@ class AstValue : public ZoneObject {
F(this, "this") \ F(this, "this") \
F(throw_iterator_result_not_an_object, "ThrowIteratorResultNotAnObject") \ F(throw_iterator_result_not_an_object, "ThrowIteratorResultNotAnObject") \
F(to_string, "ToString") \ F(to_string, "ToString") \
F(undefined, "undefined") \
F(use_asm, "use asm") \ F(use_asm, "use asm") \
F(use_strong, "use strong") \ F(use_strong, "use strong") \
F(use_strict, "use strict") \ F(use_strict, "use strict") \

View File

@ -167,6 +167,7 @@ var kMessages = {
strict_caller: ["Illegal access to a strict mode caller function."], strict_caller: ["Illegal access to a strict mode caller function."],
strong_ellision: ["In strong mode, arrays with holes are deprecated, use maps instead"], 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_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_equal: ["In strong mode, '==' and '!=' are deprecated, use '===' and '!==' instead"],
strong_delete: ["In strong mode, 'delete' is deprecated, use maps or sets 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"], strong_var: ["In strong mode, 'var' is deprecated, use 'let' or 'const' instead"],

View File

@ -465,6 +465,9 @@ bool ParserTraits::IsEvalOrArguments(const AstRawString* identifier) const {
return IsEval(identifier) || IsArguments(identifier); 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 { bool ParserTraits::IsPrototype(const AstRawString* identifier) const {
return identifier == parser_->ast_value_factory()->prototype_string(); return identifier == parser_->ast_value_factory()->prototype_string();
@ -1476,6 +1479,10 @@ ZoneList<ImportDeclaration*>* Parser::ParseNamedImports(int pos, bool* ok) {
*ok = false; *ok = false;
ReportMessage("strict_eval_arguments"); ReportMessage("strict_eval_arguments");
return NULL; return NULL;
} else if (is_strong(language_mode()) && IsUndefined(local_name)) {
*ok = false;
ReportMessage("strong_undefined");
return NULL;
} }
VariableProxy* proxy = NewUnresolved(local_name, IMPORT); VariableProxy* proxy = NewUnresolved(local_name, IMPORT);
ImportDeclaration* declaration = ImportDeclaration* declaration =
@ -1525,7 +1532,7 @@ Statement* Parser::ParseImportDeclaration(bool* ok) {
ImportDeclaration* import_default_declaration = NULL; ImportDeclaration* import_default_declaration = NULL;
if (tok != Token::MUL && tok != Token::LBRACE) { if (tok != Token::MUL && tok != Token::LBRACE) {
const AstRawString* local_name = const AstRawString* local_name =
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
VariableProxy* proxy = NewUnresolved(local_name, IMPORT); VariableProxy* proxy = NewUnresolved(local_name, IMPORT);
import_default_declaration = factory()->NewImportDeclaration( import_default_declaration = factory()->NewImportDeclaration(
proxy, ast_value_factory()->default_string(), NULL, scope_, pos); proxy, ast_value_factory()->default_string(), NULL, scope_, pos);
@ -1540,7 +1547,7 @@ Statement* Parser::ParseImportDeclaration(bool* ok) {
Consume(Token::MUL); Consume(Token::MUL);
ExpectContextualKeyword(CStrVector("as"), CHECK_OK); ExpectContextualKeyword(CStrVector("as"), CHECK_OK);
module_instance_binding = module_instance_binding =
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
// TODO(ES6): Add an appropriate declaration. // TODO(ES6): Add an appropriate declaration.
break; break;
} }
@ -2033,11 +2040,12 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
int pos = peek_position(); int pos = peek_position();
Expect(Token::FUNCTION, CHECK_OK); Expect(Token::FUNCTION, CHECK_OK);
// Allow "eval" or "arguments" for backward compatibility. // 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); Expect(Token::LPAREN, CHECK_OK);
bool done = (peek() == Token::RPAREN); bool done = (peek() == Token::RPAREN);
while (!done) { while (!done) {
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
done = (peek() == Token::RPAREN); done = (peek() == Token::RPAREN);
if (!done) { if (!done) {
Expect(Token::COMMA, CHECK_OK); Expect(Token::COMMA, CHECK_OK);
@ -2319,7 +2327,7 @@ Block* Parser::ParseVariableDeclarations(
// Parse variable name. // Parse variable name.
if (nvars > 0) Consume(Token::COMMA); if (nvars > 0) Consume(Token::COMMA);
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK); name = ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
if (!first_name) first_name = name; if (!first_name) first_name = name;
Scanner::Location variable_loc = scanner()->location(); Scanner::Location variable_loc = scanner()->location();
if (fni_ != NULL) fni_->PushVariableName(name); if (fni_ != NULL) fni_->PushVariableName(name);
@ -2671,7 +2679,7 @@ Statement* Parser::ParseContinueStatement(bool* ok) {
if (!scanner()->HasAnyLineTerminatorBeforeNext() && if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
// ECMA allows "eval" or "arguments" as labels even in strict mode. // 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); IterationStatement* target = LookupContinueTarget(label, CHECK_OK);
if (target == NULL) { if (target == NULL) {
@ -2701,7 +2709,7 @@ Statement* Parser::ParseBreakStatement(ZoneList<const AstRawString*>* labels,
if (!scanner()->HasAnyLineTerminatorBeforeNext() && if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
// ECMA allows "eval" or "arguments" as labels even in strict mode. // 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 // Parse labeled break statements that target themselves into
// empty statements, e.g. 'l1: l2: l3: break l2;' // empty statements, e.g. 'l1: l2: l3: break l2;'
@ -2926,7 +2934,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
catch_scope = NewScope(scope_, CATCH_SCOPE); catch_scope = NewScope(scope_, CATCH_SCOPE);
catch_scope->set_start_position(scanner()->location().beg_pos); catch_scope->set_start_position(scanner()->location().beg_pos);
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK); name = ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
@ -3873,6 +3881,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
Scanner::Location dupe_error_loc = Scanner::Location::invalid(); Scanner::Location dupe_error_loc = Scanner::Location::invalid();
Scanner::Location reserved_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 is_rest = false;
bool done = arity_restriction == FunctionLiteral::GETTER_ARITY || bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
(peek() == Token::RPAREN && (peek() == Token::RPAREN &&
@ -3891,6 +3902,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
if (!eval_args_error_loc.IsValid() && IsEvalOrArguments(param_name)) { if (!eval_args_error_loc.IsValid() && IsEvalOrArguments(param_name)) {
eval_args_error_loc = scanner()->location(); 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) { if (!reserved_error_loc.IsValid() && is_strict_reserved) {
reserved_error_loc = scanner()->location(); reserved_error_loc = scanner()->location();
} }
@ -4009,8 +4023,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
CHECK_OK); CHECK_OK);
const bool use_strict_params = is_rest || IsConciseMethod(kind); const bool use_strict_params = is_rest || IsConciseMethod(kind);
CheckFunctionParameterNames(language_mode(), use_strict_params, CheckFunctionParameterNames(language_mode(), use_strict_params,
eval_args_error_loc, dupe_error_loc, eval_args_error_loc, undefined_error_loc,
reserved_error_loc, CHECK_OK); dupe_error_loc, reserved_error_loc, CHECK_OK);
if (is_strict(language_mode())) { if (is_strict(language_mode())) {
CheckStrictOctalLiteral(scope->start_position(), scope->end_position(), CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
@ -4264,6 +4278,11 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
*ok = false; *ok = false;
return NULL; 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 // 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 // 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(); int pos = peek_position();
Expect(Token::MOD, CHECK_OK); Expect(Token::MOD, CHECK_OK);
// Allow "eval" or "arguments" for backward compatibility. // 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; Scanner::Location spread_pos;
ZoneList<Expression*>* args = ParseArguments(&spread_pos, CHECK_OK); ZoneList<Expression*>* args = ParseArguments(&spread_pos, CHECK_OK);

View File

@ -569,6 +569,7 @@ class ParserTraits {
bool IsEval(const AstRawString* identifier) const; bool IsEval(const AstRawString* identifier) const;
bool IsArguments(const AstRawString* identifier) const; bool IsArguments(const AstRawString* identifier) const;
bool IsEvalOrArguments(const AstRawString* identifier) const; bool IsEvalOrArguments(const AstRawString* identifier) const;
bool IsUndefined(const AstRawString* identifier) const;
V8_INLINE bool IsFutureStrictReserved(const AstRawString* identifier) const; V8_INLINE bool IsFutureStrictReserved(const AstRawString* identifier) const;
// Returns true if the expression is of type "this.foo". // Returns true if the expression is of type "this.foo".

View File

@ -53,6 +53,9 @@ PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) {
if (scanner->UnescapedLiteralMatches("arguments", 9)) { if (scanner->UnescapedLiteralMatches("arguments", 9)) {
return PreParserIdentifier::Arguments(); return PreParserIdentifier::Arguments();
} }
if (scanner->UnescapedLiteralMatches("undefined", 9)) {
return PreParserIdentifier::Undefined();
}
if (scanner->LiteralMatches("prototype", 9)) { if (scanner->LiteralMatches("prototype", 9)) {
return PreParserIdentifier::Prototype(); return PreParserIdentifier::Prototype();
} }
@ -483,7 +486,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
do { do {
// Parse variable name. // Parse variable name.
if (nvars > 0) Consume(Token::COMMA); if (nvars > 0) Consume(Token::COMMA);
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
Scanner::Location variable_loc = scanner()->location(); Scanner::Location variable_loc = scanner()->location();
nvars++; nvars++;
if (peek() == Token::ASSIGN || require_initializer || if (peek() == Token::ASSIGN || require_initializer ||
@ -588,7 +591,7 @@ PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
tok != Token::RBRACE && tok != Token::RBRACE &&
tok != Token::EOS) { tok != Token::EOS) {
// ECMA allows "eval" or "arguments" as labels even in strict mode. // ECMA allows "eval" or "arguments" as labels even in strict mode.
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
} }
ExpectSemicolon(CHECK_OK); ExpectSemicolon(CHECK_OK);
return Statement::Default(); return Statement::Default();
@ -606,7 +609,7 @@ PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
tok != Token::RBRACE && tok != Token::RBRACE &&
tok != Token::EOS) { tok != Token::EOS) {
// ECMA allows "eval" or "arguments" as labels even in strict mode. // ECMA allows "eval" or "arguments" as labels even in strict mode.
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
} }
ExpectSemicolon(CHECK_OK); ExpectSemicolon(CHECK_OK);
return Statement::Default(); return Statement::Default();
@ -853,7 +856,7 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
if (tok == Token::CATCH) { if (tok == Token::CATCH) {
Consume(Token::CATCH); Consume(Token::CATCH);
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
{ {
Scope* with_scope = NewScope(scope_, WITH_SCOPE); 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 dupe_error_loc = Scanner::Location::invalid();
Scanner::Location reserved_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 is_rest = false;
bool done = arity_restriction == FunctionLiteral::GETTER_ARITY || bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
(peek() == Token::RPAREN && (peek() == Token::RPAREN &&
@ -933,6 +939,9 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
if (!eval_args_error_loc.IsValid() && param_name.IsEvalOrArguments()) { if (!eval_args_error_loc.IsValid() && param_name.IsEvalOrArguments()) {
eval_args_error_loc = scanner()->location(); 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) { if (!reserved_error_loc.IsValid() && is_strict_reserved) {
reserved_error_loc = scanner()->location(); reserved_error_loc = scanner()->location();
} }
@ -976,8 +985,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
name_is_strict_reserved, function_name_location, CHECK_OK); name_is_strict_reserved, function_name_location, CHECK_OK);
const bool use_strict_params = is_rest || IsConciseMethod(kind); const bool use_strict_params = is_rest || IsConciseMethod(kind);
CheckFunctionParameterNames(language_mode(), use_strict_params, CheckFunctionParameterNames(language_mode(), use_strict_params,
eval_args_error_loc, dupe_error_loc, eval_args_error_loc, undefined_error_loc,
reserved_error_loc, CHECK_OK); dupe_error_loc, reserved_error_loc, CHECK_OK);
if (is_strict(language_mode())) { if (is_strict(language_mode())) {
int end_position = scanner()->location().end_pos; int end_position = scanner()->location().end_pos;
@ -1026,11 +1035,17 @@ PreParserExpression PreParser::ParseClassLiteral(
*ok = false; *ok = false;
return EmptyExpression(); 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); Scope* scope = NewScope(scope_, BLOCK_SCOPE);
BlockState block_state(&scope_, scope); BlockState block_state(&scope_, scope);
scope_->SetLanguageMode( 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. // TODO(marja): Make PreParser use scope names too.
// scope_->SetScopeName(name); // scope_->SetScopeName(name);
@ -1068,7 +1083,7 @@ PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
return Expression::Default(); return Expression::Default();
} }
// Allow "eval" or "arguments" for backward compatibility. // Allow "eval" or "arguments" for backward compatibility.
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK); ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
Scanner::Location spread_pos; Scanner::Location spread_pos;
ParseArguments(&spread_pos, ok); ParseArguments(&spread_pos, ok);

View File

@ -151,9 +151,9 @@ class ParserBase : public Traits {
void set_allow_strong_mode(bool allow) { allow_strong_mode_ = allow; } void set_allow_strong_mode(bool allow) { allow_strong_mode_ = allow; }
protected: protected:
enum AllowEvalOrArgumentsAsIdentifier { enum AllowRestrictedIdentifiers {
kAllowEvalOrArguments, kAllowRestrictedIdentifiers,
kDontAllowEvalOrArguments kDontAllowRestrictedIdentifiers
}; };
enum Mode { enum Mode {
@ -484,6 +484,11 @@ class ParserBase : public Traits {
*ok = false; *ok = false;
return; 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 // 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, void CheckFunctionParameterNames(LanguageMode language_mode,
bool strict_params, bool strict_params,
const Scanner::Location& eval_args_error_loc, const Scanner::Location& eval_args_error_loc,
const Scanner::Location& undefined_error_loc,
const Scanner::Location& dupe_error_loc, const Scanner::Location& dupe_error_loc,
const Scanner::Location& reserved_loc, const Scanner::Location& reserved_loc,
bool* ok) { bool* ok) {
@ -501,6 +507,11 @@ class ParserBase : public Traits {
*ok = false; *ok = false;
return; 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 // TODO(arv): When we add support for destructuring in setters we also need
// to check for duplicate names. // to check for duplicate names.
if (dupe_error_loc.IsValid()) { if (dupe_error_loc.IsValid()) {
@ -552,9 +563,7 @@ class ParserBase : public Traits {
// allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or // allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or
// "arguments" as identifier even in strict mode (this is needed in cases like // "arguments" as identifier even in strict mode (this is needed in cases like
// "var foo = eval;"). // "var foo = eval;").
IdentifierT ParseIdentifier( IdentifierT ParseIdentifier(AllowRestrictedIdentifiers, bool* ok);
AllowEvalOrArgumentsAsIdentifier,
bool* ok);
// Parses an identifier or a strict mode future reserved word, and indicate // Parses an identifier or a strict mode future reserved word, and indicate
// whether it is strict mode future reserved. // whether it is strict mode future reserved.
IdentifierT ParseIdentifierOrStrictReservedWord( IdentifierT ParseIdentifierOrStrictReservedWord(
@ -709,6 +718,9 @@ class PreParserIdentifier {
static PreParserIdentifier Arguments() { static PreParserIdentifier Arguments() {
return PreParserIdentifier(kArgumentsIdentifier); return PreParserIdentifier(kArgumentsIdentifier);
} }
static PreParserIdentifier Undefined() {
return PreParserIdentifier(kUndefinedIdentifier);
}
static PreParserIdentifier FutureReserved() { static PreParserIdentifier FutureReserved() {
return PreParserIdentifier(kFutureReservedIdentifier); return PreParserIdentifier(kFutureReservedIdentifier);
} }
@ -733,6 +745,7 @@ class PreParserIdentifier {
bool IsEval() const { return type_ == kEvalIdentifier; } bool IsEval() const { return type_ == kEvalIdentifier; }
bool IsArguments() const { return type_ == kArgumentsIdentifier; } bool IsArguments() const { return type_ == kArgumentsIdentifier; }
bool IsEvalOrArguments() const { return IsEval() || IsArguments(); } bool IsEvalOrArguments() const { return IsEval() || IsArguments(); }
bool IsUndefined() const { return type_ == kUndefinedIdentifier; }
bool IsLet() const { return type_ == kLetIdentifier; } bool IsLet() const { return type_ == kLetIdentifier; }
bool IsStatic() const { return type_ == kStaticIdentifier; } bool IsStatic() const { return type_ == kStaticIdentifier; }
bool IsYield() const { return type_ == kYieldIdentifier; } bool IsYield() const { return type_ == kYieldIdentifier; }
@ -744,7 +757,6 @@ class PreParserIdentifier {
type_ == kLetIdentifier || type_ == kStaticIdentifier || type_ == kLetIdentifier || type_ == kStaticIdentifier ||
type_ == kYieldIdentifier; type_ == kYieldIdentifier;
} }
bool IsValidStrictVariable() const { return type_ == kUnknownIdentifier; }
V8_INLINE bool IsValidArrowParam() const { V8_INLINE bool IsValidArrowParam() const {
// A valid identifier can be an arrow function parameter // A valid identifier can be an arrow function parameter
// except for eval, arguments, yield, and reserved keywords. // except for eval, arguments, yield, and reserved keywords.
@ -769,6 +781,7 @@ class PreParserIdentifier {
kYieldIdentifier, kYieldIdentifier,
kEvalIdentifier, kEvalIdentifier,
kArgumentsIdentifier, kArgumentsIdentifier,
kUndefinedIdentifier,
kPrototypeIdentifier, kPrototypeIdentifier,
kConstructorIdentifier kConstructorIdentifier
}; };
@ -1263,6 +1276,10 @@ class PreParserTraits {
return identifier.IsEvalOrArguments(); return identifier.IsEvalOrArguments();
} }
static bool IsUndefined(PreParserIdentifier identifier) {
return identifier.IsUndefined();
}
static bool IsPrototype(PreParserIdentifier identifier) { static bool IsPrototype(PreParserIdentifier identifier) {
return identifier.IsPrototype(); return identifier.IsPrototype();
} }
@ -1787,16 +1804,19 @@ void ParserBase<Traits>::ReportUnexpectedTokenAt(
template <class Traits> template <class Traits>
typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier( typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier(
AllowEvalOrArgumentsAsIdentifier allow_eval_or_arguments, AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) {
bool* ok) {
Token::Value next = Next(); Token::Value next = Next();
if (next == Token::IDENTIFIER) { if (next == Token::IDENTIFIER) {
IdentifierT name = this->GetSymbol(scanner()); IdentifierT name = this->GetSymbol(scanner());
if (allow_eval_or_arguments == kDontAllowEvalOrArguments) { if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) {
if (is_strict(language_mode()) && this->IsEvalOrArguments(name)) { if (is_strict(language_mode()) && this->IsEvalOrArguments(name)) {
ReportMessage("strict_eval_arguments"); ReportMessage("strict_eval_arguments");
*ok = false; *ok = false;
} }
if (is_strong(language_mode()) && this->IsUndefined(name)) {
ReportMessage("strong_undefined");
*ok = false;
}
} else { } else {
if (is_strong(language_mode()) && this->IsArguments(name)) { if (is_strong(language_mode()) && this->IsArguments(name)) {
ReportMessage("strong_arguments"); ReportMessage("strong_arguments");
@ -1817,7 +1837,6 @@ typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier(
} }
} }
template <class Traits> template <class Traits>
typename ParserBase<Traits>::IdentifierT ParserBase< typename ParserBase<Traits>::IdentifierT ParserBase<
Traits>::ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved, Traits>::ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved,
@ -1956,7 +1975,7 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
case Token::YIELD: case Token::YIELD:
case Token::FUTURE_STRICT_RESERVED_WORD: { case Token::FUTURE_STRICT_RESERVED_WORD: {
// Using eval or arguments in this context is OK even in strict mode. // 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_, result = this->ExpressionFromIdentifier(name, beg_pos, end_pos, scope_,
factory()); factory());
break; break;
@ -3059,13 +3078,15 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(int start_pos,
scope->set_end_position(scanner()->location().end_pos); scope->set_end_position(scanner()->location().end_pos);
// Arrow function *parameter lists* are always checked as in strict mode. // 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 // TODO(arv): eval_args_error_loc, undefined_error_loc, and reserved_loc
// DeclareArrowParametersFromExpression. // needs to be set by DeclareArrowParametersFromExpression.
Scanner::Location eval_args_error_loc = Scanner::Location::invalid(); Scanner::Location eval_args_error_loc = Scanner::Location::invalid();
Scanner::Location undefined_error_loc = Scanner::Location::invalid();
Scanner::Location reserved_loc = Scanner::Location::invalid(); Scanner::Location reserved_loc = Scanner::Location::invalid();
const bool use_strict_params = true; const bool use_strict_params = true;
this->CheckFunctionParameterNames(language_mode(), use_strict_params, 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. // Validate strict mode.
if (is_strict(language_mode())) { if (is_strict(language_mode())) {
@ -3189,12 +3210,21 @@ typename ParserBase<Traits>::ExpressionT ParserBase<
Traits>::CheckAndRewriteReferenceExpression(ExpressionT expression, Traits>::CheckAndRewriteReferenceExpression(ExpressionT expression,
Scanner::Location location, Scanner::Location location,
const char* message, bool* ok) { const char* message, bool* ok) {
if (is_strict(language_mode()) && this->IsIdentifier(expression) && if (this->IsIdentifier(expression)) {
if (is_strict(language_mode()) &&
this->IsEvalOrArguments(this->AsIdentifier(expression))) { this->IsEvalOrArguments(this->AsIdentifier(expression))) {
this->ReportMessageAt(location, "strict_eval_arguments", kSyntaxError); this->ReportMessageAt(location, "strict_eval_arguments", kSyntaxError);
*ok = false; *ok = false;
return this->EmptyExpression(); return this->EmptyExpression();
} else if (expression->IsValidReferenceExpression()) { }
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; return expression;
} else if (expression->IsCall()) { } else if (expression->IsCall()) {
// If it is a call, make it a runtime error for legacy web compatibility. // If it is a call, make it a runtime error for legacy web compatibility.

View 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) { TEST(ArrowFunctionASIErrors) {
const char* context_data[][2] = {{"'use strict';", ""}, {"", ""}, const char* context_data[][2] = {{"'use strict';", ""}, {"", ""},
{NULL, NULL}}; {NULL, NULL}};

View File

@ -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);