Commit 70da8959 authored by marja@chromium.org's avatar marja@chromium.org

Implement handling of arrow functions in the parser

Arrow functions are parsed from ParseAssignmentExpression(). Handling the
parameter list is done by letting ParseConditionalExpression() parse a comma
separated list of identifiers, and it returns a tree of BinaryOperation nodes
with VariableProxy leaves, or a single VariableProxy if there is only one
parameter. When the arrow token "=>" is found, the VariableProxy nodes are
passed to ParseArrowFunctionLiteral(), which will then skip parsing the
paramaeter list. This avoids having to rewind when the arrow is found and
restart parsing the parameter list.

Note that the empty parameter list "()" is handled directly in
ParsePrimaryExpression(): after is has consumed the opening parenthesis,
if a closing parenthesis follows, then the only valid input is an arrow
function. In this case, ParsePrimaryExpression() directly calls
ParseArrowFunctionLiteral(), to avoid needing to return a sentinel value
to signal the empty parameter list. Because it will consume the body of
the arrow function, ParseAssignmentExpression() will not see the arrow
"=>" token as next, and return the already-parser expression.

The implementation is done in ParserBase, so it was needed to do some
additions to ParserBase, ParserTraits and PreParserTraits. Some of the
glue code can be removed later on when more more functionality is moved
to ParserBase.

Additionally, this adds a runtime flag "harmony_arrow_functions"
(disabled by default); enabling "harmony" will enable it as well.

BUG=v8:2700
LOG=N
R=marja@chromium.org

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

