Commit 77394fa0 authored by conradw's avatar conradw Committed by Commit bot

[parser] disallow language mode directive in body of function with non-simple parameters

TC39 agreed to disallow "use strict" directives in function body when
non-simple parameter lists are used.

This is a continuation of caitp's CL https://codereview.chromium.org/1281163002/
with some refactorings removed for now.

Still TODO: there is a lot of duplication between the is_simple field of
FormalParametersBase and the NonSimpleParameter property ExpressionClassifier
keeps track of. It should be possible to remove the former with a minor
refactoring of arrow function parsing. This will be attempted in a follow-up CL.

BUG=
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#30388}
parent aca4a411
...@@ -45,11 +45,17 @@ class ExpressionClassifier { ...@@ -45,11 +45,17 @@ class ExpressionClassifier {
ArrowFormalParametersProduction) ArrowFormalParametersProduction)
}; };
enum FunctionProperties { NonSimpleParameter = 1 << 0 };
ExpressionClassifier() ExpressionClassifier()
: invalid_productions_(0), duplicate_finder_(nullptr) {} : invalid_productions_(0),
function_properties_(0),
duplicate_finder_(nullptr) {}
explicit ExpressionClassifier(DuplicateFinder* duplicate_finder) explicit ExpressionClassifier(DuplicateFinder* duplicate_finder)
: invalid_productions_(0), duplicate_finder_(duplicate_finder) {} : invalid_productions_(0),
function_properties_(0),
duplicate_finder_(duplicate_finder) {}
bool is_valid(unsigned productions) const { bool is_valid(unsigned productions) const {
return (invalid_productions_ & productions) == 0; return (invalid_productions_ & productions) == 0;
...@@ -111,6 +117,14 @@ class ExpressionClassifier { ...@@ -111,6 +117,14 @@ class ExpressionClassifier {
return strong_mode_formal_parameter_error_; return strong_mode_formal_parameter_error_;
} }
bool is_simple_parameter_list() const {
return !(function_properties_ & NonSimpleParameter);
}
void RecordNonSimpleParameter() {
function_properties_ |= NonSimpleParameter;
}
void RecordExpressionError(const Scanner::Location& loc, void RecordExpressionError(const Scanner::Location& loc,
MessageTemplate::Template message, MessageTemplate::Template message,
const char* arg = nullptr) { const char* arg = nullptr) {
...@@ -216,15 +230,21 @@ class ExpressionClassifier { ...@@ -216,15 +230,21 @@ class ExpressionClassifier {
// As an exception to the above, the result continues to be a valid arrow // As an exception to the above, the result continues to be a valid arrow
// formal parameters if the inner expression is a valid binding pattern. // formal parameters if the inner expression is a valid binding pattern.
if (productions & ArrowFormalParametersProduction && if (productions & ArrowFormalParametersProduction &&
is_valid_arrow_formal_parameters() && is_valid_arrow_formal_parameters()) {
!inner.is_valid_binding_pattern()) { // Also copy function properties if expecting an arrow function
invalid_productions_ |= ArrowFormalParametersProduction; // parameter.
arrow_formal_parameters_error_ = inner.binding_pattern_error_; function_properties_ |= inner.function_properties_;
if (!inner.is_valid_binding_pattern()) {
invalid_productions_ |= ArrowFormalParametersProduction;
arrow_formal_parameters_error_ = inner.binding_pattern_error_;
}
} }
} }
private: private:
unsigned invalid_productions_; unsigned invalid_productions_;
unsigned function_properties_;
Error expression_error_; Error expression_error_;
Error binding_pattern_error_; Error binding_pattern_error_;
Error assignment_pattern_error_; Error assignment_pattern_error_;
......
...@@ -296,6 +296,8 @@ class CallSite { ...@@ -296,6 +296,8 @@ class CallSite {
T(IllegalAccess, "Illegal access") \ T(IllegalAccess, "Illegal access") \
T(IllegalBreak, "Illegal break statement") \ T(IllegalBreak, "Illegal break statement") \
T(IllegalContinue, "Illegal continue statement") \ T(IllegalContinue, "Illegal continue statement") \
T(IllegalLanguageModeDirective, \
"Illegal '%' directive in function with non-simple parameter list") \
T(IllegalReturn, "Illegal return statement") \ T(IllegalReturn, "Illegal return statement") \
T(InvalidLhsInAssignment, "Invalid left-hand side in assignment") \ T(InvalidLhsInAssignment, "Invalid left-hand side in assignment") \
T(InvalidLhsInFor, "Invalid left-hand side in for-loop") \ T(InvalidLhsInFor, "Invalid left-hand side in for-loop") \
......
...@@ -1198,9 +1198,8 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info, ...@@ -1198,9 +1198,8 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info,
// BindingIdentifier // BindingIdentifier
ParseFormalParameter(&formals, &formals_classifier, &ok); ParseFormalParameter(&formals, &formals_classifier, &ok);
if (ok) { if (ok) {
DeclareFormalParameter( DeclareFormalParameter(formals.scope, formals.at(0),
formals.scope, formals.at(0), formals.is_simple, &formals_classifier);
&formals_classifier);
} }
} }
} }
...@@ -1322,6 +1321,20 @@ void* Parser::ParseStatementList(ZoneList<Statement*>* body, int end_token, ...@@ -1322,6 +1321,20 @@ void* Parser::ParseStatementList(ZoneList<Statement*>* body, int end_token,
token_loc.end_pos - token_loc.beg_pos == token_loc.end_pos - token_loc.beg_pos ==
ast_value_factory()->use_strong_string()->length() + 2; ast_value_factory()->use_strong_string()->length() + 2;
if (use_strict_found || use_strong_found) { if (use_strict_found || use_strong_found) {
if (!scope_->HasSimpleParameters()) {
// TC39 deemed "use strict" directives to be an error when occurring
// in the body of a function with non-simple parameter list, on
// 29/7/2015. https://goo.gl/ueA7Ln
//
// In V8, this also applies to "use strong " directives.
const AstRawString* string = literal->raw_value()->AsString();
ParserTraits::ReportMessageAt(
token_loc, MessageTemplate::kIllegalLanguageModeDirective,
string);
*ok = false;
return nullptr;
}
// Strong mode implies strict mode. If there are several "use strict" // Strong mode implies strict mode. If there are several "use strict"
// / "use strong" directives, do the strict mode changes only once. // / "use strong" directives, do the strict mode changes only once.
if (is_sloppy(scope_->language_mode())) { if (is_sloppy(scope_->language_mode())) {
...@@ -3889,8 +3902,7 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) { ...@@ -3889,8 +3902,7 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
void ParserTraits::ParseArrowFunctionFormalParameters( void ParserTraits::ParseArrowFunctionFormalParameters(
ParserFormalParameters* parameters, Expression* expr, ParserFormalParameters* parameters, Expression* expr,
const Scanner::Location& params_loc, const Scanner::Location& params_loc, bool* ok) {
Scanner::Location* duplicate_loc, bool* ok) {
if (parameters->Arity() >= Code::kMaxArguments) { if (parameters->Arity() >= Code::kMaxArguments) {
ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList); ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList);
*ok = false; *ok = false;
...@@ -3917,8 +3929,7 @@ void ParserTraits::ParseArrowFunctionFormalParameters( ...@@ -3917,8 +3929,7 @@ void ParserTraits::ParseArrowFunctionFormalParameters(
DCHECK_EQ(binop->op(), Token::COMMA); DCHECK_EQ(binop->op(), Token::COMMA);
Expression* left = binop->left(); Expression* left = binop->left();
Expression* right = binop->right(); Expression* right = binop->right();
ParseArrowFunctionFormalParameters(parameters, left, params_loc, ParseArrowFunctionFormalParameters(parameters, left, params_loc, ok);
duplicate_loc, ok);
if (!*ok) return; if (!*ok) return;
// LHS of comma expression should be unparenthesized. // LHS of comma expression should be unparenthesized.
expr = right; expr = right;
...@@ -3962,15 +3973,16 @@ void ParserTraits::ParseArrowFunctionFormalParameterList( ...@@ -3962,15 +3973,16 @@ void ParserTraits::ParseArrowFunctionFormalParameterList(
Scanner::Location* duplicate_loc, bool* ok) { Scanner::Location* duplicate_loc, bool* ok) {
if (expr->IsEmptyParentheses()) return; if (expr->IsEmptyParentheses()) return;
ParseArrowFunctionFormalParameters(parameters, expr, params_loc, ParseArrowFunctionFormalParameters(parameters, expr, params_loc, ok);
duplicate_loc, ok);
if (!*ok) return; if (!*ok) return;
ExpressionClassifier classifier;
if (!parameters->is_simple) {
classifier.RecordNonSimpleParameter();
}
for (int i = 0; i < parameters->Arity(); ++i) { for (int i = 0; i < parameters->Arity(); ++i) {
auto parameter = parameters->at(i); auto parameter = parameters->at(i);
ExpressionClassifier classifier; DeclareFormalParameter(parameters->scope, parameter, &classifier);
DeclareFormalParameter(
parameters->scope, parameter, parameters->is_simple, &classifier);
if (!duplicate_loc->IsValid()) { if (!duplicate_loc->IsValid()) {
*duplicate_loc = classifier.duplicate_formal_parameter_error().location; *duplicate_loc = classifier.duplicate_formal_parameter_error().location;
} }
...@@ -4585,7 +4597,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( ...@@ -4585,7 +4597,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
#undef SET_ALLOW #undef SET_ALLOW
} }
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction( PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
language_mode(), function_state_->kind(), logger, bookmark); language_mode(), function_state_->kind(), scope_->has_simple_parameters(),
logger, bookmark);
if (pre_parse_timer_ != NULL) { if (pre_parse_timer_ != NULL) {
pre_parse_timer_->Stop(); pre_parse_timer_->Stop();
} }
......
...@@ -786,11 +786,11 @@ class ParserTraits { ...@@ -786,11 +786,11 @@ class ParserTraits {
Expression* initializer, bool is_rest); Expression* initializer, bool is_rest);
V8_INLINE void DeclareFormalParameter( V8_INLINE void DeclareFormalParameter(
Scope* scope, const ParserFormalParameters::Parameter& parameter, Scope* scope, const ParserFormalParameters::Parameter& parameter,
bool is_simple, ExpressionClassifier* classifier); ExpressionClassifier* classifier);
void ParseArrowFunctionFormalParameters( void ParseArrowFunctionFormalParameters(ParserFormalParameters* parameters,
ParserFormalParameters* parameters, Expression* params, Expression* params,
const Scanner::Location& params_loc, const Scanner::Location& params_loc,
Scanner::Location* duplicate_loc, bool* ok); bool* ok);
void ParseArrowFunctionFormalParameterList( void ParseArrowFunctionFormalParameterList(
ParserFormalParameters* parameters, Expression* params, ParserFormalParameters* parameters, Expression* params,
const Scanner::Location& params_loc, const Scanner::Location& params_loc,
...@@ -1335,12 +1335,14 @@ void ParserTraits::AddFormalParameter( ...@@ -1335,12 +1335,14 @@ void ParserTraits::AddFormalParameter(
void ParserTraits::DeclareFormalParameter( void ParserTraits::DeclareFormalParameter(
Scope* scope, const ParserFormalParameters::Parameter& parameter, Scope* scope, const ParserFormalParameters::Parameter& parameter,
bool is_simple, ExpressionClassifier* classifier) { ExpressionClassifier* classifier) {
bool is_duplicate = false; bool is_duplicate = false;
bool is_simple = classifier->is_simple_parameter_list();
// TODO(caitp): Remove special handling for rest once desugaring is in. // TODO(caitp): Remove special handling for rest once desugaring is in.
auto name = is_simple || parameter.is_rest auto name = is_simple || parameter.is_rest
? parameter.name : parser_->ast_value_factory()->empty_string(); ? parameter.name : parser_->ast_value_factory()->empty_string();
auto mode = is_simple || parameter.is_rest ? VAR : TEMPORARY; auto mode = is_simple || parameter.is_rest ? VAR : TEMPORARY;
if (!is_simple) scope->SetHasNonSimpleParameters();
bool is_optional = parameter.initializer != nullptr; bool is_optional = parameter.initializer != nullptr;
Variable* var = scope->DeclareParameter( Variable* var = scope->DeclareParameter(
name, mode, is_optional, parameter.is_rest, &is_duplicate); name, mode, is_optional, parameter.is_rest, &is_duplicate);
......
...@@ -102,8 +102,8 @@ PreParserExpression PreParserTraits::ParseFunctionLiteral( ...@@ -102,8 +102,8 @@ PreParserExpression PreParserTraits::ParseFunctionLiteral(
PreParser::PreParseResult PreParser::PreParseLazyFunction( PreParser::PreParseResult PreParser::PreParseLazyFunction(
LanguageMode language_mode, FunctionKind kind, ParserRecorder* log, LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters,
Scanner::BookmarkScope* bookmark) { ParserRecorder* log, Scanner::BookmarkScope* bookmark) {
log_ = log; log_ = log;
// Lazy functions always have trivial outer scopes (no with/catch scopes). // Lazy functions always have trivial outer scopes (no with/catch scopes).
Scope* top_scope = NewScope(scope_, SCRIPT_SCOPE); Scope* top_scope = NewScope(scope_, SCRIPT_SCOPE);
...@@ -113,6 +113,7 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction( ...@@ -113,6 +113,7 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction(
scope_->SetLanguageMode(language_mode); scope_->SetLanguageMode(language_mode);
Scope* function_scope = NewScope( Scope* function_scope = NewScope(
scope_, IsArrowFunction(kind) ? ARROW_SCOPE : FUNCTION_SCOPE, kind); scope_, IsArrowFunction(kind) ? ARROW_SCOPE : FUNCTION_SCOPE, kind);
if (!has_simple_parameters) function_scope->SetHasNonSimpleParameters();
PreParserFactory function_factory(NULL); PreParserFactory function_factory(NULL);
FunctionState function_state(&function_state_, &scope_, function_scope, kind, FunctionState function_state(&function_state_, &scope_, function_scope, kind,
&function_factory); &function_factory);
...@@ -251,15 +252,33 @@ void PreParser::ParseStatementList(int end_token, bool* ok, ...@@ -251,15 +252,33 @@ void PreParser::ParseStatementList(int end_token, bool* ok,
} }
if (directive_prologue) { if (directive_prologue) {
if (statement.IsUseStrictLiteral()) { bool use_strict_found = statement.IsUseStrictLiteral();
bool use_strong_found =
statement.IsUseStrongLiteral() && allow_strong_mode();
if (use_strict_found) {
scope_->SetLanguageMode( scope_->SetLanguageMode(
static_cast<LanguageMode>(scope_->language_mode() | STRICT)); static_cast<LanguageMode>(scope_->language_mode() | STRICT));
} else if (statement.IsUseStrongLiteral() && allow_strong_mode()) { } else if (use_strong_found) {
scope_->SetLanguageMode(static_cast<LanguageMode>( scope_->SetLanguageMode(static_cast<LanguageMode>(
scope_->language_mode() | STRONG)); scope_->language_mode() | STRONG));
} else if (!statement.IsStringLiteral()) { } else if (!statement.IsStringLiteral()) {
directive_prologue = false; directive_prologue = false;
} }
if ((use_strict_found || use_strong_found) &&
!scope_->HasSimpleParameters()) {
// TC39 deemed "use strict" directives to be an error when occurring
// in the body of a function with non-simple parameter list, on
// 29/7/2015. https://goo.gl/ueA7Ln
//
// In V8, this also applies to "use strong " directives.
PreParserTraits::ReportMessageAt(
token_loc, MessageTemplate::kIllegalLanguageModeDirective,
use_strict_found ? "use strict" : "use strong");
*ok = false;
return;
}
} }
// If we're allowed to reset to a bookmark, we will do so when we see a long // If we're allowed to reset to a bookmark, we will do so when we see a long
...@@ -1057,7 +1076,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral( ...@@ -1057,7 +1076,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
int start_position = scanner()->location().beg_pos; int start_position = scanner()->location().beg_pos;
function_scope->set_start_position(start_position); function_scope->set_start_position(start_position);
PreParserFormalParameters formals(nullptr); PreParserFormalParameters formals(function_scope);
ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK); ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos; int formals_end_position = scanner()->location().end_pos;
......
...@@ -1648,8 +1648,11 @@ class PreParserTraits { ...@@ -1648,8 +1648,11 @@ class PreParserTraits {
++parameters->arity; ++parameters->arity;
} }
void DeclareFormalParameter(Scope* scope, PreParserIdentifier parameter, void DeclareFormalParameter(Scope* scope, PreParserIdentifier parameter,
bool is_simple, ExpressionClassifier* classifier) {
ExpressionClassifier* classifier) {} if (!classifier->is_simple_parameter_list()) {
scope->SetHasNonSimpleParameters();
}
}
void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {} void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {}
...@@ -1747,8 +1750,8 @@ class PreParser : public ParserBase<PreParserTraits> { ...@@ -1747,8 +1750,8 @@ class PreParser : public ParserBase<PreParserTraits> {
// At return, unless an error occurred, the scanner is positioned before the // At return, unless an error occurred, the scanner is positioned before the
// the final '}'. // the final '}'.
PreParseResult PreParseLazyFunction( PreParseResult PreParseLazyFunction(
LanguageMode language_mode, FunctionKind kind, ParserRecorder* log, LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters,
Scanner::BookmarkScope* bookmark = nullptr); ParserRecorder* log, Scanner::BookmarkScope* bookmark = nullptr);
private: private:
friend class PreParserTraits; friend class PreParserTraits;
...@@ -2283,6 +2286,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, ...@@ -2283,6 +2286,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
classifier->RecordExpressionError(scanner()->location(), classifier->RecordExpressionError(scanner()->location(),
MessageTemplate::kUnexpectedToken, MessageTemplate::kUnexpectedToken,
Token::String(Token::ELLIPSIS)); Token::String(Token::ELLIPSIS));
classifier->RecordNonSimpleParameter();
Scanner::Location expr_loc = scanner()->peek_location(); Scanner::Location expr_loc = scanner()->peek_location();
Token::Value tok = peek(); Token::Value tok = peek();
result = this->ParseAssignmentExpression(true, classifier, CHECK_OK); result = this->ParseAssignmentExpression(true, classifier, CHECK_OK);
...@@ -2295,6 +2299,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, ...@@ -2295,6 +2299,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
return this->EmptyExpression(); return this->EmptyExpression();
} }
result = factory()->NewSpread(result, ellipsis_pos); result = factory()->NewSpread(result, ellipsis_pos);
if (peek() == Token::COMMA) { if (peek() == Token::COMMA) {
ReportMessageAt(scanner()->peek_location(), ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kParamAfterRest); MessageTemplate::kParamAfterRest);
...@@ -2385,6 +2390,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( ...@@ -2385,6 +2390,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
this->ParseAssignmentExpression(accept_IN, &binding_classifier, CHECK_OK); this->ParseAssignmentExpression(accept_IN, &binding_classifier, CHECK_OK);
classifier->Accumulate(binding_classifier, classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions); ExpressionClassifier::AllProductions);
bool is_simple_parameter_list = this->IsIdentifier(result);
bool seen_rest = false; bool seen_rest = false;
while (peek() == Token::COMMA) { while (peek() == Token::COMMA) {
if (seen_rest) { if (seen_rest) {
...@@ -2408,10 +2414,15 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( ...@@ -2408,10 +2414,15 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
ExpressionT right = this->ParseAssignmentExpression( ExpressionT right = this->ParseAssignmentExpression(
accept_IN, &binding_classifier, CHECK_OK); accept_IN, &binding_classifier, CHECK_OK);
if (is_rest) right = factory()->NewSpread(right, pos); if (is_rest) right = factory()->NewSpread(right, pos);
is_simple_parameter_list =
is_simple_parameter_list && this->IsIdentifier(right);
classifier->Accumulate(binding_classifier, classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions); ExpressionClassifier::AllProductions);
result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos); result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos);
} }
if (!is_simple_parameter_list || seen_rest) {
classifier->RecordNonSimpleParameter();
}
return result; return result;
} }
...@@ -2839,7 +2850,6 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, ...@@ -2839,7 +2850,6 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
} }
ExpressionT expression = this->ParseConditionalExpression( ExpressionT expression = this->ParseConditionalExpression(
accept_IN, &arrow_formals_classifier, CHECK_OK); accept_IN, &arrow_formals_classifier, CHECK_OK);
if (allow_harmony_arrow_functions() && peek() == Token::ARROW) { if (allow_harmony_arrow_functions() && peek() == Token::ARROW) {
BindingPatternUnexpectedToken(classifier); BindingPatternUnexpectedToken(classifier);
ValidateArrowFormalParameters(&arrow_formals_classifier, expression, ValidateArrowFormalParameters(&arrow_formals_classifier, expression,
...@@ -2848,6 +2858,10 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, ...@@ -2848,6 +2858,10 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
Scope* scope = Scope* scope =
this->NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction); this->NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction);
FormalParametersT parameters(scope); FormalParametersT parameters(scope);
if (!arrow_formals_classifier.is_simple_parameter_list()) {
scope->SetHasNonSimpleParameters();
parameters.is_simple = false;
}
checkpoint.Restore(&parameters.materialized_literals_count); checkpoint.Restore(&parameters.materialized_literals_count);
scope->set_start_position(lhs_beg_pos); scope->set_start_position(lhs_beg_pos);
...@@ -3662,6 +3676,7 @@ void ParserBase<Traits>::ParseFormalParameter( ...@@ -3662,6 +3676,7 @@ void ParserBase<Traits>::ParseFormalParameter(
return; return;
} }
parameters->is_simple = false; parameters->is_simple = false;
classifier->RecordNonSimpleParameter();
} }
ExpressionT initializer = Traits::EmptyExpression(); ExpressionT initializer = Traits::EmptyExpression();
...@@ -3672,6 +3687,7 @@ void ParserBase<Traits>::ParseFormalParameter( ...@@ -3672,6 +3687,7 @@ void ParserBase<Traits>::ParseFormalParameter(
ValidateExpression(&init_classifier, ok); ValidateExpression(&init_classifier, ok);
if (!*ok) return; if (!*ok) return;
parameters->is_simple = false; parameters->is_simple = false;
classifier->RecordNonSimpleParameter();
} }
Traits::AddFormalParameter(parameters, pattern, initializer, is_rest); Traits::AddFormalParameter(parameters, pattern, initializer, is_rest);
...@@ -3712,6 +3728,7 @@ void ParserBase<Traits>::ParseFormalParameterList( ...@@ -3712,6 +3728,7 @@ void ParserBase<Traits>::ParseFormalParameterList(
if (parameters->has_rest) { if (parameters->has_rest) {
parameters->is_simple = false; parameters->is_simple = false;
classifier->RecordNonSimpleParameter();
if (peek() == Token::COMMA) { if (peek() == Token::COMMA) {
ReportMessageAt(scanner()->peek_location(), ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kParamAfterRest); MessageTemplate::kParamAfterRest);
...@@ -3723,8 +3740,7 @@ void ParserBase<Traits>::ParseFormalParameterList( ...@@ -3723,8 +3740,7 @@ void ParserBase<Traits>::ParseFormalParameterList(
for (int i = 0; i < parameters->Arity(); ++i) { for (int i = 0; i < parameters->Arity(); ++i) {
auto parameter = parameters->at(i); auto parameter = parameters->at(i);
Traits::DeclareFormalParameter( Traits::DeclareFormalParameter(parameters->scope, parameter, classifier);
parameters->scope, parameter, parameters->is_simple, classifier);
} }
} }
......
...@@ -475,7 +475,6 @@ Variable* Scope::DeclareParameter( ...@@ -475,7 +475,6 @@ Variable* Scope::DeclareParameter(
Variable* var; Variable* var;
if (mode == TEMPORARY) { if (mode == TEMPORARY) {
var = NewTemporary(name); var = NewTemporary(name);
has_simple_parameters_ = false;
} else { } else {
var = variables_.Declare(this, name, mode, Variable::NORMAL, var = variables_.Declare(this, name, mode, Variable::NORMAL,
kCreatedInitialized); kCreatedInitialized);
...@@ -489,7 +488,6 @@ Variable* Scope::DeclareParameter( ...@@ -489,7 +488,6 @@ Variable* Scope::DeclareParameter(
DCHECK_NULL(rest_parameter_); DCHECK_NULL(rest_parameter_);
rest_parameter_ = var; rest_parameter_ = var;
rest_index_ = num_parameters(); rest_index_ = num_parameters();
has_simple_parameters_ = false;
} }
params_.Add(var, zone()); params_.Add(var, zone());
return var; return var;
......
...@@ -387,10 +387,25 @@ class Scope: public ZoneObject { ...@@ -387,10 +387,25 @@ class Scope: public ZoneObject {
} }
bool has_simple_parameters() const { bool has_simple_parameters() const {
DCHECK(is_function_scope());
return has_simple_parameters_; return has_simple_parameters_;
} }
// TODO(caitp): manage this state in a better way. PreParser must be able to
// communicate that the scope is non-simple, without allocating any parameters
// as the Parser does. This is necessary to ensure that TC39's proposed early
// error can be reported consistently regardless of whether lazily parsed or
// not.
void SetHasNonSimpleParameters() {
DCHECK(is_function_scope());
has_simple_parameters_ = false;
}
// Retrieve `IsSimpleParameterList` of current or outer function.
bool HasSimpleParameters() {
Scope* scope = ClosureScope();
return !scope->is_function_scope() || scope->has_simple_parameters();
}
// The local variable 'arguments' if we need to allocate it; NULL otherwise. // The local variable 'arguments' if we need to allocate it; NULL otherwise.
Variable* arguments() const { Variable* arguments() const {
DCHECK(!is_arrow_scope() || arguments_ == nullptr); DCHECK(!is_arrow_scope() || arguments_ == nullptr);
......
...@@ -6925,3 +6925,90 @@ TEST(LetSloppy) { ...@@ -6925,3 +6925,90 @@ TEST(LetSloppy) {
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags, RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags)); arraysize(always_flags));
} }
TEST(LanguageModeDirectivesNonSimpleParameterListErrors) {
// TC39 deemed "use strict" directives to be an error when occurring in the
// body of a function with non-simple parameter list, on 29/7/2015.
// https://goo.gl/ueA7Ln
//
// In V8, this also applies to "use strong " directives.
const char* context_data[][2] = {
{"function f(", ") { 'use strict'; }"},
{"function f(", ") { 'use strong'; }"},
{"function* g(", ") { 'use strict'; }"},
{"function* g(", ") { 'use strong'; }"},
{"class c { foo(", ") { 'use strict' }"},
{"class c { foo(", ") { 'use strong' }"},
{"var a = (", ") => { 'use strict'; }"},
{"var a = (", ") => { 'use strong'; }"},
{"var o = { m(", ") { 'use strict'; }"},
{"var o = { m(", ") { 'use strong'; }"},
{"var o = { *gm(", ") { 'use strict'; }"},
{"var o = { *gm(", ") { 'use strong'; }"},
{"var c = { m(", ") { 'use strict'; }"},
{"var c = { m(", ") { 'use strong'; }"},
{"var c = { *gm(", ") { 'use strict'; }"},
{"var c = { *gm(", ") { 'use strong'; }"},
{"'use strict'; function f(", ") { 'use strict'; }"},
{"'use strict'; function f(", ") { 'use strong'; }"},
{"'use strict'; function* g(", ") { 'use strict'; }"},
{"'use strict'; function* g(", ") { 'use strong'; }"},
{"'use strict'; class c { foo(", ") { 'use strict' }"},
{"'use strict'; class c { foo(", ") { 'use strong' }"},
{"'use strict'; var a = (", ") => { 'use strict'; }"},
{"'use strict'; var a = (", ") => { 'use strong'; }"},
{"'use strict'; var o = { m(", ") { 'use strict'; }"},
{"'use strict'; var o = { m(", ") { 'use strong'; }"},
{"'use strict'; var o = { *gm(", ") { 'use strict'; }"},
{"'use strict'; var o = { *gm(", ") { 'use strong'; }"},
{"'use strict'; var c = { m(", ") { 'use strict'; }"},
{"'use strict'; var c = { m(", ") { 'use strong'; }"},
{"'use strict'; var c = { *gm(", ") { 'use strict'; }"},
{"'use strict'; var c = { *gm(", ") { 'use strong'; }"},
{"'use strong'; function f(", ") { 'use strict'; }"},
{"'use strong'; function f(", ") { 'use strong'; }"},
{"'use strong'; function* g(", ") { 'use strict'; }"},
{"'use strong'; function* g(", ") { 'use strong'; }"},
{"'use strong'; class c { foo(", ") { 'use strict' }"},
{"'use strong'; class c { foo(", ") { 'use strong' }"},
{"'use strong'; var a = (", ") => { 'use strict'; }"},
{"'use strong'; var a = (", ") => { 'use strong'; }"},
{"'use strong'; var o = { m(", ") { 'use strict'; }"},
{"'use strong'; var o = { m(", ") { 'use strong'; }"},
{"'use strong'; var o = { *gm(", ") { 'use strict'; }"},
{"'use strong'; var o = { *gm(", ") { 'use strong'; }"},
{"'use strong'; var c = { m(", ") { 'use strict'; }"},
{"'use strong'; var c = { m(", ") { 'use strong'; }"},
{"'use strong'; var c = { *gm(", ") { 'use strict'; }"},
{"'use strong'; var c = { *gm(", ") { 'use strong'; }"},
{NULL, NULL}};
const char* data[] = {
// TODO(@caitp): support formal parameter initializers
"{}",
"[]",
"[{}]",
"{a}",
"a, {b}",
"a, b, {c, d, e}",
"initializer = true",
"a, b, c = 1",
"...args",
"a, b, ...rest",
"[a, b, ...rest]",
"{ bindingPattern = {} }",
"{ initializedBindingPattern } = { initializedBindingPattern: true }",
NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonyArrowFunctions, kAllowHarmonyDefaultParameters,
kAllowHarmonyDestructuring, kAllowHarmonyRestParameters,
kAllowHarmonySloppy, kAllowStrongMode
};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
...@@ -18,18 +18,20 @@ ...@@ -18,18 +18,20 @@
// //
// strictTest(6,5,4,3,2,1) // strictTest(6,5,4,3,2,1)
// //
var strictTest = (a, b, ...c) => { var strictTest = (() => {
"use strict"; "use strict";
assertEquals(Array, c.constructor); return (a, b, ...c) => {
assertTrue(Array.isArray(c)); assertEquals(Array, c.constructor);
assertTrue(Array.isArray(c));
var expectedLength = (a === undefined) ? 0 : a - 2; var expectedLength = (a === undefined) ? 0 : a - 2;
assertEquals(expectedLength, c.length); assertEquals(expectedLength, c.length);
for (var i = 2; i < a; ++i) { for (var i = 2; i < a; ++i) {
assertEquals(c[i - 2], a - i); assertEquals(c[i - 2], a - i);
} }
} };
})();
var sloppyTest = (a, b, ...c) => { var sloppyTest = (a, b, ...c) => {
assertEquals(Array, c.constructor); assertEquals(Array, c.constructor);
......
...@@ -81,85 +81,92 @@ ...@@ -81,85 +81,92 @@
})(); })();
(function TestParameterScoping() { (function TestParameterScopingSloppy() {
var x = 1; var x = 1;
function f1(a = x) { var x = 2; return a; } function f1(a = x) { var x = 2; return a; }
assertEquals(1, f1()); assertEquals(1, f1());
function f2(a = x) { function x() {}; return a; } function f2(a = x) { function x() {}; return a; }
assertEquals(1, f2()); assertEquals(1, f2());
function f3(a = x) { 'use strict'; let x = 2; return a; } function f3(a = eval("x")) { var x; return a; }
assertEquals(1, f3()); assertEquals(1, f3());
function f4(a = x) { 'use strict'; const x = 2; return a; } function f31(a = eval("'use strict'; x")) { var x; return a; }
assertEquals(1, f31());
function f4(a = function() { return x }) { var x; return a(); }
assertEquals(1, f4()); assertEquals(1, f4());
function f5(a = x) { 'use strict'; function x() {}; return a; } function f5(a = () => x) { var x; return a(); }
assertEquals(1, f5()); assertEquals(1, f5());
function f6(a = eval("x")) { var x; return a; } function f6(a = () => eval("x")) { var x; return a(); }
assertEquals(1, f6()); assertEquals(1, f6());
function f61(a = eval("x")) { 'use strict'; var x; return a; } function f61(a = () => { 'use strict'; return eval("x") }) { var x; return a(); }
assertEquals(1, f61()); assertEquals(1, f61());
function f62(a = eval("'use strict'; x")) { var x; return a; } function f62(a = () => eval("'use strict'; x")) { var x; return a(); }
assertEquals(1, f62()); assertEquals(1, f62());
function f7(a = function() { return x }) { var x; return a(); }
assertEquals(1, f7());
function f8(a = () => x) { var x; return a(); }
assertEquals(1, f8());
function f9(a = () => eval("x")) { var x; return a(); }
assertEquals(1, f9());
function f91(a = () => eval("x")) { 'use strict'; var x; return a(); }
assertEquals(1, f91());
function f92(a = () => { 'use strict'; return eval("x") }) { var x; return a(); }
assertEquals(1, f92());
function f93(a = () => eval("'use strict'; x")) { var x; return a(); }
assertEquals(1, f93());
var g1 = (a = x) => { var x = 2; return a; }; var g1 = (a = x) => { var x = 2; return a; };
assertEquals(1, g1()); assertEquals(1, g1());
var g2 = (a = x) => { function x() {}; return a; }; var g2 = (a = x) => { function x() {}; return a; };
assertEquals(1, g2()); assertEquals(1, g2());
var g3 = (a = x) => { 'use strict'; let x = 2; return a; }; var g3 = (a = eval("x")) => { var x; return a; };
assertEquals(1, g3()); assertEquals(1, g3());
var g4 = (a = x) => { 'use strict'; const x = 2; return a; }; var g31 = (a = eval("'use strict'; x")) => { var x; return a; };
assertEquals(1, g31());
var g4 = (a = function() { return x }) => { var x; return a(); };
assertEquals(1, g4()); assertEquals(1, g4());
var g5 = (a = x) => { 'use strict'; function x() {}; return a; }; var g5 = (a = () => x) => { var x; return a(); };
assertEquals(1, g5()); assertEquals(1, g5());
var g6 = (a = eval("x")) => { var x; return a; }; var g6 = (a = () => eval("x")) => { var x; return a(); };
assertEquals(1, g6()); assertEquals(1, g6());
var g61 = (a = eval("x")) => { 'use strict'; var x; return a; }; var g61 = (a = () => { 'use strict'; return eval("x") }) => { var x; return a(); };
assertEquals(1, g61()); assertEquals(1, g61());
var g62 = (a = eval("'use strict'; x")) => { var x; return a; }; var g62 = (a = () => eval("'use strict'; x")) => { var x; return a(); };
assertEquals(1, g62()); assertEquals(1, g62());
var g7 = (a = function() { return x }) => { var x; return a(); };
assertEquals(1, g7());
var g8 = (a = () => x) => { var x; return a(); };
assertEquals(1, g8());
var g9 = (a = () => eval("x")) => { var x; return a(); };
assertEquals(1, g9());
var g91 = (a = () => eval("x")) => { 'use strict'; var x; return a(); };
assertEquals(1, g91());
var g92 = (a = () => { 'use strict'; return eval("x") }) => { var x; return a(); };
assertEquals(1, g92());
var g93 = (a = () => eval("'use strict'; x")) => { var x; return a(); };
assertEquals(1, g93());
var f11 = function f(x = f) { var f; return x; } var f11 = function f(x = f) { var f; return x; }
assertSame(f11, f11()); assertSame(f11, f11());
var f12 = function f(x = f) { function f() {}; return x; } var f12 = function f(x = f) { function f() {}; return x; }
assertSame(f12, f12()); assertSame(f12, f12());
var f13 = function f(x = f) { 'use strict'; let f; return x; } var f13 = function f(f = 7, x = f) { return x; }
assertSame(f13, f13()); assertSame(7, f13());
var f14 = function f(x = f) { 'use strict'; const f = 0; return x; }
assertSame(f14, f14());
var f15 = function f(x = f) { 'use strict'; function f() {}; return x; }
assertSame(f15, f15());
var f16 = function f(f = 7, x = f) { return x; }
assertSame(7, f16());
var o1 = {f: function(x = this) { return x; }}; var o1 = {f: function(x = this) { return x; }};
assertSame(o1, o1.f()); assertSame(o1, o1.f());
assertSame(1, o1.f(1)); assertSame(1, o1.f(1));
})(); })();
(function TestParameterScopingStrict() {
"use strict";
var x = 1;
function f1(a = x) { let x = 2; return a; }
assertEquals(1, f1());
function f2(a = x) { const x = 2; return a; }
assertEquals(1, f2());
function f3(a = x) { function x() {}; return a; }
assertEquals(1, f3());
function f4(a = eval("x")) { var x; return a; }
assertEquals(1, f4());
function f5(a = () => eval("x")) { var x; return a(); }
assertEquals(1, f5());
var g1 = (a = x) => { let x = 2; return a; };
assertEquals(1, g1());
var g2 = (a = x) => { const x = 2; return a; };
assertEquals(1, g2());
var g3 = (a = x) => { function x() {}; return a; };
assertEquals(1, g3());
var g4 = (a = eval("x")) => { var x; return a; };
assertEquals(1, g4());
var g5 = (a = () => eval("x")) => { var x; return a(); };
assertEquals(1, g5());
var f11 = function f(x = f) { let f; return x; }
assertSame(f11, f11());
var f12 = function f(x = f) { const f = 0; return x; }
assertSame(f12, f12());
var f13 = function f(x = f) { function f() {}; return x; }
assertSame(f13, f13());
})();
(function TestSloppyEvalScoping() { (function TestSloppyEvalScoping() {
var x = 1; var x = 1;
...@@ -342,29 +349,22 @@ ...@@ -342,29 +349,22 @@
assertEquals(3, f22(() => 3)); assertEquals(3, f22(() => 3));
})(); })();
(function TestParameterTDZSloppy() {
(function TestParameterTDZ() {
function f1(a = x, x) { return a } function f1(a = x, x) { return a }
assertThrows(() => f1(undefined, 4), ReferenceError); assertThrows(() => f1(undefined, 4), ReferenceError);
assertEquals(4, f1(4, 5)); assertEquals(4, f1(4, 5));
function f2(a = eval("x"), x) { return a } function f2(a = eval("x"), x) { return a }
assertThrows(() => f2(undefined, 4), ReferenceError); assertThrows(() => f2(undefined, 4), ReferenceError);
assertEquals(4, f2(4, 5)); assertEquals(4, f2(4, 5));
function f3(a = eval("x"), x) { 'use strict'; return a } function f3(a = eval("'use strict'; x"), x) { return a }
assertThrows(() => f3(undefined, 4), ReferenceError); assertThrows(() => f3(undefined, 4), ReferenceError);
assertEquals(4, f3(4, 5)); assertEquals(4, f3(4, 5));
function f4(a = eval("'use strict'; x"), x) { return a } function f4(a = () => x, x) { return a() }
assertThrows(() => f4(undefined, 4), ReferenceError); assertEquals(4, f4(() => 4, 5));
assertEquals(4, f4(4, 5)); function f5(a = () => eval("x"), x) { return a() }
function f5(a = () => x, x) { return a() }
assertEquals(4, f5(() => 4, 5)); assertEquals(4, f5(() => 4, 5));
function f6(a = () => eval("x"), x) { return a() } function f6(a = () => eval("'use strict'; x"), x) { return a() }
assertEquals(4, f6(() => 4, 5)); assertEquals(4, f6(() => 4, 5));
function f7(a = () => eval("x"), x) { 'use strict'; return a() }
assertEquals(4, f7(() => 4, 5));
function f8(a = () => eval("'use strict'; x"), x) { return a() }
assertEquals(4, f8(() => 4, 5));
function f11(a = x, x = 2) { return a } function f11(a = x, x = 2) { return a }
assertThrows(() => f11(), ReferenceError); assertThrows(() => f11(), ReferenceError);
...@@ -376,36 +376,49 @@ ...@@ -376,36 +376,49 @@
assertThrows(() => f12(undefined), ReferenceError); assertThrows(() => f12(undefined), ReferenceError);
assertThrows(() => f12(undefined, 4), ReferenceError); assertThrows(() => f12(undefined, 4), ReferenceError);
assertEquals(4, f12(4, 5)); assertEquals(4, f12(4, 5));
function f13(a = eval("x"), x = 2) { 'use strict'; return a } function f13(a = eval("'use strict'; x"), x = 2) { return a }
assertThrows(() => f13(), ReferenceError); assertThrows(() => f13(), ReferenceError);
assertThrows(() => f13(undefined), ReferenceError); assertThrows(() => f13(undefined), ReferenceError);
assertThrows(() => f13(undefined, 4), ReferenceError); assertThrows(() => f13(undefined, 4), ReferenceError);
assertEquals(4, f13(4, 5)); assertEquals(4, f13(4, 5));
function f14(a = eval("'use strict'; x"), x = 2) { return a }
assertThrows(() => f14(), ReferenceError); function f21(x = function() { return a }, ...a) { return x()[0] }
assertThrows(() => f14(undefined), ReferenceError); assertEquals(4, f21(undefined, 4));
assertThrows(() => f14(undefined, 4), ReferenceError); function f22(x = () => a, ...a) { return x()[0] }
assertEquals(4, f14(4, 5)); assertEquals(4, f22(undefined, 4));
function f23(x = () => eval("a"), ...a) { return x()[0] }
function f34(x = function() { return a }, ...a) { return x()[0] } assertEquals(4, f23(undefined, 4));
assertEquals(4, f34(undefined, 4)); function f24(x = () => {'use strict'; return eval("a") }, ...a) {
function f35(x = () => a, ...a) { return x()[0] } return x()[0]
assertEquals(4, f35(undefined, 4)); }
function f36(x = () => eval("a"), ...a) { return x()[0] } assertEquals(4, f24(undefined, 4));
assertEquals(4, f36(undefined, 4)); function f25(x = () => eval("'use strict'; a"), ...a) { return x()[0] }
function f37(x = () => eval("a"), ...a) { 'use strict'; return x()[0] } assertEquals(4, f25(undefined, 4));
assertEquals(4, f37(undefined, 4));
function f38(x = () => { 'use strict'; return eval("a") }, ...a) { return x()[0] } var g1 = (x = function() { return a }, ...a) => { return x()[0] };
assertEquals(4, f38(undefined, 4)); assertEquals(4, g1(undefined, 4));
function f39(x = () => eval("'use strict'; a"), ...a) { return x()[0] } var g2 = (x = () => a, ...a) => { return x()[0] };
assertEquals(4, f39(undefined, 4)); assertEquals(4, g2(undefined, 4));
var g34 = (x = function() { return a }, ...a) => { return x()[0] };
assertEquals(4, g34(undefined, 4));
var g35 = (x = () => a, ...a) => { return x()[0] };
assertEquals(4, g35(undefined, 4));
})(); })();
(function TestParameterTDZStrict() {
"use strict";
function f1(a = eval("x"), x) { return a }
assertThrows(() => f1(undefined, 4), ReferenceError);
assertEquals(4, f1(4, 5));
function f2(a = () => eval("x"), x) { return a() }
assertEquals(4, f2(() => 4, 5));
function f11(a = eval("x"), x = 2) { return a }
assertThrows(() => f11(), ReferenceError);
assertThrows(() => f11(undefined), ReferenceError);
assertThrows(() => f11(undefined, 4), ReferenceError);
assertEquals(4, f11(4, 5));
function f21(x = () => eval("a"), ...a) { return x()[0] }
assertEquals(4, f21(undefined, 4));
})();
(function TestArgumentsForNonSimpleParameters() { (function TestArgumentsForNonSimpleParameters() {
function f1(x = 900) { arguments[0] = 1; return x } function f1(x = 900) { arguments[0] = 1; return x }
...@@ -429,3 +442,18 @@ ...@@ -429,3 +442,18 @@
assertEquals(1, (function(x, y = 1, z, v = 2) {}).length); assertEquals(1, (function(x, y = 1, z, v = 2) {}).length);
assertEquals(1, (function(x, y = 1, z, v = 2, ...a) {}).length); assertEquals(1, (function(x, y = 1, z, v = 2, ...a) {}).length);
})(); })();
(function TestDirectiveThrows() {
"use strict";
assertThrows(function(){ eval("function(x=1){'use strict';}") }, SyntaxError);
assertThrows(function(){ eval("(x=1) => {'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(class{foo(x=1) {'use strict';}});") }, SyntaxError);
assertThrows(
function(){ eval("function(a, x=1){'use strict';}") }, SyntaxError);
assertThrows(function(){ eval("(a, x=1) => {'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(class{foo(a, x=1) {'use strict';}});") }, SyntaxError);
})();
...@@ -741,16 +741,22 @@ ...@@ -741,16 +741,22 @@
assertEquals(1, f1({})); assertEquals(1, f1({}));
function f2({a = x}) { function x() {}; return a; } function f2({a = x}) { function x() {}; return a; }
assertEquals(1, f2({})); assertEquals(1, f2({}));
function f3({a = x}) { 'use strict'; let x = 2; return a; } (function() {
assertEquals(1, f3({})); 'use strict';
function f4({a = x}) { 'use strict'; const x = 2; return a; } function f3({a = x}) { let x = 2; return a; }
assertEquals(1, f4({})); assertEquals(1, f3({}));
function f5({a = x}) { 'use strict'; function x() {}; return a; } function f4({a = x}) { const x = 2; return a; }
assertEquals(1, f5({})); assertEquals(1, f4({}));
function f5({a = x}) { function x() {}; return a; }
assertEquals(1, f5({}));
})();
function f6({a = eval("x")}) { var x; return a; } function f6({a = eval("x")}) { var x; return a; }
assertEquals(1, f6({})); assertEquals(1, f6({}));
function f61({a = eval("x")}) { 'use strict'; var x; return a; } (function() {
assertEquals(1, f61({})); 'use strict';
function f61({a = eval("x")}) { var x; return a; }
assertEquals(1, f61({}));
})();
function f62({a = eval("'use strict'; x")}) { var x; return a; } function f62({a = eval("'use strict'; x")}) { var x; return a; }
assertEquals(1, f62({})); assertEquals(1, f62({}));
function f7({a = function() { return x }}) { var x; return a(); } function f7({a = function() { return x }}) { var x; return a(); }
...@@ -759,8 +765,11 @@ ...@@ -759,8 +765,11 @@
assertEquals(1, f8({})); assertEquals(1, f8({}));
function f9({a = () => eval("x")}) { var x; return a(); } function f9({a = () => eval("x")}) { var x; return a(); }
assertEquals(1, f9({})); assertEquals(1, f9({}));
function f91({a = () => eval("x")}) { 'use strict'; var x; return a(); } (function TestInitializedWithEvalArrowStrict() {
assertEquals(1, f91({})); 'use strict';
function f91({a = () => eval("x")}) { var x; return a(); }
assertEquals(1, f91({}));
})();
function f92({a = () => { 'use strict'; return eval("x") }}) { var x; return a(); } function f92({a = () => { 'use strict'; return eval("x") }}) { var x; return a(); }
assertEquals(1, f92({})); assertEquals(1, f92({}));
function f93({a = () => eval("'use strict'; x")}) { var x; return a(); } function f93({a = () => eval("'use strict'; x")}) { var x; return a(); }
...@@ -770,16 +779,22 @@ ...@@ -770,16 +779,22 @@
assertEquals(1, g1({})); assertEquals(1, g1({}));
var g2 = ({a = x}) => { function x() {}; return a; }; var g2 = ({a = x}) => { function x() {}; return a; };
assertEquals(1, g2({})); assertEquals(1, g2({}));
var g3 = ({a = x}) => { 'use strict'; let x = 2; return a; }; (function() {
assertEquals(1, g3({})); 'use strict';
var g4 = ({a = x}) => { 'use strict'; const x = 2; return a; }; var g3 = ({a = x}) => { let x = 2; return a; };
assertEquals(1, g4({})); assertEquals(1, g3({}));
var g5 = ({a = x}) => { 'use strict'; function x() {}; return a; }; var g4 = ({a = x}) => { const x = 2; return a; };
assertEquals(1, g5({})); assertEquals(1, g4({}));
var g5 = ({a = x}) => { function x() {}; return a; };
assertEquals(1, g5({}));
})();
var g6 = ({a = eval("x")}) => { var x; return a; }; var g6 = ({a = eval("x")}) => { var x; return a; };
assertEquals(1, g6({})); assertEquals(1, g6({}));
var g61 = ({a = eval("x")}) => { 'use strict'; var x; return a; }; (function() {
assertEquals(1, g61({})); 'use strict';
var g61 = ({a = eval("x")}) => { var x; return a; };
assertEquals(1, g61({}));
})();
var g62 = ({a = eval("'use strict'; x")}) => { var x; return a; }; var g62 = ({a = eval("'use strict'; x")}) => { var x; return a; };
assertEquals(1, g62({})); assertEquals(1, g62({}));
var g7 = ({a = function() { return x }}) => { var x; return a(); }; var g7 = ({a = function() { return x }}) => { var x; return a(); };
...@@ -788,10 +803,13 @@ ...@@ -788,10 +803,13 @@
assertEquals(1, g8({})); assertEquals(1, g8({}));
var g9 = ({a = () => eval("x")}) => { var x; return a(); }; var g9 = ({a = () => eval("x")}) => { var x; return a(); };
assertEquals(1, g9({})); assertEquals(1, g9({}));
var g91 = ({a = () => eval("x")}) => { 'use strict'; var x; return a(); }; (function() {
assertEquals(1, g91({})); 'use strict';
var g92 = ({a = () => { 'use strict'; return eval("x") }}) => { var x; return a(); }; var g91 = ({a = () => eval("x")}) => { var x; return a(); };
assertEquals(1, g92({})); assertEquals(1, g91({}));
var g92 = ({a = () => { return eval("x") }}) => { var x; return a(); };
assertEquals(1, g92({}));
})();
var g93 = ({a = () => eval("'use strict'; x")}) => { var x; return a(); }; var g93 = ({a = () => eval("'use strict'; x")}) => { var x; return a(); };
assertEquals(1, g93({})); assertEquals(1, g93({}));
...@@ -799,12 +817,15 @@ ...@@ -799,12 +817,15 @@
assertSame(f11, f11({})); assertSame(f11, f11({}));
var f12 = function f({x = f}) { function f() {}; return x; } var f12 = function f({x = f}) { function f() {}; return x; }
assertSame(f12, f12({})); assertSame(f12, f12({}));
var f13 = function f({x = f}) { 'use strict'; let f; return x; } (function() {
assertSame(f13, f13({})); 'use strict';
var f14 = function f({x = f}) { 'use strict'; const f = 0; return x; } var f13 = function f({x = f}) { let f; return x; }
assertSame(f14, f14({})); assertSame(f13, f13({}));
var f15 = function f({x = f}) { 'use strict'; function f() {}; return x; } var f14 = function f({x = f}) { const f = 0; return x; }
assertSame(f15, f15({})); assertSame(f14, f14({}));
var f15 = function f({x = f}) { function f() {}; return x; }
assertSame(f15, f15({}));
})();
var f16 = function f({f = 7, x = f}) { return x; } var f16 = function f({f = 7, x = f}) { return x; }
assertSame(7, f16({})); assertSame(7, f16({}));
...@@ -827,9 +848,12 @@ ...@@ -827,9 +848,12 @@
function f2({a = eval("x")}, x) { return a } function f2({a = eval("x")}, x) { return a }
assertThrows(() => f2({}, 4), ReferenceError); assertThrows(() => f2({}, 4), ReferenceError);
assertEquals(4, f2({a: 4}, 5)); assertEquals(4, f2({a: 4}, 5));
function f3({a = eval("x")}, x) { 'use strict'; return a } (function() {
assertThrows(() => f3({}, 4), ReferenceError); 'use strict';
assertEquals(4, f3({a: 4}, 5)); function f3({a = eval("x")}, x) { return a }
assertThrows(() => f3({}, 4), ReferenceError);
assertEquals(4, f3({a: 4}, 5));
})();
function f4({a = eval("'use strict'; x")}, x) { return a } function f4({a = eval("'use strict'; x")}, x) { return a }
assertThrows(() => f4({}, 4), ReferenceError); assertThrows(() => f4({}, 4), ReferenceError);
assertEquals(4, f4({a: 4}, 5)); assertEquals(4, f4({a: 4}, 5));
...@@ -838,8 +862,11 @@ ...@@ -838,8 +862,11 @@
assertEquals(4, f5({a: () => 4}, 5)); assertEquals(4, f5({a: () => 4}, 5));
function f6({a = () => eval("x")}, x) { return a() } function f6({a = () => eval("x")}, x) { return a() }
assertEquals(4, f6({a: () => 4}, 5)); assertEquals(4, f6({a: () => 4}, 5));
function f7({a = () => eval("x")}, x) { 'use strict'; return a() } (function() {
assertEquals(4, f7({a: () => 4}, 5)); 'use strict';
function f7({a = () => eval("x")}, x) { return a() }
assertEquals(4, f7({a: () => 4}, 5));
})();
function f8({a = () => eval("'use strict'; x")}, x) { return a() } function f8({a = () => eval("'use strict'; x")}, x) { return a() }
assertEquals(4, f8({a: () => 4}, 5)); assertEquals(4, f8({a: () => 4}, 5));
...@@ -849,9 +876,12 @@ ...@@ -849,9 +876,12 @@
function f12({a = eval("b")}, {b}) { return a } function f12({a = eval("b")}, {b}) { return a }
assertThrows(() => f12({}, {b: 4}), ReferenceError); assertThrows(() => f12({}, {b: 4}), ReferenceError);
assertEquals(4, f12({a: 4}, {b: 5})); assertEquals(4, f12({a: 4}, {b: 5}));
function f13({a = eval("b")}, {b}) { 'use strict'; return a } (function() {
assertThrows(() => f13({}, {b: 4}), ReferenceError); 'use strict';
assertEquals(4, f13({a: 4}, {b: 5})); function f13({a = eval("b")}, {b}) { return a }
assertThrows(() => f13({}, {b: 4}), ReferenceError);
assertEquals(4, f13({a: 4}, {b: 5}));
})();
function f14({a = eval("'use strict'; b")}, {b}) { return a } function f14({a = eval("'use strict'; b")}, {b}) { return a }
assertThrows(() => f14({}, {b: 4}), ReferenceError); assertThrows(() => f14({}, {b: 4}), ReferenceError);
assertEquals(4, f14({a: 4}, {b: 5})); assertEquals(4, f14({a: 4}, {b: 5}));
...@@ -860,8 +890,11 @@ ...@@ -860,8 +890,11 @@
assertEquals(4, f15({a: () => 4}, {b: 5})); assertEquals(4, f15({a: () => 4}, {b: 5}));
function f16({a = () => eval("b")}, {b}) { return a() } function f16({a = () => eval("b")}, {b}) { return a() }
assertEquals(4, f16({a: () => 4}, {b: 5})); assertEquals(4, f16({a: () => 4}, {b: 5}));
function f17({a = () => eval("b")}, {b}) { 'use strict'; return a() } (function() {
assertEquals(4, f17({a: () => 4}, {b: 5})); 'use strict';
function f17({a = () => eval("b")}, {b}) { return a() }
assertEquals(4, f17({a: () => 4}, {b: 5}));
})();
function f18({a = () => eval("'use strict'; b")}, {b}) { return a() } function f18({a = () => eval("'use strict'; b")}, {b}) { return a() }
assertEquals(4, f18({a: () => 4}, {b: 5})); assertEquals(4, f18({a: () => 4}, {b: 5}));
...@@ -885,8 +918,11 @@ ...@@ -885,8 +918,11 @@
assertEquals(4, f35({}, 4)); assertEquals(4, f35({}, 4));
function f36({x = () => eval("a")}, ...a) { return x()[0] } function f36({x = () => eval("a")}, ...a) { return x()[0] }
assertEquals(4, f36({}, 4)); assertEquals(4, f36({}, 4));
function f37({x = () => eval("a")}, ...a) { 'use strict'; return x()[0] } (function() {
assertEquals(4, f37({}, 4)); 'use strict';
function f37({x = () => eval("a")}, ...a) { return x()[0] }
assertEquals(4, f37({}, 4));
})();
function f38({x = () => { 'use strict'; return eval("a") }}, ...a) { return x()[0] } function f38({x = () => { 'use strict'; return eval("a") }}, ...a) { return x()[0] }
assertEquals(4, f38({}, 4)); assertEquals(4, f38({}, 4));
function f39({x = () => eval("'use strict'; a")}, ...a) { return x()[0] } function f39({x = () => eval("'use strict'; a")}, ...a) { return x()[0] }
...@@ -969,3 +1005,19 @@ ...@@ -969,3 +1005,19 @@
assertEquals(1, (function(x, {y} = {}, {z}, {v} = {}) {}).length); assertEquals(1, (function(x, {y} = {}, {z}, {v} = {}) {}).length);
assertEquals(1, (function({x}, {y} = {}, {z}, {v} = {}, ...a) {}).length); assertEquals(1, (function({x}, {y} = {}, {z}, {v} = {}, ...a) {}).length);
})(); })();
(function TestDirectiveThrows() {
"use strict";
assertThrows(function(){ eval("function({}){'use strict';}") }, SyntaxError);
assertThrows(function(){ eval("({}) => {'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(class{foo({}) {'use strict';}});") }, SyntaxError);
assertThrows(
function(){ eval("function(a, {}){'use strict';}") }, SyntaxError);
assertThrows(function(){ eval("(a, {}) => {'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(class{foo(a, {}) {'use strict';}});") }, SyntaxError);
})();
...@@ -16,18 +16,20 @@ ...@@ -16,18 +16,20 @@
return args.length; })(1,2,3,4,5)); return args.length; })(1,2,3,4,5));
})(); })();
function strictTest(a, b, ...c) { var strictTest = (function() {
"use strict"; "use strict";
assertEquals(Array, c.constructor); return function strictTest(a, b, ...c) {
assertTrue(Array.isArray(c)); assertEquals(Array, c.constructor);
assertTrue(Array.isArray(c));
var expectedLength = arguments.length >= 3 ? arguments.length - 2 : 0; var expectedLength = arguments.length >= 3 ? arguments.length - 2 : 0;
assertEquals(expectedLength, c.length); assertEquals(expectedLength, c.length);
for (var i = 2, j = 0; i < arguments.length; ++i) { for (var i = 2, j = 0; i < arguments.length; ++i) {
assertEquals(c[j++], arguments[i]); assertEquals(c[j++], arguments[i]);
} }
} };
})();
function sloppyTest(a, b, ...c) { function sloppyTest(a, b, ...c) {
assertEquals(Array, c.constructor); assertEquals(Array, c.constructor);
...@@ -144,14 +146,15 @@ var O = { ...@@ -144,14 +146,15 @@ var O = {
(function testNoAliasArgumentsStrict() { (function testNoAliasArgumentsStrict() {
function strictF(a, ...rest) { ((function() {
"use strict"; "use strict";
arguments[0] = 1; return (function strictF(a, ...rest) {
assertEquals(3, a); arguments[0] = 1;
arguments[1] = 2; assertEquals(3, a);
assertArrayEquals([4, 5], rest); arguments[1] = 2;
} assertArrayEquals([4, 5], rest);
strictF(3, 4, 5); });
})())(3, 4, 5);
})(); })();
...@@ -212,3 +215,21 @@ var O = { ...@@ -212,3 +215,21 @@ var O = {
assertEquals([1, 2, 3], c.child); assertEquals([1, 2, 3], c.child);
assertEquals([1, 2, 3], c.base); assertEquals([1, 2, 3], c.base);
})(); })();
(function TestDirectiveThrows() {
"use strict";
assertThrows(
function(){ eval("function(...rest){'use strict';}") }, SyntaxError);
assertThrows(function(){ eval("(...rest) => {'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(class{foo(...rest) {'use strict';}});") }, SyntaxError);
assertThrows(
function(){ eval("function(a, ...rest){'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(a, ...rest) => {'use strict';}") }, SyntaxError);
assertThrows(
function(){ eval("(class{foo(a, ...rest) {'use strict';}});") },
SyntaxError);
})();
...@@ -4,17 +4,28 @@ ...@@ -4,17 +4,28 @@
// Flags: --harmony-spreadcalls --harmony-sloppy --harmony-rest-parameters // Flags: --harmony-spreadcalls --harmony-sloppy --harmony-rest-parameters
(function testCallSuperProperty() { (function testCallSuperPropertyStrict() {
"use strict";
class BaseClass { class BaseClass {
strict_method(...args) { "use strict"; return [this].concat(args); } method(...args) { return [this].concat(args); }
sloppy_method(...args) { return [this].concat(args); }
} }
class SubClass extends BaseClass { class SubClass extends BaseClass {
strict_m(...args) { return super.strict_method(...args); } method(...args) { return super.method(...args); }
sloppy_m(...args) { return super.sloppy_method(...args); }
} }
var c = new SubClass(); var c = new SubClass();
assertEquals([c, 1, 2, 3, 4, 5], c.strict_m(1, 2, 3, 4, 5)); assertEquals([c, 1, 2, 3, 4, 5], c.method(1, 2, 3, 4, 5));
assertEquals([c, 1, 2, 3, 4, 5], c.sloppy_m(1, 2, 3, 4, 5)); })();
(function testCallSuperPropertySloppy() {
class BaseClass {
method(...args) { return [this].concat(args); }
}
class SubClass extends BaseClass {
method(...args) { return super.method(...args); }
}
var c = new SubClass();
assertEquals([c, 1, 2, 3, 4, 5], c.method(1, 2, 3, 4, 5));
})(); })();
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
// Flags: --harmony-arrow-functions --strong-mode --allow-natives-syntax // Flags: --harmony-arrow-functions --strong-mode --allow-natives-syntax
(function() { (function() {
function f({ x = function() { return []; } }) { "use strong"; return x(); } var f = (function() {
"use strong";
return function f({ x = function() { return []; } }) { return x(); };
})();
var a = f({ x: undefined }); var a = f({ x: undefined });
assertTrue(%IsStrong(a)); assertTrue(%IsStrong(a));
...@@ -19,7 +22,10 @@ ...@@ -19,7 +22,10 @@
assertFalse(%IsStrong(a)); assertFalse(%IsStrong(a));
function outerf() { return []; } function outerf() { return []; }
function f2({ x = outerf }) { "use strong"; return x(); } var f2 = (function() {
"use strong";
return function f2({ x = outerf }) { return x(); };
})();
a = f2({ x: undefined }); a = f2({ x: undefined });
assertFalse(%IsStrong(a)); assertFalse(%IsStrong(a));
})(); })();
...@@ -21,7 +21,7 @@ function generateArguments(n, prefix) { ...@@ -21,7 +21,7 @@ function generateArguments(n, prefix) {
} }
function generateParams(n) { function generateParams(n, directive_in_body) {
let a = []; let a = [];
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
a[i] = `p${i}`; a[i] = `p${i}`;
...@@ -29,13 +29,17 @@ function generateParams(n) { ...@@ -29,13 +29,17 @@ function generateParams(n) {
return a.join(', '); return a.join(', ');
} }
function generateParamsWithRest(n) { function generateParamsWithRest(n, directive_in_body) {
let a = []; let a = [];
let i = 0; let i = 0;
for (; i < n; i++) { for (; i < n; i++) {
a[i] = `p${i}`; a[i] = `p${i}`;
} }
a.push(`...p${i}`) if (!directive_in_body) {
// If language mode directive occurs in body, rest parameters will trigger
// an early error regardless of language mode.
a.push(`...p${i}`);
}
return a.join(', '); return a.join(', ');
} }
...@@ -76,6 +80,7 @@ function generateSpread(n) { ...@@ -76,6 +80,7 @@ function generateSpread(n) {
for (let call of calls) { for (let call of calls) {
let code = `'use strict'; ${def}; ${call};`; let code = `'use strict'; ${def}; ${call};`;
if (argumentCount < parameterCount) { if (argumentCount < parameterCount) {
print(code);
assertThrows(code, TypeError); assertThrows(code, TypeError);
} else { } else {
assertDoesNotThrow(code); assertDoesNotThrow(code);
...@@ -106,13 +111,13 @@ function generateSpread(n) { ...@@ -106,13 +111,13 @@ function generateSpread(n) {
for (let parameterCount = 0; parameterCount < 3; parameterCount++) { for (let parameterCount = 0; parameterCount < 3; parameterCount++) {
let defs = [ let defs = [
`let o = new class { `let o = new class {
m(${genParams(parameterCount)}) { 'use strong'; } m(${genParams(parameterCount, true)}) { 'use strong'; }
}`, }`,
`let o = new class { `let o = new class {
*m(${genParams(parameterCount)}) { 'use strong'; } *m(${genParams(parameterCount, true)}) { 'use strong'; }
}`, }`,
`let o = { m(${genParams(parameterCount)}) { 'use strong'; } }`, `let o = { m(${genParams(parameterCount, true)}) { 'use strong'; } }`,
`let o = { *m(${genParams(parameterCount)}) { 'use strong'; } }`, `let o = { *m(${genParams(parameterCount, true)}) { 'use strong'; } }`,
`'use strong'; `'use strong';
let o = new class { m(${genParams(parameterCount)}) {} }`, let o = new class { m(${genParams(parameterCount)}) {} }`,
`'use strong'; `'use strong';
...@@ -173,7 +178,7 @@ function generateSpread(n) { ...@@ -173,7 +178,7 @@ function generateSpread(n) {
class C { constructor(${genParams(parameterCount)}) {} }`, class C { constructor(${genParams(parameterCount)}) {} }`,
`'use strict'; `'use strict';
class C { class C {
constructor(${genParams(parameterCount)}) { 'use strong'; } constructor(${genParams(parameterCount, true)}) { 'use strong'; }
}`, }`,
]; ];
for (let def of defs) { for (let def of defs) {
...@@ -214,7 +219,7 @@ function generateSpread(n) { ...@@ -214,7 +219,7 @@ function generateSpread(n) {
}`, }`,
`'use strict'; `'use strict';
class B { class B {
constructor(${genParams(parameterCount)}) { 'use strong'; } constructor(${genParams(parameterCount, true)}) { 'use strong'; }
} }
class C extends B { class C extends B {
constructor() { constructor() {
...@@ -250,7 +255,7 @@ function generateSpread(n) { ...@@ -250,7 +255,7 @@ function generateSpread(n) {
class C extends B {}`, class C extends B {}`,
`'use strict'; `'use strict';
class B { class B {
constructor(${genParams(parameterCount)}) { 'use strong'; } constructor(${genParams(parameterCount, true)}) { 'use strong'; }
} }
class C extends B {}`, class C extends B {}`,
]; ];
......
...@@ -112,14 +112,14 @@ ...@@ -112,14 +112,14 @@
assertWeakArray({a: [], b: {}}.a); assertWeakArray({a: [], b: {}}.a);
})(); })();
(function StrongArrayLiterals(...args) { (function StrongArrayLiterals() {
'use strong'; 'use strong';
function assertStrongArray(x) { function assertStrongArray(x) {
assertTrue(%IsStrong(x)); assertTrue(%IsStrong(x));
assertSame(Array.prototype, Object.getPrototypeOf(x)); assertSame(Array.prototype, Object.getPrototypeOf(x));
} }
let [...r] = []; let [...r] = [];
assertStrongArray(args); assertStrongArray((function(...a) { return a; })());
assertStrongArray(r); assertStrongArray(r);
assertStrongArray([]); assertStrongArray([]);
assertStrongArray([1, 2, 3]); assertStrongArray([1, 2, 3]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment