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 { ...@@ -344,6 +344,11 @@ class Expression : public AstNode {
Bounds bounds() const { return bounds_; } Bounds bounds() const { return bounds_; }
void set_bounds(Bounds bounds) { bounds_ = 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. // Type feedback information for assignments and properties.
virtual bool IsMonomorphic() { virtual bool IsMonomorphic() {
UNREACHABLE(); UNREACHABLE();
...@@ -370,6 +375,7 @@ class Expression : public AstNode { ...@@ -370,6 +375,7 @@ class Expression : public AstNode {
: AstNode(pos), : AstNode(pos),
zone_(zone), zone_(zone),
bounds_(Bounds::Unbounded(zone)), bounds_(Bounds::Unbounded(zone)),
parenthesization_level_(0),
id_(GetNextId(zone)), id_(GetNextId(zone)),
test_id_(GetNextId(zone)) {} test_id_(GetNextId(zone)) {}
void set_to_boolean_types(byte types) { to_boolean_types_ = types; } void set_to_boolean_types(byte types) { to_boolean_types_ = types; }
...@@ -379,6 +385,7 @@ class Expression : public AstNode { ...@@ -379,6 +385,7 @@ class Expression : public AstNode {
private: private:
Bounds bounds_; Bounds bounds_;
byte to_boolean_types_; byte to_boolean_types_;
unsigned parenthesization_level_;
const BailoutId id_; const BailoutId id_;
const TypeFeedbackId test_id_; const TypeFeedbackId test_id_;
......
...@@ -165,6 +165,7 @@ DEFINE_BOOL(harmony_numeric_literals, false, ...@@ -165,6 +165,7 @@ DEFINE_BOOL(harmony_numeric_literals, false,
DEFINE_BOOL(harmony_strings, false, "enable harmony string") DEFINE_BOOL(harmony_strings, false, "enable harmony string")
DEFINE_BOOL(harmony_arrays, false, "enable harmony arrays") DEFINE_BOOL(harmony_arrays, false, "enable harmony arrays")
DEFINE_BOOL(harmony_maths, false, "enable harmony math functions") 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_BOOL(harmony, false, "enable all harmony features (except typeof)")
DEFINE_IMPLICATION(harmony, harmony_scoping) DEFINE_IMPLICATION(harmony, harmony_scoping)
...@@ -176,6 +177,7 @@ DEFINE_IMPLICATION(harmony, harmony_iteration) ...@@ -176,6 +177,7 @@ DEFINE_IMPLICATION(harmony, harmony_iteration)
DEFINE_IMPLICATION(harmony, harmony_numeric_literals) DEFINE_IMPLICATION(harmony, harmony_numeric_literals)
DEFINE_IMPLICATION(harmony, harmony_strings) DEFINE_IMPLICATION(harmony, harmony_strings)
DEFINE_IMPLICATION(harmony, harmony_arrays) DEFINE_IMPLICATION(harmony, harmony_arrays)
DEFINE_IMPLICATION(harmony, harmony_arrow_functions)
DEFINE_IMPLICATION(harmony_modules, harmony_scoping) DEFINE_IMPLICATION(harmony_modules, harmony_scoping)
DEFINE_IMPLICATION(harmony_collections, harmony_symbols) DEFINE_IMPLICATION(harmony_collections, harmony_symbols)
DEFINE_IMPLICATION(harmony_generators, harmony_symbols) DEFINE_IMPLICATION(harmony_generators, harmony_symbols)
......
...@@ -154,6 +154,7 @@ var kMessages = { ...@@ -154,6 +154,7 @@ var kMessages = {
strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"], 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_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."], 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."], generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."],
unprotected_let: ["Illegal let declaration in unprotected statement context."], unprotected_let: ["Illegal let declaration in unprotected statement context."],
unprotected_const: ["Illegal const declaration in unprotected statement context."], unprotected_const: ["Illegal const declaration in unprotected statement context."],
......
...@@ -718,6 +718,7 @@ Parser::Parser(CompilationInfo* info) ...@@ -718,6 +718,7 @@ Parser::Parser(CompilationInfo* info)
set_allow_lazy(false); // Must be explicitly enabled. set_allow_lazy(false); // Must be explicitly enabled.
set_allow_generators(FLAG_harmony_generators); set_allow_generators(FLAG_harmony_generators);
set_allow_for_of(FLAG_harmony_iteration); set_allow_for_of(FLAG_harmony_iteration);
set_allow_arrow_functions(FLAG_harmony_arrow_functions);
set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals); set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) { ++feature) {
...@@ -3271,6 +3272,68 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) { ...@@ -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( FunctionLiteral* Parser::ParseFunctionLiteral(
const AstRawString* function_name, const AstRawString* function_name,
Scanner::Location function_name_location, Scanner::Location function_name_location,
...@@ -3680,6 +3743,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( ...@@ -3680,6 +3743,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
reusable_preparser_->set_allow_lazy(true); reusable_preparser_->set_allow_lazy(true);
reusable_preparser_->set_allow_generators(allow_generators()); reusable_preparser_->set_allow_generators(allow_generators());
reusable_preparser_->set_allow_for_of(allow_for_of()); 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( reusable_preparser_->set_allow_harmony_numeric_literals(
allow_harmony_numeric_literals()); allow_harmony_numeric_literals());
} }
......
...@@ -351,9 +351,13 @@ class ParserTraits { ...@@ -351,9 +351,13 @@ class ParserTraits {
// Used by FunctionState and BlockState. // Used by FunctionState and BlockState.
typedef v8::internal::Scope Scope; typedef v8::internal::Scope Scope;
typedef v8::internal::Scope* ScopePtr;
typedef Variable GeneratorVariable; typedef Variable GeneratorVariable;
typedef v8::internal::Zone Zone; typedef v8::internal::Zone Zone;
typedef v8::internal::AstProperties AstProperties;
typedef Vector<VariableProxy*> ParameterIdentifierVector;
// Return types for traversing functions. // Return types for traversing functions.
typedef const AstRawString* Identifier; typedef const AstRawString* Identifier;
typedef v8::internal::Expression* Expression; typedef v8::internal::Expression* Expression;
...@@ -388,6 +392,7 @@ class ParserTraits { ...@@ -388,6 +392,7 @@ class ParserTraits {
// Helper functions for recursive descent. // Helper functions for recursive descent.
bool IsEvalOrArguments(const AstRawString* identifier) const; bool IsEvalOrArguments(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".
static bool IsThisProperty(Expression* expression); static bool IsThisProperty(Expression* expression);
...@@ -501,14 +506,19 @@ class ParserTraits { ...@@ -501,14 +506,19 @@ class ParserTraits {
static Expression* EmptyExpression() { static Expression* EmptyExpression() {
return NULL; return NULL;
} }
static Expression* EmptyArrowParamList() { return NULL; }
static Literal* EmptyLiteral() { static Literal* EmptyLiteral() {
return NULL; return NULL;
} }
// Used in error return values. // Used in error return values.
static ZoneList<Expression*>* NullExpressionList() { static ZoneList<Expression*>* NullExpressionList() {
return NULL; return NULL;
} }
// Non-NULL empty string.
V8_INLINE const AstRawString* EmptyIdentifierString();
// Odd-ball literal creators. // Odd-ball literal creators.
Literal* GetLiteralTheHole(int position, Literal* GetLiteralTheHole(int position,
AstNodeFactory<AstConstructionVisitor>* factory); AstNodeFactory<AstConstructionVisitor>* factory);
...@@ -537,6 +547,13 @@ class ParserTraits { ...@@ -537,6 +547,13 @@ class ParserTraits {
ZoneList<v8::internal::Statement*>* NewStatementList(int size, Zone* zone) { ZoneList<v8::internal::Statement*>* NewStatementList(int size, Zone* zone) {
return new(zone) ZoneList<v8::internal::Statement*>(size, 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. // Temporary glue; these functions will move to ParserBase.
Expression* ParseV8Intrinsic(bool* ok); Expression* ParseV8Intrinsic(bool* ok);
...@@ -549,6 +566,14 @@ class ParserTraits { ...@@ -549,6 +566,14 @@ class ParserTraits {
FunctionLiteral::FunctionType type, FunctionLiteral::FunctionType type,
FunctionLiteral::ArityRestriction arity_restriction, FunctionLiteral::ArityRestriction arity_restriction,
bool* ok); 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: private:
Parser* parser_; Parser* parser_;
...@@ -782,6 +807,50 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -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 // Support for handling complex values (array and object literals) that
// can be fully handled at compile time. // can be fully handled at compile time.
class CompileTimeValue: public AllStatic { class CompileTimeValue: public AllStatic {
......
This diff is collapsed.
...@@ -466,10 +466,12 @@ void Scanner::Scan() { ...@@ -466,10 +466,12 @@ void Scanner::Scan() {
break; break;
case '=': case '=':
// = == === // = == === =>
Advance(); Advance();
if (c0_ == '=') { if (c0_ == '=') {
token = Select('=', Token::EQ_STRICT, Token::EQ); token = Select('=', Token::EQ_STRICT, Token::EQ);
} else if (c0_ == '>') {
token = Select(Token::ARROW);
} else { } else {
token = Token::ASSIGN; token = Token::ASSIGN;
} }
...@@ -1006,6 +1008,16 @@ static Token::Value KeywordOrIdentifierToken(const uint8_t* input, ...@@ -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() { Token::Value Scanner::ScanIdentifierOrKeyword() {
ASSERT(unicode_cache_->IsIdentifierStart(c0_)); ASSERT(unicode_cache_->IsIdentifierStart(c0_));
LiteralScope literal(this); LiteralScope literal(this);
......
...@@ -458,6 +458,8 @@ class Scanner { ...@@ -458,6 +458,8 @@ class Scanner {
return &source_mapping_url_; return &source_mapping_url_;
} }
bool IdentifierIsFutureStrictReserved(const AstRawString* string) const;
private: private:
// The current and look-ahead token. // The current and look-ahead token.
struct TokenDesc { struct TokenDesc {
......
...@@ -42,6 +42,7 @@ namespace internal { ...@@ -42,6 +42,7 @@ namespace internal {
T(CONDITIONAL, "?", 3) \ T(CONDITIONAL, "?", 3) \
T(INC, "++", 0) \ T(INC, "++", 0) \
T(DEC, "--", 0) \ T(DEC, "--", 0) \
T(ARROW, "=>", 0) \
\ \
/* Assignment operators. */ \ /* Assignment operators. */ \
/* IsAssignmentOp() and Assignment::is_compound() relies on */ \ /* IsAssignmentOp() and Assignment::is_compound() relies on */ \
......
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