Commit 7367720d 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 ParseFunctionLiteral(),
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 ParseExpression() expects parenthesized expressions
to not be empty, so checking for a closing parenthesis is added in
handling the empty parameter list "()" will accept a right-paren and
return an empty expression, which means that the parameter list is
empty.

Additionally, this adds the following machinery:

 - A runtime flag "harmony_arrow_functions" (disabled by default).
   Enabling "harmony" will enable it as well.
 - An IsArrow bit in SharedFunctionInfo, and accessors for it.
 - An IsArrow bit in FunctionLiteral, accessorts for it, and
   a constructor parameter to set its value.
 - In ParserBase: allow_arrow_functions() and set_allow_arrow_functions()
 - A V8 native %FunctionIsArrow(), which is used to skip adding the
   "function " prefix when getting the source code for an arrow
   function.

R=marja@chromium.org

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

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22265 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 72441a11
......@@ -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)
......
......@@ -157,6 +157,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."],
......
......@@ -782,6 +782,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) {
......@@ -3321,6 +3322,57 @@ Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
}
bool CheckAndCollectArrowParameter(ParserTraits* traits,
Collector<VariableProxy*>* collector,
Expression* expression) {
// 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;
collector->Add(expression->AsVariableProxy());
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 CheckAndCollectArrowParameter(traits, collector, binop->left()) &&
CheckAndCollectArrowParameter(traits, collector, binop->right());
}
// Any other kind of expression is not a valid parameter list.
return false;
}
Vector<VariableProxy*> ParserTraits::ParameterListFromExpression(
Expression* expression, bool* ok) {
Collector<VariableProxy*> collector;
*ok = CheckAndCollectArrowParameter(this, &collector, expression);
return collector.ToVector();
}
FunctionLiteral* Parser::ParseFunctionLiteral(
const AstRawString* function_name,
Scanner::Location function_name_location,
......@@ -3740,6 +3792,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());
}
......
......@@ -385,9 +385,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;
......@@ -422,6 +426,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);
......@@ -535,14 +540,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);
......@@ -571,6 +581,12 @@ 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
Vector<VariableProxy*> ParameterListFromExpression(Expression* expression,
bool* ok);
V8_INLINE AstValueFactory* ast_value_factory();
// Temporary glue; these functions will move to ParserBase.
Expression* ParseV8Intrinsic(bool* ok);
......@@ -583,6 +599,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_;
......@@ -827,6 +851,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