Patch from Adrián Pérez de Castro <aperez@igalia.com>.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22366 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 168742b8
......@@ -344,6 +344,11 @@ class Expression : public AstNode {
Bounds bounds() const { return bounds_; }
void set_bounds(Bounds bounds) { bounds_ = bounds; }
// Whether the expression is parenthesized
unsigned parenthesization_level() const { return parenthesization_level_; }
bool is_parenthesized() const { return parenthesization_level_ > 0; }
void increase_parenthesization_level() { ++parenthesization_level_; }
// Type feedback information for assignments and properties.
virtual bool IsMonomorphic() {
UNREACHABLE();
......@@ -370,6 +375,7 @@ class Expression : public AstNode {
: AstNode(pos),
zone_(zone),
bounds_(Bounds::Unbounded(zone)),
parenthesization_level_(0),
id_(GetNextId(zone)),
test_id_(GetNextId(zone)) {}
void set_to_boolean_types(byte types) { to_boolean_types_ = types; }
......@@ -379,6 +385,7 @@ class Expression : public AstNode {
private:
Bounds bounds_;
byte to_boolean_types_;
unsigned parenthesization_level_;
const BailoutId id_;
const TypeFeedbackId test_id_;
......
......@@ -165,6 +165,7 @@ DEFINE_BOOL(harmony_numeric_literals, false,
DEFINE_BOOL(harmony_strings, false, "enable harmony string")
DEFINE_BOOL(harmony_arrays, false, "enable harmony arrays")
DEFINE_BOOL(harmony_maths, false, "enable harmony math functions")
DEFINE_BOOL(harmony_arrow_functions, false, "enable harmony arrow functions")
DEFINE_BOOL(harmony, false, "enable all harmony features (except typeof)")
DEFINE_IMPLICATION(harmony, harmony_scoping)
......@@ -176,6 +177,7 @@ DEFINE_IMPLICATION(harmony, harmony_iteration)
DEFINE_IMPLICATION(harmony, harmony_numeric_literals)
DEFINE_IMPLICATION(harmony, harmony_strings)
DEFINE_IMPLICATION(harmony, harmony_arrays)
DEFINE_IMPLICATION(harmony, harmony_arrow_functions)
DEFINE_IMPLICATION(harmony_modules, harmony_scoping)
DEFINE_IMPLICATION(harmony_collections, harmony_symbols)
DEFINE_IMPLICATION(harmony_generators, harmony_symbols)
......
......@@ -154,6 +154,7 @@ var kMessages = {
strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"],
strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
strict_caller: ["Illegal access to a strict mode caller function."],
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."],
unprotected_let: ["Illegal let declaration in unprotected statement context."],
unprotected_const: ["Illegal const declaration in unprotected statement context."],
......
......@@ -718,6 +718,7 @@ Parser::Parser(CompilationInfo* info)
set_allow_lazy(false); // Must be explicitly enabled.
set_allow_generators(FLAG_harmony_generators);
set_allow_for_of(FLAG_harmony_iteration);
set_allow_arrow_functions(FLAG_harmony_arrow_functions);
set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
......@@ -3271,6 +3272,68 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
}
bool CheckAndDeclareArrowParameter(ParserTraits* traits, Expression* expression,
Scope* scope, int* num_params,
Scanner::Location* dupe_loc) {
// Case for empty parameter lists:
// () => ...
if (expression == NULL) return true;
// Too many parentheses around expression:
// (( ... )) => ...
if (expression->parenthesization_level() > 1) return false;
// Case for a single parameter:
// (foo) => ...
// foo => ...
if (expression->IsVariableProxy()) {
if (expression->AsVariableProxy()->is_this()) return false;
const AstRawString* raw_name = expression->AsVariableProxy()->raw_name();
if (traits->IsEvalOrArguments(raw_name) ||
traits->IsFutureStrictReserved(raw_name))
return false;
if (scope->IsDeclared(raw_name)) {
*dupe_loc = Scanner::Location(
expression->position(), expression->position() + raw_name->length());
return false;
}
scope->DeclareParameter(raw_name, VAR);
++(*num_params);
return true;
}
// Case for more than one parameter:
// (foo, bar [, ...]) => ...
if (expression->IsBinaryOperation()) {
BinaryOperation* binop = expression->AsBinaryOperation();
if (binop->op() != Token::COMMA || binop->left()->is_parenthesized() ||
binop->right()->is_parenthesized())
return false;
return CheckAndDeclareArrowParameter(traits, binop->left(), scope,
num_params, dupe_loc) &&
CheckAndDeclareArrowParameter(traits, binop->right(), scope,
num_params, dupe_loc);
}
// Any other kind of expression is not a valid parameter list.
return false;
}
int ParserTraits::DeclareArrowParametersFromExpression(
Expression* expression, Scope* scope, Scanner::Location* dupe_loc,
bool* ok) {
int num_params = 0;
*ok = CheckAndDeclareArrowParameter(this, expression, scope, &num_params,
dupe_loc);
return num_params;
}
FunctionLiteral* Parser::ParseFunctionLiteral(
const AstRawString* function_name,
Scanner::Location function_name_location,
......@@ -3680,6 +3743,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
reusable_preparser_->set_allow_lazy(true);
reusable_preparser_->set_allow_generators(allow_generators());
reusable_preparser_->set_allow_for_of(allow_for_of());
reusable_preparser_->set_allow_arrow_functions(allow_arrow_functions());
reusable_preparser_->set_allow_harmony_numeric_literals(
allow_harmony_numeric_literals());
}
......
......@@ -351,9 +351,13 @@ class ParserTraits {
// Used by FunctionState and BlockState.
typedef v8::internal::Scope Scope;
typedef v8::internal::Scope* ScopePtr;
typedef Variable GeneratorVariable;
typedef v8::internal::Zone Zone;
typedef v8::internal::AstProperties AstProperties;
typedef Vector<VariableProxy*> ParameterIdentifierVector;
// Return types for traversing functions.
typedef const AstRawString* Identifier;
typedef v8::internal::Expression* Expression;
......@@ -388,6 +392,7 @@ class ParserTraits {
// Helper functions for recursive descent.
bool IsEvalOrArguments(const AstRawString* identifier) const;
V8_INLINE bool IsFutureStrictReserved(const AstRawString* identifier) const;
// Returns true if the expression is of type "this.foo".
static bool IsThisProperty(Expression* expression);
......@@ -501,14 +506,19 @@ class ParserTraits {
static Expression* EmptyExpression() {
return NULL;
}
static Expression* EmptyArrowParamList() { return NULL; }
static Literal* EmptyLiteral() {
return NULL;
}
// Used in error return values.
static ZoneList<Expression*>* NullExpressionList() {
return NULL;
}
// Non-NULL empty string.
V8_INLINE const AstRawString* EmptyIdentifierString();
// Odd-ball literal creators.
Literal* GetLiteralTheHole(int position,
AstNodeFactory<AstConstructionVisitor>* factory);
......@@ -537,6 +547,13 @@ class ParserTraits {
ZoneList<v8::internal::Statement*>* NewStatementList(int size, Zone* zone) {
return new(zone) ZoneList<v8::internal::Statement*>(size, zone);
}
V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type);
// Utility functions
int DeclareArrowParametersFromExpression(Expression* expression, Scope* scope,
Scanner::Location* dupe_loc,
bool* ok);
V8_INLINE AstValueFactory* ast_value_factory();
// Temporary glue; these functions will move to ParserBase.
Expression* ParseV8Intrinsic(bool* ok);
......@@ -549,6 +566,14 @@ class ParserTraits {
FunctionLiteral::FunctionType type,
FunctionLiteral::ArityRestriction arity_restriction,
bool* ok);
V8_INLINE void SkipLazyFunctionBody(const AstRawString* name,
int* materialized_literal_count,
int* expected_property_count, bool* ok);
V8_INLINE ZoneList<Statement*>* ParseEagerFunctionBody(
const AstRawString* name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok);
V8_INLINE void CheckConflictingVarDeclarations(v8::internal::Scope* scope,
bool* ok);
private:
Parser* parser_;
......@@ -782,6 +807,50 @@ class Parser : public ParserBase<ParserTraits> {
};
bool ParserTraits::IsFutureStrictReserved(
const AstRawString* identifier) const {
return identifier->IsOneByteEqualTo("yield") ||
parser_->scanner()->IdentifierIsFutureStrictReserved(identifier);
}
Scope* ParserTraits::NewScope(Scope* parent_scope, ScopeType scope_type) {
return parser_->NewScope(parent_scope, scope_type);
}
const AstRawString* ParserTraits::EmptyIdentifierString() {
return parser_->ast_value_factory_->empty_string();
}
void ParserTraits::SkipLazyFunctionBody(const AstRawString* function_name,
int* materialized_literal_count,
int* expected_property_count,
bool* ok) {
return parser_->SkipLazyFunctionBody(
function_name, materialized_literal_count, expected_property_count, ok);
}
ZoneList<Statement*>* ParserTraits::ParseEagerFunctionBody(
const AstRawString* name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok) {
return parser_->ParseEagerFunctionBody(name, pos, fvar, fvar_init_op,
is_generator, ok);
}
void ParserTraits::CheckConflictingVarDeclarations(v8::internal::Scope* scope,
bool* ok) {
parser_->CheckConflictingVarDeclarations(scope, ok);
}
AstValueFactory* ParserTraits::ast_value_factory() {
return parser_->ast_value_factory_;
}
// Support for handling complex values (array and object literals) that
// can be fully handled at compile time.
class CompileTimeValue: public AllStatic {
......
This diff is collapsed.
......@@ -466,10 +466,12 @@ void Scanner::Scan() {
break;
case '=':
// = == ===
// = == === =>
Advance();
if (c0_ == '=') {
token = Select('=', Token::EQ_STRICT, Token::EQ);
} else if (c0_ == '>') {
token = Select(Token::ARROW);
} else {
token = Token::ASSIGN;
}
......@@ -1006,6 +1008,16 @@ static Token::Value KeywordOrIdentifierToken(const uint8_t* input,
}
bool Scanner::IdentifierIsFutureStrictReserved(
const AstRawString* string) const {
// Keywords are always 1-byte strings.
return string->is_one_byte() &&
Token::FUTURE_STRICT_RESERVED_WORD ==
KeywordOrIdentifierToken(string->raw_data(), string->length(),
harmony_scoping_, harmony_modules_);
}
Token::Value Scanner::ScanIdentifierOrKeyword() {
ASSERT(unicode_cache_->IsIdentifierStart(c0_));
LiteralScope literal(this);
......
......@@ -458,6 +458,8 @@ class Scanner {
return &source_mapping_url_;
}
bool IdentifierIsFutureStrictReserved(const AstRawString* string) const;
private:
// The current and look-ahead token.
struct TokenDesc {
......
This diff is collapsed.
This diff is collapsed.
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