DONT_OPTIMIZE_NODE(ModulePath)
DONT_OPTIMIZE_NODE(ModuleUrl)
DONT_OPTIMIZE_NODE(ModuleStatement)
+DONT_OPTIMIZE_NODE(Yield)
DONT_OPTIMIZE_NODE(WithStatement)
DONT_OPTIMIZE_NODE(TryCatchStatement)
DONT_OPTIMIZE_NODE(TryFinallyStatement)
V(ObjectLiteral) \
V(ArrayLiteral) \
V(Assignment) \
+ V(Yield) \
V(Throw) \
V(Property) \
V(Call) \
};
+class Yield: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Yield)
+
+ Expression* expression() const { return expression_; }
+ bool is_delegating_yield() const { return is_delegating_yield_; }
+ virtual int position() const { return pos_; }
+
+ protected:
+ Yield(Isolate* isolate,
+ Expression* expression,
+ bool is_delegating_yield,
+ int pos)
+ : Expression(isolate),
+ expression_(expression),
+ is_delegating_yield_(is_delegating_yield),
+ pos_(pos) { }
+
+ private:
+ Expression* expression_;
+ bool is_delegating_yield_;
+ int pos_;
+};
+
+
class Throw: public Expression {
public:
DECLARE_NODE_TYPE(Throw)
kNotParenthesized
};
+ enum IsGeneratorFlag {
+ kIsGenerator,
+ kNotGenerator
+ };
+
DECLARE_NODE_TYPE(FunctionLiteral)
Handle<String> name() const { return name_; }
bitfield_ = IsParenthesized::update(bitfield_, kIsParenthesized);
}
+ bool is_generator() {
+ return IsGenerator::decode(bitfield_) == kIsGenerator;
+ }
+
int ast_node_count() { return ast_properties_.node_count(); }
AstProperties::Flags* flags() { return ast_properties_.flags(); }
void set_ast_properties(AstProperties* ast_properties) {
Type type,
ParameterFlag has_duplicate_parameters,
IsFunctionFlag is_function,
- IsParenthesizedFlag is_parenthesized)
+ IsParenthesizedFlag is_parenthesized,
+ IsGeneratorFlag is_generator)
: Expression(isolate),
name_(name),
scope_(scope),
Pretenure::encode(false) |
HasDuplicateParameters::encode(has_duplicate_parameters) |
IsFunction::encode(is_function) |
- IsParenthesized::encode(is_parenthesized);
+ IsParenthesized::encode(is_parenthesized) |
+ IsGenerator::encode(is_generator);
}
private:
class HasDuplicateParameters: public BitField<ParameterFlag, 4, 1> {};
class IsFunction: public BitField<IsFunctionFlag, 5, 1> {};
class IsParenthesized: public BitField<IsParenthesizedFlag, 6, 1> {};
+ class IsGenerator: public BitField<IsGeneratorFlag, 7, 1> {};
};
VISIT_AND_RETURN(Assignment, assign)
}
+ Yield* NewYield(Expression* expression, bool is_delegating_yield, int pos) {
+ Yield* yield =
+ new(zone_) Yield(isolate_, expression, is_delegating_yield, pos);
+ VISIT_AND_RETURN(Yield, yield)
+ }
+
Throw* NewThrow(Expression* exception, int pos) {
Throw* t = new(zone_) Throw(isolate_, exception, pos);
VISIT_AND_RETURN(Throw, t)
FunctionLiteral::ParameterFlag has_duplicate_parameters,
FunctionLiteral::Type type,
FunctionLiteral::IsFunctionFlag is_function,
- FunctionLiteral::IsParenthesizedFlag is_parenthesized) {
+ FunctionLiteral::IsParenthesizedFlag is_parenthesized,
+ FunctionLiteral::IsGeneratorFlag is_generator) {
FunctionLiteral* lit = new(zone_) FunctionLiteral(
isolate_, name, scope, body,
materialized_literal_count, expected_property_count, handler_count,
has_only_simple_this_property_assignments, this_property_assignments,
parameter_count, type, has_duplicate_parameters, is_function,
- is_parenthesized);
+ is_parenthesized, is_generator);
// Top-level literal doesn't count for the AST's properties.
if (is_function == FunctionLiteral::kIsFunction) {
visitor_.VisitFunctionLiteral(lit);
function_info->set_dont_optimize(lit->flags()->Contains(kDontOptimize));
function_info->set_dont_inline(lit->flags()->Contains(kDontInline));
function_info->set_dont_cache(lit->flags()->Contains(kDontCache));
+ function_info->set_is_generator(lit->is_generator());
}
"enable harmony object observation (implies harmony collections")
DEFINE_bool(harmony_typed_arrays, false,
"enable harmony typed arrays")
+DEFINE_bool(harmony_generators, false, "enable harmony generators")
DEFINE_bool(harmony, false, "enable all harmony features (except typeof)")
DEFINE_implication(harmony, harmony_scoping)
DEFINE_implication(harmony, harmony_modules)
DEFINE_implication(harmony, harmony_proxies)
DEFINE_implication(harmony, harmony_collections)
DEFINE_implication(harmony, harmony_observation)
+DEFINE_implication(harmony, harmony_generators)
DEFINE_implication(harmony_modules, harmony_scoping)
DEFINE_implication(harmony_observation, harmony_collections)
DEFINE_implication(harmony, harmony_typed_arrays)
}
+void BreakableStatementChecker::VisitYield(Yield* expr) {
+ // Yield is breakable if the expression is.
+ Visit(expr->expression());
+}
+
+
void BreakableStatementChecker::VisitThrow(Throw* expr) {
// Throw is breakable if the expression is.
Visit(expr->exception());
}
+void FullCodeGenerator::VisitYield(Yield* expr) {
+ if (expr->is_delegating_yield())
+ UNIMPLEMENTED();
+
+ Comment cmnt(masm_, "[ Yield");
+ VisitForAccumulatorValue(expr->expression());
+ // TODO(wingo): Assert that the operand stack depth is 0, at least while
+ // general yield expressions are unimplemented.
+
+ // TODO(wingo): What follows is as in VisitReturnStatement. Replace it with a
+ // call to a builtin that will resume the generator.
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ while (current != NULL) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+ EmitReturnSequence();
+}
+
+
void FullCodeGenerator::VisitThrow(Throw* expr) {
Comment cmnt(masm_, "[ Throw");
VisitForStackValue(expr->exception());
bool HOptimizedGraphBuilder::BuildGraph() {
+ if (info()->function()->is_generator()) {
+ Bailout("function is a generator");
+ return false;
+ }
Scope* scope = info()->scope();
if (scope->HasIllegalRedeclaration()) {
Bailout("function with illegal redeclaration");
}
+void HOptimizedGraphBuilder::VisitYield(Yield* expr) {
+ // Generators are not optimized, so we should never get here.
+ UNREACHABLE();
+}
+
+
void HOptimizedGraphBuilder::VisitThrow(Throw* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
kDontOptimize)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_inline, kDontInline)
BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_cache, kDontCache)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_generator, kIsGenerator)
void SharedFunctionInfo::BeforeVisitingPointers() {
if (IsInobjectSlackTrackingInProgress()) DetachInitialMap();
// Indicates that code for this function cannot be cached.
DECL_BOOLEAN_ACCESSORS(dont_cache)
+ // Indicates that this function is a generator.
+ DECL_BOOLEAN_ACCESSORS(is_generator)
+
// Indicates whether or not the code in the shared function support
// deoptimization.
inline bool has_deoptimization_support();
kDontOptimize,
kDontInline,
kDontCache,
+ kIsGenerator,
kCompilerHintsCount // Pseudo entry
};
Parser::FunctionState::FunctionState(Parser* parser,
Scope* scope,
+ bool is_generator,
Isolate* isolate)
: next_materialized_literal_index_(JSFunction::kLiteralsPrefixSize),
next_handler_index_(0),
expected_property_count_(0),
+ is_generator_(is_generator),
only_simple_this_property_assignments_(false),
this_property_assignments_(isolate->factory()->empty_fixed_array()),
parser_(parser),
}
ParsingModeScope parsing_mode(this, mode);
- FunctionState function_state(this, scope, isolate()); // Enters 'scope'.
+ bool is_generator = false;
+ // Enters 'scope'.
+ FunctionState function_state(this, scope, is_generator, isolate());
+
top_scope_->SetLanguageMode(info->language_mode());
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
bool ok = true;
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::ANONYMOUS_EXPRESSION,
FunctionLiteral::kGlobalOrEval,
- FunctionLiteral::kNotParenthesized);
+ FunctionLiteral::kNotParenthesized,
+ FunctionLiteral::kNotGenerator);
result->set_ast_properties(factory()->visitor()->ast_properties());
} else if (stack_overflow_) {
isolate()->StackOverflow();
scope = Scope::DeserializeScopeChain(info()->closure()->context(), scope,
zone());
}
- FunctionState function_state(this, scope, isolate());
+ bool is_generator = false; // Top scope is not a generator.
+ FunctionState function_state(this, scope, is_generator, isolate());
ASSERT(scope->language_mode() != STRICT_MODE || !info()->is_classic_mode());
ASSERT(scope->language_mode() != EXTENDED_MODE ||
info()->is_extended_mode());
bool ok = true;
result = ParseFunctionLiteral(name,
false, // Strict mode name already checked.
+ shared_info->is_generator(),
RelocInfo::kNoPosition,
type,
&ok);
// ModuleDeclaration
// ImportDeclaration
// ExportDeclaration
+ // GeneratorDeclaration
switch (peek()) {
case Token::FUNCTION:
// 'export' Identifier (',' Identifier)* ';'
// 'export' VariableDeclaration
// 'export' FunctionDeclaration
+ // 'export' GeneratorDeclaration
// 'export' ModuleDeclaration
//
// TODO(ES6): implement structuring ExportSpecifiers
// BlockElement (aka SourceElement):
// LetDeclaration
// ConstDeclaration
+ // GeneratorDeclaration
switch (peek()) {
case Token::FUNCTION:
// FunctionDeclaration
// Common language extension is to allow function declaration in place
// of any statement. This language extension is disabled in strict mode.
+ //
+ // In Harmony mode, this case also handles the extension:
+ // Statement:
+ // GeneratorDeclaration
if (!top_scope_->is_classic_mode()) {
ReportMessageAt(scanner().peek_location(), "strict_function",
Vector<const char*>::empty());
Statement* Parser::ParseFunctionDeclaration(ZoneStringList* names, bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
+ // GeneratorDeclaration ::
+ // 'function' '*' Identifier '(' FormalParameterListopt ')'
+ // '{' FunctionBody '}'
Expect(Token::FUNCTION, CHECK_OK);
int function_token_position = scanner().location().beg_pos;
+ bool is_generator = FLAG_harmony_generators && Check(Token::MUL);
bool is_strict_reserved = false;
Handle<String> name = ParseIdentifierOrStrictReservedWord(
&is_strict_reserved, CHECK_OK);
FunctionLiteral* fun = ParseFunctionLiteral(name,
is_strict_reserved,
+ is_generator,
function_token_position,
FunctionLiteral::DECLARATION,
CHECK_OK);
Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
// AssignmentExpression ::
// ConditionalExpression
+ // YieldExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
+ if (peek() == Token::YIELD && is_generator()) {
+ return ParseYieldExpression(ok);
+ }
+
if (fni_ != NULL) fni_->Enter();
Expression* expression = ParseConditionalExpression(accept_IN, CHECK_OK);
}
+Expression* Parser::ParseYieldExpression(bool* ok) {
+ // YieldExpression ::
+ // 'yield' '*'? AssignmentExpression
+ int position = scanner().peek_location().beg_pos;
+ Expect(Token::YIELD, CHECK_OK);
+ bool is_yield_star = Check(Token::MUL);
+ Expression* expression = ParseAssignmentExpression(false, CHECK_OK);
+ return factory()->NewYield(expression, is_yield_star, position);
+}
+
+
// Precedence = 3
Expression* Parser::ParseConditionalExpression(bool accept_IN, bool* ok) {
// ConditionalExpression ::
if (peek() == Token::FUNCTION) {
Expect(Token::FUNCTION, CHECK_OK);
int function_token_position = scanner().location().beg_pos;
+ bool is_generator = FLAG_harmony_generators && Check(Token::MUL);
Handle<String> name;
bool is_strict_reserved_name = false;
if (peek_any_identifier()) {
: FunctionLiteral::NAMED_EXPRESSION;
result = ParseFunctionLiteral(name,
is_strict_reserved_name,
+ is_generator,
function_token_position,
type,
CHECK_OK);
break;
case Token::IDENTIFIER:
+ case Token::YIELD:
case Token::FUTURE_STRICT_RESERVED_WORD: {
Handle<String> name = ParseIdentifier(CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
FunctionLiteral* value =
ParseFunctionLiteral(name,
false, // reserved words are allowed here
+ false, // not a generator
RelocInfo::kNoPosition,
FunctionLiteral::ANONYMOUS_EXPRESSION,
CHECK_OK);
FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
bool name_is_strict_reserved,
+ bool is_generator,
int function_token_position,
FunctionLiteral::Type type,
bool* ok) {
FunctionLiteral::IsParenthesizedFlag parenthesized = parenthesized_function_
? FunctionLiteral::kIsParenthesized
: FunctionLiteral::kNotParenthesized;
+ FunctionLiteral::IsGeneratorFlag generator = is_generator
+ ? FunctionLiteral::kIsGenerator
+ : FunctionLiteral::kNotGenerator;
AstProperties ast_properties;
// Parse function body.
- { FunctionState function_state(this, scope, isolate());
+ { FunctionState function_state(this, scope, is_generator, isolate());
top_scope_->SetScopeName(function_name);
// FormalParameterList ::
duplicate_parameters,
type,
FunctionLiteral::kIsFunction,
- parenthesized);
+ parenthesized,
+ generator);
function_literal->set_function_token_position(function_token_position);
function_literal->set_ast_properties(&ast_properties);
stack_limit,
do_allow_lazy,
allow_natives_syntax_,
- allow_modules_);
+ allow_modules_,
+ FLAG_harmony_generators);
}
preparser::PreParser::PreParseResult result =
reusable_preparser_->PreParseLazyFunction(top_scope_->language_mode(),
+ is_generator(),
logger);
return result;
}
Token::Value next = peek();
return next == Token::IDENTIFIER ||
next == Token::FUTURE_RESERVED_WORD ||
- next == Token::FUTURE_STRICT_RESERVED_WORD;
+ next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ next == Token::YIELD;
}
// Parses an identifier that is valid for the current scope, in particular it
// fails on strict mode future reserved keywords in a strict scope.
Handle<String> Parser::ParseIdentifier(bool* ok) {
- if (!top_scope_->is_classic_mode()) {
- Expect(Token::IDENTIFIER, ok);
- } else if (!Check(Token::IDENTIFIER)) {
- Expect(Token::FUTURE_STRICT_RESERVED_WORD, ok);
+ Token::Value next = Next();
+ if (next == Token::IDENTIFIER ||
+ (top_scope_->is_classic_mode() &&
+ (next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ (next == Token::YIELD && !is_generator())))) {
+ return GetSymbol(ok);
+ } else {
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return Handle<String>();
}
- if (!*ok) return Handle<String>();
- return GetSymbol(ok);
}
// whether it is strict mode future reserved.
Handle<String> Parser::ParseIdentifierOrStrictReservedWord(
bool* is_strict_reserved, bool* ok) {
- *is_strict_reserved = false;
- if (!Check(Token::IDENTIFIER)) {
- Expect(Token::FUTURE_STRICT_RESERVED_WORD, ok);
+ Token::Value next = Next();
+ if (next == Token::IDENTIFIER) {
+ *is_strict_reserved = false;
+ } else if (next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ (next == Token::YIELD && !is_generator())) {
*is_strict_reserved = true;
+ } else {
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return Handle<String>();
}
- if (!*ok) return Handle<String>();
return GetSymbol(ok);
}
if (FLAG_lazy && (extension == NULL)) {
flags |= kAllowLazy;
}
+ if (FLAG_harmony_generators) {
+ flags |= kAllowGenerators;
+ }
CompleteParserRecorder recorder;
return DoPreParse(source, flags, &recorder);
}
public:
FunctionState(Parser* parser,
Scope* scope,
+ bool is_generator,
Isolate* isolate);
~FunctionState();
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
+ bool is_generator() const { return is_generator_; }
+
AstNodeFactory<AstConstructionVisitor>* factory() { return &factory_; }
private:
// Properties count estimation.
int expected_property_count_;
+ // Indicates that this function is a generator.
+ bool is_generator_;
+
// Keeps track of assignments to properties of this. Used for
// optimizing constructors.
bool only_simple_this_property_assignments_;
Expression* ParseExpression(bool accept_IN, bool* ok);
Expression* ParseAssignmentExpression(bool accept_IN, bool* ok);
+ Expression* ParseYieldExpression(bool* ok);
Expression* ParseConditionalExpression(bool accept_IN, bool* ok);
Expression* ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
Expression* ParseUnaryExpression(bool* ok);
ZoneList<Expression*>* ParseArguments(bool* ok);
FunctionLiteral* ParseFunctionLiteral(Handle<String> var_name,
bool name_is_reserved,
+ bool is_generator,
int function_token_position,
FunctionLiteral::Type type,
bool* ok);
return scanner().Next();
}
+ bool is_generator() const { return current_function_state_->is_generator(); }
+
bool peek_any_identifier();
INLINE(void Consume(Token::Value token));
namespace preparser {
PreParser::PreParseResult PreParser::PreParseLazyFunction(
- i::LanguageMode mode, i::ParserRecorder* log) {
+ i::LanguageMode mode, bool is_generator, i::ParserRecorder* log) {
log_ = log;
// Lazy functions always have trivial outer scopes (no with/catch scopes).
Scope top_scope(&scope_, kTopLevelScope);
set_language_mode(mode);
Scope function_scope(&scope_, kFunctionScope);
+ function_scope.set_is_generator(is_generator);
ASSERT_EQ(i::Token::LBRACE, scanner_->current_token());
bool ok = true;
int start_position = scanner_->peek_location().beg_pos;
// SourceElement:
// LetDeclaration
// ConstDeclaration
+ // GeneratorDeclaration
switch (peek()) {
case i::Token::FUNCTION:
PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
+ // GeneratorDeclaration ::
+ // 'function' '*' Identifier '(' FormalParameterListopt ')'
+ // '{' FunctionBody '}'
Expect(i::Token::FUNCTION, CHECK_OK);
+ bool is_generator = allow_generators_ && Check(i::Token::MUL);
Identifier identifier = ParseIdentifier(CHECK_OK);
i::Scanner::Location location = scanner_->location();
- Expression function_value = ParseFunctionLiteral(CHECK_OK);
+ Expression function_value = ParseFunctionLiteral(is_generator, CHECK_OK);
if (function_value.IsStrictFunction() &&
!identifier.IsValidStrictVariable()) {
// Strict mode violation, using either reserved word or eval/arguments
// as name of strict function.
const char* type = "strict_function_name";
- if (identifier.IsFutureStrictReserved()) {
+ if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
type = "strict_reserved_word";
}
ReportMessageAt(location, type, NULL);
Expression expr = ParseExpression(true, CHECK_OK);
if (expr.IsRawIdentifier()) {
ASSERT(!expr.AsIdentifier().IsFutureReserved());
- ASSERT(is_classic_mode() || !expr.AsIdentifier().IsFutureStrictReserved());
+ ASSERT(is_classic_mode() ||
+ (!expr.AsIdentifier().IsFutureStrictReserved() &&
+ !expr.AsIdentifier().IsYield()));
if (peek() == i::Token::COLON) {
Consume(i::Token::COLON);
return ParseStatement(ok);
bool* ok) {
// AssignmentExpression ::
// ConditionalExpression
+ // YieldExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
+ if (scope_->is_generator() && peek() == i::Token::YIELD) {
+ return ParseYieldExpression(ok);
+ }
+
i::Scanner::Location before = scanner_->peek_location();
Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK);
// Precedence = 3
+PreParser::Expression PreParser::ParseYieldExpression(bool* ok) {
+ // YieldExpression ::
+ // 'yield' '*'? AssignmentExpression
+ Consume(i::Token::YIELD);
+ Check(i::Token::MUL);
+
+ ParseAssignmentExpression(false, CHECK_OK);
+
+ return Expression::Default();
+}
+
+
+// Precedence = 3
PreParser::Expression PreParser::ParseConditionalExpression(bool accept_IN,
bool* ok) {
// ConditionalExpression ::
Expression result = Expression::Default();
if (peek() == i::Token::FUNCTION) {
Consume(i::Token::FUNCTION);
+
+ bool is_generator = allow_generators_ && Check(i::Token::MUL);
Identifier identifier = Identifier::Default();
if (peek_any_identifier()) {
identifier = ParseIdentifier(CHECK_OK);
}
- result = ParseFunctionLiteral(CHECK_OK);
+ result = ParseFunctionLiteral(is_generator, CHECK_OK);
if (result.IsStrictFunction() && !identifier.IsValidStrictVariable()) {
StrictModeIdentifierViolation(scanner_->location(),
"strict_function_name",
case i::Token::FUTURE_RESERVED_WORD:
case i::Token::FUTURE_STRICT_RESERVED_WORD:
+ case i::Token::YIELD:
case i::Token::IDENTIFIER: {
Identifier id = ParseIdentifier(CHECK_OK);
result = Expression::FromIdentifier(id);
}
PropertyType type = is_getter ? kGetterProperty : kSetterProperty;
CheckDuplicate(&duplicate_finder, name, type, CHECK_OK);
- ParseFunctionLiteral(CHECK_OK);
+ ParseFunctionLiteral(false, CHECK_OK);
if (peek() != i::Token::RBRACE) {
Expect(i::Token::COMMA, CHECK_OK);
}
}
-PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
+PreParser::Expression PreParser::ParseFunctionLiteral(bool is_generator,
+ bool* ok) {
// Function ::
// '(' FormalParameterList? ')' '{' FunctionBody '}'
ScopeType outer_scope_type = scope_->type();
bool inside_with = scope_->IsInsideWith();
Scope function_scope(&scope_, kFunctionScope);
+ function_scope.set_is_generator(is_generator);
// FormalParameterList ::
// '(' (Identifier)*[','] ')'
Expect(i::Token::LPAREN, CHECK_OK);
} else if (scanner_->current_token() ==
i::Token::FUTURE_STRICT_RESERVED_WORD) {
return Identifier::FutureStrictReserved();
+ } else if (scanner_->current_token() == i::Token::YIELD) {
+ return Identifier::Yield();
}
if (scanner_->is_literal_ascii()) {
// Detect strict-mode poison words.
*ok = false;
return GetIdentifierSymbol();
}
+ case i::Token::YIELD:
+ if (scope_->is_generator()) {
+ // 'yield' in a generator is only valid as part of a YieldExpression.
+ ReportMessageAt(scanner_->location(), "unexpected_token", "yield");
+ *ok = false;
+ return Identifier::Yield();
+ }
+ // FALLTHROUGH
case i::Token::FUTURE_STRICT_RESERVED_WORD:
if (!is_classic_mode()) {
i::Scanner::Location location = scanner_->location();
const char* type = eval_args_type;
if (identifier.IsFutureReserved()) {
type = "reserved_word";
- } else if (identifier.IsFutureStrictReserved()) {
+ } else if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
type = "strict_reserved_word";
}
if (!is_classic_mode()) {
i::Token::Value next = peek();
return next == i::Token::IDENTIFIER ||
next == i::Token::FUTURE_RESERVED_WORD ||
- next == i::Token::FUTURE_STRICT_RESERVED_WORD;
+ next == i::Token::FUTURE_STRICT_RESERVED_WORD ||
+ next == i::Token::YIELD;
}
uintptr_t stack_limit,
bool allow_lazy,
bool allow_natives_syntax,
- bool allow_modules)
+ bool allow_modules,
+ bool allow_generators)
: scanner_(scanner),
log_(log),
scope_(NULL),
allow_lazy_(allow_lazy),
allow_modules_(allow_modules),
allow_natives_syntax_(allow_natives_syntax),
+ allow_generators_(allow_generators),
parenthesized_function_(false),
harmony_scoping_(scanner->HarmonyScoping()) { }
bool allow_lazy = (flags & i::kAllowLazy) != 0;
bool allow_natives_syntax = (flags & i::kAllowNativesSyntax) != 0;
bool allow_modules = (flags & i::kAllowModules) != 0;
+ bool allow_generators = (flags & i::kAllowGenerators) != 0;
return PreParser(scanner, log, stack_limit, allow_lazy,
- allow_natives_syntax, allow_modules).PreParse();
+ allow_natives_syntax, allow_modules,
+ allow_generators).PreParse();
}
// Parses a single function literal, from the opening parentheses before
// parameters to the closing brace after the body.
// Returns a FunctionEntry describing the body of the function in enough
// detail that it can be lazily compiled.
- // The scanner is expected to have matched the "function" keyword and
- // parameters, and have consumed the initial '{'.
+ // The scanner is expected to have matched the "function" or "function*"
+ // keyword and parameters, and have consumed the initial '{'.
// At return, unless an error occurred, the scanner is positioned before the
// the final '}'.
PreParseResult PreParseLazyFunction(i::LanguageMode mode,
+ bool is_generator,
i::ParserRecorder* log);
private:
static Identifier FutureStrictReserved() {
return Identifier(kFutureStrictReservedIdentifier);
}
+ static Identifier Yield() {
+ return Identifier(kYieldIdentifier);
+ }
bool IsEval() { return type_ == kEvalIdentifier; }
bool IsArguments() { return type_ == kArgumentsIdentifier; }
bool IsEvalOrArguments() { return type_ >= kEvalIdentifier; }
+ bool IsYield() { return type_ == kYieldIdentifier; }
bool IsFutureReserved() { return type_ == kFutureReservedIdentifier; }
bool IsFutureStrictReserved() {
return type_ == kFutureStrictReservedIdentifier;
kUnknownIdentifier,
kFutureReservedIdentifier,
kFutureStrictReservedIdentifier,
+ kYieldIdentifier,
kEvalIdentifier,
kArgumentsIdentifier
};
// Identifiers and string literals can be parenthesized.
// They no longer work as labels or directive prologues,
// but are still recognized in other contexts.
- return Expression(code_ | kParentesizedExpressionFlag);
+ return Expression(code_ | kParenthesizedExpressionFlag);
}
// For other types of expressions, it's not important to remember
// the parentheses.
kUseStrictString = kStringLiteralFlag | 8,
kStringLiteralMask = kUseStrictString,
- kParentesizedExpressionFlag = 4, // Only if identifier or string literal.
+ // Only if identifier or string literal.
+ kParenthesizedExpressionFlag = 4,
// Below here applies if neither identifier nor string literal.
kThisExpression = 4,
expected_properties_(0),
with_nesting_count_(0),
language_mode_(
- (prev_ != NULL) ? prev_->language_mode() : i::CLASSIC_MODE) {
+ (prev_ != NULL) ? prev_->language_mode() : i::CLASSIC_MODE),
+ is_generator_(false) {
*variable = this;
}
~Scope() { *variable_ = prev_; }
int expected_properties() { return expected_properties_; }
int materialized_literal_count() { return materialized_literal_count_; }
bool IsInsideWith() { return with_nesting_count_ != 0; }
+ bool is_generator() { return is_generator_; }
+ void set_is_generator(bool is_generator) { is_generator_ = is_generator; }
bool is_classic_mode() {
return language_mode_ == i::CLASSIC_MODE;
}
int expected_properties_;
int with_nesting_count_;
i::LanguageMode language_mode_;
+ bool is_generator_;
};
// Preparse the program. Only called in PreParseProgram after creating
Expression ParseExpression(bool accept_IN, bool* ok);
Expression ParseAssignmentExpression(bool accept_IN, bool* ok);
+ Expression ParseYieldExpression(bool* ok);
Expression ParseConditionalExpression(bool accept_IN, bool* ok);
Expression ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
Expression ParseUnaryExpression(bool* ok);
Expression ParseV8Intrinsic(bool* ok);
Arguments ParseArguments(bool* ok);
- Expression ParseFunctionLiteral(bool* ok);
+ Expression ParseFunctionLiteral(bool is_generator, bool* ok);
void ParseLazyFunctionLiteralBody(bool* ok);
Identifier ParseIdentifier(bool* ok);
bool allow_lazy_;
bool allow_modules_;
bool allow_natives_syntax_;
+ bool allow_generators_;
bool parenthesized_function_;
bool harmony_scoping_;
};
}
+void PrettyPrinter::VisitYield(Yield* node) {
+ Print("yield ");
+ Visit(node->expression());
+}
+
+
void PrettyPrinter::VisitThrow(Throw* node) {
Print("throw ");
Visit(node->exception());
}
+void AstPrinter::VisitYield(Yield* node) {
+ PrintIndentedVisit("YIELD", node->expression());
+}
+
+
void AstPrinter::VisitThrow(Throw* node) {
PrintIndentedVisit("THROW", node->exception());
}
KEYWORD("while", Token::WHILE) \
KEYWORD("with", Token::WITH) \
KEYWORD_GROUP('y') \
- KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD)
+ KEYWORD("yield", Token::YIELD)
static Token::Value KeywordOrIdentifierToken(const char* input,
kLanguageModeMask = 0x03,
kAllowLazy = 0x04,
kAllowNativesSyntax = 0x08,
- kAllowModules = 0x10
+ kAllowModules = 0x10,
+ kAllowGenerators = 0x20
};
STATIC_ASSERT((kLanguageModeMask & CLASSIC_MODE) == CLASSIC_MODE);
K(EXPORT, "export", 0) \
K(IMPORT, "import", 0) \
K(LET, "let", 0) \
+ K(YIELD, "yield", 0) \
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-generators
+
+// Test basic generator syntax.
+
+// Yield statements.
+function* g() { yield 3; yield 4; }
+
+// Yield expressions.
+function* g() { (yield 3) + (yield 4); }
+
+// You can have a generator in strict mode.
+function* g() { "use strict"; yield 3; yield 4; }
+
+// Generator expression.
+(function* () { yield 3; });
+
+// Named generator expression.
+(function* g() { yield 3; });
+
+// A generator without a yield is specified as causing an early error. This
+// behavior is currently unimplemented. See
+// https://bugs.ecmascript.org/show_bug.cgi?id=1283.
+function* g() { }
+
+// A YieldExpression in the RHS of a YieldExpression is currently specified as
+// causing an early error. This behavior is currently unimplemented. See
+// https://bugs.ecmascript.org/show_bug.cgi?id=1283.
+function* g() { yield yield 1; }
+function* g() { yield 3 + (yield 4); }
+
+// Generator definitions with a name of "yield" are not specifically ruled out
+// by the spec, as the `yield' name is outside the generator itself. However,
+// in strict-mode, "yield" is an invalid identifier.
+function* yield() { (yield 3) + (yield 4); }
+assertThrows("function* yield() { \"use strict\"; (yield 3) + (yield 4); }",
+ SyntaxError);
+
+// In classic mode, yield is a normal identifier, outside of generators.
+function yield(yield) { yield: yield (yield + yield (0)); }
+
+// Yield is always valid as a key in an object literal.
+({ yield: 1 });
+function* g() { yield ({ yield: 1 }) }
+function* g() { yield ({ get yield() { return 1; }}) }
+
+// Checks that yield is a valid label in classic mode, but not valid in a strict
+// mode or in generators.
+function f() { yield: 1 }
+assertThrows("function f() { \"use strict\"; yield: 1 }", SyntaxError)
+assertThrows("function f*() { yield: 1 }", SyntaxError)
+
+// Yield is only a keyword in the body of the generator, not in nested
+// functions.
+function* g() { function f() { yield (yield + yield (0)); } }
+
+// Yield needs a RHS.
+assertThrows("function* g() { yield; }", SyntaxError);
+
+// Yield in a generator is not an identifier.
+assertThrows("function* g() { yield = 10; }", SyntaxError);
+
+// Yield binds very loosely, so this parses as "yield (3 + yield 4)", which is
+// invalid.
+assertThrows("function* g() { yield 3 + yield 4; }", SyntaxError);
+
+// Yield is still a future-reserved-word in strict mode
+assertThrows("function f() { \"use strict\"; var yield = 13; }", SyntaxError);
+
+// The name of the NFE is let-bound in G, so is invalid.
+assertThrows("function* g() { yield (function yield() {}); }", SyntaxError);
+
+// In generators, yield is invalid as a formal argument name.
+assertThrows("function* g(yield) { yield (10); }", SyntaxError);