Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(327)

Unified Diff: src/parsing/parser.cc

Issue 1643903003: [generators] Desugar yield*. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Refer to ResumeMode Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/parsing/parser.h ('k') | src/parsing/parser-base.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/parsing/parser.cc
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index 1b5b7e6779be608e70c4fdc487871cc94bce30fd..c9b3d495a3699d4900e3ea24bee7af61632a282c 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -5797,5 +5797,574 @@ void ParserTraits::SetFunctionNameFromIdentifierRef(Expression* value,
}
+// Desugaring of yield*
+// ====================
+//
+// With the help of do-expressions and function.sent, we desugar yield* into a
+// loop containing a "raw" yield (a yield that doesn't wrap an iterator result
+// object around its argument). Concretely, "yield* iterable" turns into
+// roughly the following code:
+//
+// do {
+// const kNext = 0;
+// const kReturn = 1;
+// const kThrow = 2;
+//
+// let input = function.sent;
+// let mode = kNext;
+// let output = undefined;
+//
+// let iterator = iterable[Symbol.iterator]();
+// if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid);
+//
+// while (true) {
+// // From the generator to the iterator:
+// // Forward input according to resume mode and obtain output.
+// switch (mode) {
+// case kNext:
+// output = iterator.next(input);
+// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
+// break;
+// case kReturn:
+// IteratorClose(iterator, input); // See below.
+// break;
+// case kThrow:
+// let iteratorThrow = iterator.throw;
+// if (IS_NULL_OR_UNDEFINED(iteratorThrow)) {
+// IteratorClose(iterator); // See below.
+// throw MakeTypeError(kThrowMethodMissing);
+// }
+// output = %_Call(iteratorThrow, iterator, input);
+// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
+// break;
+// }
+// if (output.done) break;
+//
+// // From the generator to its user:
+// // Forward output, receive new input, and determine resume mode.
+// mode = kReturn;
+// try {
+// try {
+// RawYield(output); // See explanation above.
+// mode = kNext;
+// } catch (error) {
+// mode = kThrow;
+// }
+// } finally {
+// input = function.sent;
+// continue;
+// }
+// }
+//
+// output.value;
+// }
+//
+// IteratorClose(iterator) expands to the following:
+//
+// let iteratorReturn = iterator.return;
+// if (IS_NULL_OR_UNDEFINED(iteratorReturn)) return;
+// let result = %_Call(iteratorReturn, iterator);
+// if (!IS_RECEIVER(result)) %ThrowIterResultNotAnObject(result);
+//
+// IteratorClose(iterator, input) expands to the following:
+//
+// let iteratorReturn = iterator.return;
+// if (IS_NULL_OR_UNDEFINED(iteratorReturn)) return input;
+// let result = %_Call(iteratorReturn, iterator, input);
+// if (!IS_RECEIVER(result)) %ThrowIterResultNotAnObject(result);
+
+
+Expression* ParserTraits::RewriteYieldStar(
+ Expression* generator, Expression* iterable, int pos) {
+
+ const int nopos = RelocInfo::kNoPosition;
+
+ auto factory = parser_->factory();
+ auto avfactory = parser_->ast_value_factory();
+ auto scope = parser_->scope_;
+ auto zone = parser_->zone();
+
+ Statement* skip = factory->NewEmptyStatement(nopos);
+
+ enum { kNext, kReturn, kThrow };
+ // TODO(neis): Use JSGenerator::ResumeMode once extended with RETURN.
+
+ // Forward definition for break/continue statements.
+ WhileStatement* loop = factory->NewWhileStatement(nullptr, nopos);
+
+
+ // let input = undefined;
+ Variable* var_input = scope->NewTemporary(avfactory->empty_string());
+ Statement* initialize_input;
+ {
+ Expression* input_proxy = factory->NewVariableProxy(var_input);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, input_proxy, factory->NewUndefinedLiteral(nopos), nopos);
+ initialize_input = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // let mode = kNext;
+ Variable* var_mode = scope->NewTemporary(avfactory->empty_string());
+ Statement* initialize_mode;
+ {
+ Expression* mode_proxy = factory->NewVariableProxy(var_mode);
+ Expression* knext = factory->NewSmiLiteral(kNext, nopos);
+ Expression* assignment =
+ factory->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
+ initialize_mode = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // let output = undefined;
+ Variable* var_output = scope->NewTemporary(avfactory->empty_string());
+ Statement* initialize_output;
+ {
+ Expression* output_proxy = factory->NewVariableProxy(var_output);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, output_proxy, factory->NewUndefinedLiteral(nopos),
+ nopos);
+ initialize_output = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // let iterator = iterable[Symbol.iterator];
+ Variable* var_iterator = scope->NewTemporary(avfactory->empty_string());
+ Statement* get_iterator;
+ {
+ Expression* iterator = GetIterator(iterable, factory, nopos);
+ Expression* iterator_proxy = factory->NewVariableProxy(var_iterator);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, iterator_proxy, iterator, nopos);
+ get_iterator = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid);
+ Statement* validate_iterator;
+ {
+ Expression* is_receiver_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var_iterator), zone);
+ is_receiver_call =
+ factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
+ }
+
+ Statement* throw_call;
+ {
+ Expression* call = NewThrowTypeError(
+ MessageTemplate::kSymbolIteratorInvalid, avfactory->empty_string(),
+ nopos);
+ throw_call = factory->NewExpressionStatement(call, nopos);
+ }
+
+ validate_iterator =
+ factory->NewIfStatement(is_receiver_call, skip, throw_call, nopos);
+ }
+
+
+ // output = iterator.next(input);
+ Statement* call_next;
+ {
+ Expression* iterator_proxy = factory->NewVariableProxy(var_iterator);
+ Expression* literal =
+ factory->NewStringLiteral(avfactory->next_string(), nopos);
+ Expression* next_property =
+ factory->NewProperty(iterator_proxy, literal, nopos);
+ Expression* input_proxy = factory->NewVariableProxy(var_input);
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(input_proxy, zone);
+ Expression* call = factory->NewCall(next_property, args, nopos);
+ Expression* output_proxy = factory->NewVariableProxy(var_output);
+ Expression* assignment =
+ factory->NewAssignment(Token::ASSIGN, output_proxy, call, nopos);
+ call_next = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
+ Statement* validate_next_output;
+ {
+ Expression* is_receiver_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var_output), zone);
+ is_receiver_call =
+ factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
+ }
+
+ Statement* throw_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var_output), zone);
+ Expression* call = factory->NewCallRuntime(
+ Runtime::kThrowIteratorResultNotAnObject, args, nopos);
+ throw_call = factory->NewExpressionStatement(call, nopos);
+ }
+
+ validate_next_output =
+ factory->NewIfStatement(is_receiver_call, skip, throw_call, nopos);
+ }
+
+
+ // let iteratorThrow = iterator.throw;
+ Variable* var_throw = scope->NewTemporary(avfactory->empty_string());
+ Statement* get_throw;
+ {
+ Expression* iterator_proxy = factory->NewVariableProxy(var_iterator);
+ Expression* literal =
+ factory->NewStringLiteral(avfactory->throw_string(), nopos);
+ Expression* property =
+ factory->NewProperty(iterator_proxy, literal, nopos);
+ Expression* throw_proxy = factory->NewVariableProxy(var_throw);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, throw_proxy, property, nopos);
+ get_throw = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // if (IS_NULL_OR_UNDEFINED(iteratorThrow) {
+ // IteratorClose(iterator);
+ // throw MakeTypeError(kThrowMethodMissing);
+ // }
+ Statement* check_throw;
+ {
+ Expression* condition = factory->NewCompareOperation(
+ Token::EQ, factory->NewVariableProxy(var_throw),
+ factory->NewNullLiteral(nopos), nopos);
+
+ Expression* call = NewThrowTypeError(
+ MessageTemplate::kThrowMethodMissing,
+ avfactory->empty_string(), nopos);
+ Statement* throw_call = factory->NewExpressionStatement(call, nopos);
+
+ Block* then = factory->NewBlock(nullptr, 4+1, false, nopos);
+ BuildIteratorClose(then->statements(), var_iterator, Nothing<Variable*>());
+ then->statements()->Add(throw_call, zone);
+ check_throw =
+ factory->NewIfStatement(condition, then, skip, nopos);
+ }
+
+
+ // output = %_Call(iteratorThrow, iterator, input);
+ Statement* call_throw;
+ {
+ auto args = new (zone) ZoneList<Expression*>(3, zone);
+ args->Add(factory->NewVariableProxy(var_throw), zone);
+ args->Add(factory->NewVariableProxy(var_iterator), zone);
+ args->Add(factory->NewVariableProxy(var_input), zone);
+ Expression* call =
+ factory->NewCallRuntime(Runtime::kInlineCall, args, nopos);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, factory->NewVariableProxy(var_output), call, nopos);
+ call_throw = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
+ Statement* validate_throw_output;
+ {
+ Expression* is_receiver_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var_output), zone);
+ is_receiver_call =
+ factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
+ }
+
+ Statement* throw_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var_output), zone);
+ Expression* call = factory->NewCallRuntime(
+ Runtime::kThrowIteratorResultNotAnObject, args, nopos);
+ throw_call = factory->NewExpressionStatement(call, nopos);
+ }
+
+ validate_throw_output =
+ factory->NewIfStatement(is_receiver_call, skip, throw_call, nopos);
+ }
+
+
+ // if (output.done) break;
+ Statement* if_done;
+ {
+ Expression* output_proxy = factory->NewVariableProxy(var_output);
+ Expression* literal =
+ factory->NewStringLiteral(avfactory->done_string(), nopos);
+ Expression* property = factory->NewProperty(output_proxy, literal, nopos);
+ BreakStatement* break_loop = factory->NewBreakStatement(loop, nopos);
+ if_done = factory->NewIfStatement(property, break_loop, skip, nopos);
+ }
+
+
+ // mode = kReturn;
+ Statement* set_mode_return;
+ {
+ Expression* mode_proxy = factory->NewVariableProxy(var_mode);
+ Expression* kreturn = factory->NewSmiLiteral(kReturn, nopos);
+ Expression* assignment =
+ factory->NewAssignment(Token::ASSIGN, mode_proxy, kreturn, nopos);
+ set_mode_return = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // RawYield(output);
+ Statement* yield_output;
+ {
+ Expression* output_proxy = factory->NewVariableProxy(var_output);
+ Yield* yield = factory->NewYield(
+ generator, output_proxy, Yield::kInitial, nopos);
+ yield_output = factory->NewExpressionStatement(yield, nopos);
+ }
+
+
+ // mode = kNext;
+ Statement* set_mode_next;
+ {
+ Expression* mode_proxy = factory->NewVariableProxy(var_mode);
+ Expression* knext = factory->NewSmiLiteral(kNext, nopos);
+ Expression* assignment =
+ factory->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
+ set_mode_next = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // mode = kThrow;
+ Statement* set_mode_throw;
+ {
+ Expression* mode_proxy = factory->NewVariableProxy(var_mode);
+ Expression* kthrow = factory->NewSmiLiteral(kThrow, nopos);
+ Expression* assignment =
+ factory->NewAssignment(Token::ASSIGN, mode_proxy, kthrow, nopos);
+ set_mode_throw = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // input = function.sent;
+ Statement* get_input;
+ {
+ Expression* function_sent = FunctionSentExpression(scope, factory, nopos);
+ Expression* input_proxy = factory->NewVariableProxy(var_input);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, input_proxy, function_sent, nopos);
+ get_input = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+
+ // output.value;
+ Statement* get_value;
+ {
+ Expression* output_proxy = factory->NewVariableProxy(var_output);
+ Expression* literal =
+ factory->NewStringLiteral(avfactory->value_string(), nopos);
+ Expression* property = factory->NewProperty(output_proxy, literal, nopos);
+ get_value = factory->NewExpressionStatement(property, nopos);
+ }
+
+
+ // Now put things together.
+
+
+ // try { ... } catch(e) { ... }
+ Statement* try_catch;
+ {
+ Block* try_block = factory->NewBlock(nullptr, 2, false, nopos);
+ try_block->statements()->Add(yield_output, zone);
+ try_block->statements()->Add(set_mode_next, zone);
+
+ Block* catch_block = factory->NewBlock(nullptr, 1, false, nopos);
+ catch_block->statements()->Add(set_mode_throw, zone);
+
+ Scope* catch_scope = NewScope(scope, CATCH_SCOPE);
+ const AstRawString* name = avfactory->dot_catch_string();
+ Variable* catch_variable =
+ catch_scope->DeclareLocal(name, VAR, kCreatedInitialized,
+ Variable::NORMAL);
+
+ try_catch = factory->NewTryCatchStatement(
+ try_block, catch_scope, catch_variable, catch_block, nopos);
+ }
+
+
+ // try { ... } finally { ... }
+ Statement* try_finally;
+ {
+ Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
+ try_block->statements()->Add(try_catch, zone);
+
+ Block* finally = factory->NewBlock(nullptr, 2, false, nopos);
+ finally->statements()->Add(get_input, zone);
+ finally->statements()->Add(
+ factory->NewContinueStatement(loop, nopos), zone);
+
+ try_finally = factory->NewTryFinallyStatement(try_block, finally, nopos);
+ }
+
+
+ // switch (mode) { ... }
+ SwitchStatement* switch_mode = factory->NewSwitchStatement(nullptr, nopos);
+ {
+ auto case_next = new (zone) ZoneList<Statement*>(3, zone);
+ case_next->Add(call_next, zone);
+ case_next->Add(validate_next_output, zone);
+ case_next->Add(factory->NewBreakStatement(switch_mode, nopos), zone);
+
+ auto case_return = new (zone) ZoneList<Statement*>(5, zone);
+ BuildIteratorClose(case_return, var_iterator, Just(var_input));
+ case_return->Add(factory->NewBreakStatement(switch_mode, nopos), zone);
+
+ auto case_throw = new (zone) ZoneList<Statement*>(5, zone);
+ case_throw->Add(get_throw, zone);
+ case_throw->Add(check_throw, zone);
+ case_throw->Add(call_throw, zone);
+ case_throw->Add(validate_throw_output, zone);
+ case_throw->Add(factory->NewBreakStatement(switch_mode, nopos), zone);
+
+ auto cases = new (zone) ZoneList<CaseClause*>(3, zone);
+ Expression* knext = factory->NewSmiLiteral(kNext, nopos);
+ Expression* kreturn = factory->NewSmiLiteral(kReturn, nopos);
+ Expression* kthrow = factory->NewSmiLiteral(kThrow, nopos);
+ cases->Add(factory->NewCaseClause(knext, case_next, nopos), zone);
+ cases->Add(factory->NewCaseClause(kreturn, case_return, nopos), zone);
+ cases->Add(factory->NewCaseClause(kthrow, case_throw, nopos), zone);
+
+ switch_mode->Initialize(factory->NewVariableProxy(var_mode), cases);
+ }
+
+
+ // while (true) { ... }
+ // Already defined earlier: WhileStatement* loop = ...
+ {
+ Block* loop_body = factory->NewBlock(nullptr, 4, false, nopos);
+ loop_body->statements()->Add(switch_mode, zone);
+ loop_body->statements()->Add(if_done, zone);
+ loop_body->statements()->Add(set_mode_return, zone);
+ loop_body->statements()->Add(try_finally, zone);
+
+ loop->Initialize(factory->NewBooleanLiteral(true, nopos), loop_body);
+ }
+
+
+ // do { ... }
+ DoExpression* yield_star;
+ {
+ // The rewriter needs to process the get_value statement only, hence we
+ // put the preceding statements into an init block.
+
+ Block* do_block_ = factory->NewBlock(nullptr, 6, true, nopos);
+ do_block_->statements()->Add(initialize_input, zone);
+ do_block_->statements()->Add(initialize_mode, zone);
+ do_block_->statements()->Add(initialize_output, zone);
+ do_block_->statements()->Add(get_iterator, zone);
+ do_block_->statements()->Add(validate_iterator, zone);
+ do_block_->statements()->Add(loop, zone);
+
+ Block* do_block = factory->NewBlock(nullptr, 2, false, nopos);
+ do_block->statements()->Add(do_block_, zone);
+ do_block->statements()->Add(get_value, zone);
+
+ Variable* dot_result = scope->NewTemporary(avfactory->dot_result_string());
+ yield_star = factory->NewDoExpression(do_block, dot_result, nopos);
+ Rewriter::Rewrite(parser_, yield_star, avfactory);
+ }
+
+ return yield_star;
+}
+
+
+void ParserTraits::BuildIteratorClose(ZoneList<Statement*>* statements,
+ Variable* iterator,
+ Maybe<Variable*> input) {
+ const int nopos = RelocInfo::kNoPosition;
+ auto factory = parser_->factory();
+ auto avfactory = parser_->ast_value_factory();
+ auto scope = parser_->scope_;
+ auto zone = parser_->zone();
+ Statement* skip = factory->NewEmptyStatement(nopos);
+
+ // let iteratorReturn = iterator.return;
+ Variable* var = scope->NewTemporary(avfactory->empty_string());
+ Statement* get_return;
+ {
+ Expression* iterator_proxy = factory->NewVariableProxy(iterator);
+ Expression* literal =
+ factory->NewStringLiteral(avfactory->return_string(), nopos);
+ Expression* property =
+ factory->NewProperty(iterator_proxy, literal, nopos);
+ Expression* return_proxy = factory->NewVariableProxy(var);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, return_proxy, property, nopos);
+ get_return = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+ // if (IS_NULL_OR_UNDEFINED(iteratorReturn) return; OR
+ // if (IS_NULL_OR_UNDEFINED(iteratorReturn) return input;
+ Statement* check_return;
+ {
+ Expression* condition = factory->NewCompareOperation(
+ Token::EQ, factory->NewVariableProxy(var),
+ factory->NewNullLiteral(nopos), nopos);
+
+ Expression* value = input.IsJust() ?
+ static_cast<Expression*>(factory->NewVariableProxy(input.FromJust())) :
+ factory->NewUndefinedLiteral(nopos);
+
+ Statement* return_undefined = factory->NewReturnStatement(value, nopos);
+
+ check_return =
+ factory->NewIfStatement(condition, return_undefined, skip, nopos);
+ }
+
+ // let result = %_Call(iteratorReturn, iterator); OR
+ // let result = %_Call(iteratorReturn, iterator, input);
+ Statement* call_return;
+ {
+ auto args = new (zone) ZoneList<Expression*>(3, zone);
+ args->Add(factory->NewVariableProxy(var), zone);
+ args->Add(factory->NewVariableProxy(iterator), zone);
+ if (input.IsJust()) {
+ args->Add(factory->NewVariableProxy(input.FromJust()), zone);
+ }
+
+ Expression* call =
+ factory->NewCallRuntime(Runtime::kInlineCall, args, nopos);
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, factory->NewVariableProxy(var), call, nopos);
+ call_return = factory->NewExpressionStatement(assignment, nopos);
+ }
+
+ // if (!IS_RECEIVER(result)) %ThrowIterResultNotAnObject(result);
+ Statement* validate_result;
+ {
+ Expression* is_receiver_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var), zone);
+ is_receiver_call =
+ factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
+ }
+
+ Statement* throw_call;
+ {
+ auto args = new (zone) ZoneList<Expression*>(1, zone);
+ args->Add(factory->NewVariableProxy(var), zone);
+ Expression* call = factory->NewCallRuntime(
+ Runtime::kThrowIteratorResultNotAnObject, args, nopos);
+ throw_call = factory->NewExpressionStatement(call, nopos);
+ }
+
+ validate_result =
+ factory->NewIfStatement(is_receiver_call, skip, throw_call, nopos);
+ }
+
+ statements->Add(get_return, zone);
+ statements->Add(check_return, zone);
+ statements->Add(call_return, zone);
+ statements->Add(validate_result, zone);
+}
+
+
} // namespace internal
} // namespace v8
« no previous file with comments | « src/parsing/parser.h ('k') | src/parsing/parser-base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698