Commit 9355457b authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

Implement parsing of ES6 Rest Parameters

BUG=v8:2159
LOG=N
R=marja@chromium.org, arv@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#26362}
parent 3df0a9ae
......@@ -1599,6 +1599,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_templates)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_sloppy)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_unicode)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_computed_property_names)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_rest_parameters)
void Genesis::InstallNativeFunctions_harmony_proxies() {
......@@ -1629,6 +1630,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_proxies)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_templates)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_sloppy)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_computed_property_names)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_rest_parameters)
void Genesis::InitializeGlobal_harmony_regexps() {
Handle<JSObject> builtins(native_context()->builtins());
......@@ -2208,6 +2210,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_sloppy_natives[] = {NULL};
static const char* harmony_unicode_natives[] = {NULL};
static const char* harmony_computed_property_names_natives[] = {NULL};
static const char* harmony_rest_parameters_natives[] = {NULL};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
......
......@@ -186,7 +186,8 @@ DEFINE_IMPLICATION(es_staging, harmony)
V(harmony_proxies, "harmony proxies") \
V(harmony_sloppy, "harmony features in sloppy mode") \
V(harmony_unicode, "harmony unicode escapes") \
V(harmony_computed_property_names, "harmony computed property names")
V(harmony_computed_property_names, "harmony computed property names") \
V(harmony_rest_parameters, "harmony rest parameters")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
......
......@@ -816,6 +816,7 @@ Parser::Parser(CompilationInfo* info, ParseInfo* parse_info)
set_allow_harmony_unicode(FLAG_harmony_unicode);
set_allow_harmony_computed_property_names(
FLAG_harmony_computed_property_names);
set_allow_harmony_rest_params(FLAG_harmony_rest_parameters);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -3675,11 +3676,17 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
Scanner::Location dupe_error_loc = Scanner::Location::invalid();
Scanner::Location reserved_loc = Scanner::Location::invalid();
bool is_rest = false;
bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
(peek() == Token::RPAREN &&
arity_restriction != FunctionLiteral::SETTER_ARITY);
while (!done) {
bool is_strict_reserved = false;
is_rest = peek() == Token::ELLIPSIS && allow_harmony_rest_params();
if (is_rest) {
Consume(Token::ELLIPSIS);
}
const AstRawString* param_name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
......@@ -3695,7 +3702,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
dupe_error_loc = scanner()->location();
}
Variable* var = scope_->DeclareParameter(param_name, VAR);
Variable* var = scope_->DeclareParameter(param_name, VAR, is_rest);
if (scope->strict_mode() == SLOPPY) {
// TODO(sigurds) Mark every parameter as maybe assigned. This is a
// conservative approximation necessary to account for parameters
......@@ -3711,7 +3718,14 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
}
if (arity_restriction == FunctionLiteral::SETTER_ARITY) break;
done = (peek() == Token::RPAREN);
if (!done) Expect(Token::COMMA, CHECK_OK);
if (!done) {
if (is_rest) {
ReportMessageAt(scanner()->peek_location(), "param_after_rest");
*ok = false;
return NULL;
}
Expect(Token::COMMA, CHECK_OK);
}
}
Expect(Token::RPAREN, CHECK_OK);
......@@ -3794,7 +3808,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// Validate strict mode.
// Concise methods use StrictFormalParameters.
if (strict_mode() == STRICT || IsConciseMethod(kind)) {
// Functions for which IsSimpleParameterList() returns false use
// StrictFormalParameters.
if (strict_mode() == STRICT || IsConciseMethod(kind) || is_rest) {
CheckStrictFunctionNameAndParameters(function_name,
name_is_strict_reserved,
function_name_location,
......@@ -3979,6 +3995,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
reusable_preparser_->set_allow_harmony_unicode(allow_harmony_unicode());
reusable_preparser_->set_allow_harmony_computed_property_names(
allow_harmony_computed_property_names());
reusable_preparser_->set_allow_harmony_rest_params(
allow_harmony_rest_params());
}
PreParser::PreParseResult result =
reusable_preparser_->PreParseLazyFunction(strict_mode(),
......
......@@ -878,11 +878,17 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
Scanner::Location dupe_error_loc = Scanner::Location::invalid();
Scanner::Location reserved_error_loc = Scanner::Location::invalid();
bool is_rest = false;
bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
(peek() == Token::RPAREN &&
arity_restriction != FunctionLiteral::SETTER_ARITY);
while (!done) {
bool is_strict_reserved = false;
is_rest = peek() == Token::ELLIPSIS && allow_harmony_rest_params();
if (is_rest) {
Consume(Token::ELLIPSIS);
}
Identifier param_name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
if (!eval_args_error_loc.IsValid() && param_name.IsEvalOrArguments()) {
......@@ -900,7 +906,14 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
if (arity_restriction == FunctionLiteral::SETTER_ARITY) break;
done = (peek() == Token::RPAREN);
if (!done) Expect(Token::COMMA, CHECK_OK);
if (!done) {
if (is_rest) {
ReportMessageAt(scanner()->peek_location(), "param_after_rest");
*ok = false;
return Expression::Default();
}
Expect(Token::COMMA, CHECK_OK);
}
}
Expect(Token::RPAREN, CHECK_OK);
......@@ -921,7 +934,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
// Validate strict mode. We can do this only after parsing the function,
// since the function can declare itself strict.
// Concise methods use StrictFormalParameters.
if (strict_mode() == STRICT || IsConciseMethod(kind)) {
if (strict_mode() == STRICT || IsConciseMethod(kind) || is_rest) {
if (function_name.IsEvalOrArguments()) {
ReportMessageAt(function_name_location, "strict_eval_arguments");
*ok = false;
......
......@@ -89,7 +89,8 @@ class ParserBase : public Traits {
allow_harmony_arrow_functions_(false),
allow_harmony_object_literals_(false),
allow_harmony_sloppy_(false),
allow_harmony_computed_property_names_(false) {}
allow_harmony_computed_property_names_(false),
allow_harmony_rest_params_(false) {}
// Getters that indicate whether certain syntactical constructs are
// allowed to be parsed by this instance of the parser.
......@@ -113,6 +114,9 @@ class ParserBase : public Traits {
bool allow_harmony_computed_property_names() const {
return allow_harmony_computed_property_names_;
}
bool allow_harmony_rest_params() const {
return allow_harmony_rest_params_;
}
// Setters that determine whether certain syntactical constructs are
// allowed to be parsed by this instance of the parser.
......@@ -148,6 +152,9 @@ class ParserBase : public Traits {
void set_allow_harmony_computed_property_names(bool allow) {
allow_harmony_computed_property_names_ = allow;
}
void set_allow_harmony_rest_params(bool allow) {
allow_harmony_rest_params_ = allow;
}
protected:
enum AllowEvalOrArgumentsAsIdentifier {
......@@ -625,6 +632,7 @@ class ParserBase : public Traits {
bool allow_harmony_object_literals_;
bool allow_harmony_sloppy_;
bool allow_harmony_computed_property_names_;
bool allow_harmony_rest_params_;
};
......
......@@ -599,6 +599,15 @@ void Scanner::Scan() {
token = ScanNumber(true);
} else {
token = Token::PERIOD;
if (c0_ == '.') {
Advance();
if (c0_ == '.') {
Advance();
token = Token::ELLIPSIS;
} else {
PushBack('.');
}
}
}
break;
......
......@@ -180,6 +180,8 @@ void Scope::SetDefaults(ScopeType scope_type,
num_heap_slots_ = 0;
num_modules_ = 0;
module_var_ = NULL,
rest_parameter_ = NULL;
rest_index_ = -1;
scope_info_ = scope_info;
start_position_ = RelocInfo::kNoPosition;
end_position_ = RelocInfo::kNoPosition;
......@@ -450,11 +452,17 @@ Variable* Scope::Lookup(const AstRawString* name) {
}
Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode) {
Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode,
bool is_rest) {
DCHECK(!already_resolved());
DCHECK(is_function_scope());
Variable* var = variables_.Declare(this, name, mode, true, Variable::NORMAL,
kCreatedInitialized);
if (is_rest) {
DCHECK_NULL(rest_parameter_);
rest_parameter_ = var;
rest_index_ = num_parameters();
}
params_.Add(var, zone());
return var;
}
......@@ -1286,12 +1294,18 @@ void Scope::AllocateParameterLocals() {
uses_sloppy_arguments = strict_mode() == SLOPPY;
}
if (rest_parameter_ && !MustAllocate(rest_parameter_)) {
rest_parameter_ = NULL;
}
// The same parameter may occur multiple times in the parameters_ list.
// If it does, and if it is not copied into the context object, it must
// receive the highest parameter index for that parameter; thus iteration
// order is relevant!
for (int i = params_.length() - 1; i >= 0; --i) {
Variable* var = params_[i];
if (var == rest_parameter_) continue;
DCHECK(var->scope() == this);
if (uses_sloppy_arguments || has_forced_context_allocation()) {
// Force context allocation of the parameter.
......@@ -1359,6 +1373,10 @@ void Scope::AllocateNonParameterLocals() {
if (function_ != NULL) {
AllocateNonParameterLocal(function_->proxy()->var());
}
if (rest_parameter_) {
AllocateNonParameterLocal(rest_parameter_);
}
}
......
......@@ -126,7 +126,8 @@ class Scope: public ZoneObject {
// Declare a parameter in this scope. When there are duplicated
// parameters the rightmost one 'wins'. However, the implementation
// expects all parameters to be declared and from left to right.
Variable* DeclareParameter(const AstRawString* name, VariableMode mode);
Variable* DeclareParameter(const AstRawString* name, VariableMode mode,
bool is_rest = false);
// Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned.
......@@ -358,8 +359,32 @@ class Scope: public ZoneObject {
return params_[index];
}
// Returns the default function arity --- does not include rest parameters.
int default_function_length() const {
int count = params_.length();
if (rest_index_ >= 0) {
DCHECK(count > 0);
DCHECK(is_function_scope());
--count;
}
return count;
}
int num_parameters() const { return params_.length(); }
// A function can have at most one rest parameter. Returns Variable* or NULL.
Variable* rest_parameter(int* index) const {
*index = rest_index_;
if (rest_index_ < 0) return NULL;
return rest_parameter_;
}
bool is_simple_parameter_list() const {
DCHECK(is_function_scope());
if (rest_index_ >= 0) return false;
return true;
}
// The local variable 'arguments' if we need to allocate it; NULL otherwise.
Variable* arguments() const { return arguments_; }
......@@ -555,6 +580,10 @@ class Scope: public ZoneObject {
// For module scopes, the host scope's internal variable binding this module.
Variable* module_var_;
// Rest parameter
Variable* rest_parameter_;
int rest_index_;
// Serialized scope info support.
Handle<ScopeInfo> scope_info_;
bool already_resolved() { return already_resolved_; }
......
......@@ -40,6 +40,7 @@ namespace internal {
T(COLON, ":", 0) \
T(SEMICOLON, ";", 0) \
T(PERIOD, ".", 0) \
T(ELLIPSIS, "...", 0) \
T(CONDITIONAL, "?", 3) \
T(INC, "++", 0) \
T(DEC, "--", 0) \
......
......@@ -1356,6 +1356,7 @@ enum ParserFlag {
kAllowHarmonyArrowFunctions,
kAllowHarmonyClasses,
kAllowHarmonyObjectLiterals,
kAllowHarmonyRestParameters,
kAllowHarmonyTemplates,
kAllowHarmonySloppy,
kAllowHarmonyUnicode,
......@@ -1384,6 +1385,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
flags.Contains(kAllowHarmonyArrowFunctions));
parser->set_allow_harmony_classes(flags.Contains(kAllowHarmonyClasses));
parser->set_allow_harmony_templates(flags.Contains(kAllowHarmonyTemplates));
parser->set_allow_harmony_rest_params(
flags.Contains(kAllowHarmonyRestParameters));
parser->set_allow_harmony_sloppy(flags.Contains(kAllowHarmonySloppy));
parser->set_allow_harmony_unicode(flags.Contains(kAllowHarmonyUnicode));
parser->set_allow_harmony_computed_property_names(
......@@ -4569,6 +4572,66 @@ TEST(TemplateLiteralsIllegalTokens) {
}
TEST(ParseRestParameters) {
const char* context_data[][2] = {{"'use strict';(function(",
"){ return args;})(1, [], /regexp/, 'str',"
"function(){});"},
{"(function(", "){ return args;})(1, [],"
"/regexp/, 'str', function(){});"},
{NULL, NULL}};
const char* data[] = {
"...args",
"a, ...args",
"... args",
"a, ... args",
"...\targs",
"a, ...\targs",
"...\r\nargs",
"a, ...\r\nargs",
"...\rargs",
"a, ...\rargs",
"...\t\n\t\t\n args",
"a, ... \n \n args",
NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyRestParameters};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(ParseRestParametersErrors) {
const char* context_data[][2] = {{"'use strict';(function(",
"){ return args;}(1, [], /regexp/, 'str',"
"function(){});"},
{"(function(", "){ return args;}(1, [],"
"/regexp/, 'str', function(){});"},
{NULL, NULL}};
const char* data[] = {
"...args, b",
"a, ...args, b",
"...args, b",
"a, ...args, b",
"...args,\tb",
"a,...args\t,b",
"...args\r\n, b",
"a, ... args,\r\nb",
"...args\r,b",
"a, ... args,\rb",
"...args\t\n\t\t\n, b",
"a, ... args, \n \n b",
"a, a, ...args",
"a,\ta, ...args",
"a,\ra, ...args",
"a,\na, ...args",
NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyRestParameters};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(LexicalScopingSloppyMode) {
const char* context_data[][2] = {
{"", ""},
......
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