Commit e2466bb5 authored by neis's avatar neis Committed by Commit bot

Implement the function.sent proposal.

The body of a generator function can now refer to the generator's input value via a new
"function.sent" expression.  We extend the proposal at
https://github.com/allenwb/ESideas/blob/master/Generator%20metaproperty.md
in the obvious way to also apply to GeneratorResumeAbrupt.
This will enable us to desugar yield*.

The new syntax is behind a new --harmony-function-sent flag.

BUG=v8:4700
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33574}
parent 17bf607d
...@@ -2317,6 +2317,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tolength) ...@@ -2317,6 +2317,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tolength)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_name) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_name)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(promise_extra) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(promise_extra)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls)
...@@ -2955,6 +2956,7 @@ bool Genesis::InstallExperimentalNatives() { ...@@ -2955,6 +2956,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_regexp_subclass_natives[] = {nullptr}; static const char* harmony_regexp_subclass_natives[] = {nullptr};
static const char* harmony_regexp_lookbehind_natives[] = {nullptr}; static const char* harmony_regexp_lookbehind_natives[] = {nullptr};
static const char* harmony_function_name_natives[] = {nullptr}; static const char* harmony_function_name_natives[] = {nullptr};
static const char* harmony_function_sent_natives[] = {nullptr};
static const char* promise_extra_natives[] = {"native promise-extra.js", static const char* promise_extra_natives[] = {"native promise-extra.js",
nullptr}; nullptr};
static const char* harmony_object_values_entries_natives[] = {nullptr}; static const char* harmony_object_values_entries_natives[] = {nullptr};
......
...@@ -202,6 +202,7 @@ DEFINE_IMPLICATION(es_staging, move_object_start) ...@@ -202,6 +202,7 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
V(harmony_modules, "harmony modules") \ V(harmony_modules, "harmony modules") \
V(harmony_unicode_regexps, "harmony unicode regexps") \ V(harmony_unicode_regexps, "harmony unicode regexps") \
V(harmony_function_name, "harmony Function name inference") \ V(harmony_function_name, "harmony Function name inference") \
V(harmony_function_sent, "harmony function.sent") \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \ V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd") \ V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \ V(harmony_do_expressions, "harmony do-expressions") \
......
...@@ -2086,6 +2086,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -2086,6 +2086,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ pop(r1); __ pop(r1);
// Store input value into generator object.
__ str(result_register(),
FieldMemOperand(r1, JSGeneratorObject::kInputOffset));
__ mov(r2, result_register());
__ RecordWriteField(r1, JSGeneratorObject::kInputOffset, r2, r3,
kLRHasBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset)); __ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset));
__ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset)); __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset));
......
...@@ -4524,6 +4524,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -4524,6 +4524,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ Pop(generator_object); __ Pop(generator_object);
// Store input value into generator object.
__ Str(result_register(),
FieldMemOperand(x1, JSGeneratorObject::kInputOffset));
__ Mov(x2, result_register());
__ RecordWriteField(x1, JSGeneratorObject::kInputOffset, x2, x3,
kLRHasBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ Ldr(cp, FieldMemOperand(generator_object, __ Ldr(cp, FieldMemOperand(generator_object,
JSGeneratorObject::kContextOffset)); JSGeneratorObject::kContextOffset));
......
...@@ -1999,6 +1999,12 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -1999,6 +1999,12 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ pop(ebx); __ pop(ebx);
// Store input value into generator object.
__ mov(FieldOperand(ebx, JSGeneratorObject::kInputOffset), result_register());
__ mov(ecx, result_register());
__ RecordWriteField(ebx, JSGeneratorObject::kInputOffset, ecx, edx,
kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset)); __ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
__ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset));
......
...@@ -2087,6 +2087,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -2087,6 +2087,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ pop(a1); __ pop(a1);
// Store input value into generator object.
__ sw(result_register(),
FieldMemOperand(a1, JSGeneratorObject::kInputOffset));
__ mov(a2, result_register());
__ RecordWriteField(a1, JSGeneratorObject::kInputOffset, a2, a3,
kRAHasBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ lw(cp, FieldMemOperand(a1, JSGeneratorObject::kContextOffset)); __ lw(cp, FieldMemOperand(a1, JSGeneratorObject::kContextOffset));
__ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); __ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset));
......
...@@ -1974,6 +1974,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { ...@@ -1974,6 +1974,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
Label l_next, l_call; Label l_next, l_call;
Register load_receiver = LoadDescriptor::ReceiverRegister(); Register load_receiver = LoadDescriptor::ReceiverRegister();
Register load_name = LoadDescriptor::NameRegister(); Register load_name = LoadDescriptor::NameRegister();
// Initial send value is undefined. // Initial send value is undefined.
__ LoadRoot(a0, Heap::kUndefinedValueRootIndex); __ LoadRoot(a0, Heap::kUndefinedValueRootIndex);
__ Branch(&l_next); __ Branch(&l_next);
...@@ -2086,6 +2087,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -2086,6 +2087,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ pop(a1); __ pop(a1);
// Store input value into generator object.
__ sd(result_register(),
FieldMemOperand(a1, JSGeneratorObject::kInputOffset));
__ mov(a2, result_register());
__ RecordWriteField(a1, JSGeneratorObject::kInputOffset, a2, a3,
kRAHasBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ ld(cp, FieldMemOperand(a1, JSGeneratorObject::kContextOffset)); __ ld(cp, FieldMemOperand(a1, JSGeneratorObject::kContextOffset));
__ ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); __ ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset));
......
...@@ -2041,6 +2041,13 @@ void FullCodeGenerator::EmitGeneratorResume( ...@@ -2041,6 +2041,13 @@ void FullCodeGenerator::EmitGeneratorResume(
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ pop(r4); __ pop(r4);
// Store input value into generator object.
__ StoreP(result_register(),
FieldMemOperand(r4, JSGeneratorObject::kInputOffset));
__ mr(r5, result_register());
__ RecordWriteField(r4, JSGeneratorObject::kInputOffset, r5, r6,
kLRHasBeenSaved, kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ LoadP(cp, FieldMemOperand(r4, JSGeneratorObject::kContextOffset)); __ LoadP(cp, FieldMemOperand(r4, JSGeneratorObject::kContextOffset));
__ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); __ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset));
......
...@@ -2021,6 +2021,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -2021,6 +2021,13 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ Pop(rbx); __ Pop(rbx);
// Store input value into generator object.
__ movp(FieldOperand(rbx, JSGeneratorObject::kInputOffset),
result_register());
__ movp(rcx, result_register());
__ RecordWriteField(rbx, JSGeneratorObject::kInputOffset, rcx, rdx,
kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ movp(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset)); __ movp(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset));
__ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset)); __ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset));
......
...@@ -1991,6 +1991,12 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator, ...@@ -1991,6 +1991,12 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
VisitForAccumulatorValue(value); VisitForAccumulatorValue(value);
__ pop(ebx); __ pop(ebx);
// Store input value into generator object.
__ mov(FieldOperand(ebx, JSGeneratorObject::kInputOffset), result_register());
__ mov(edx, result_register());
__ RecordWriteField(ebx, JSGeneratorObject::kInputOffset, edx, ecx,
kDontSaveFPRegs);
// Load suspended function and context. // Load suspended function and context.
__ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset)); __ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
__ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset));
......
...@@ -495,6 +495,8 @@ class CallSite { ...@@ -495,6 +495,8 @@ class CallSite {
T(TypedArrayTooShort, \ T(TypedArrayTooShort, \
"Derived TypedArray constructor created an array which was too small") \ "Derived TypedArray constructor created an array which was too small") \
T(UnexpectedEOS, "Unexpected end of input") \ T(UnexpectedEOS, "Unexpected end of input") \
T(UnexpectedFunctionSent, \
"function.sent expression is not allowed outside a generator") \
T(UnexpectedReserved, "Unexpected reserved word") \ T(UnexpectedReserved, "Unexpected reserved word") \
T(UnexpectedStrictReserved, "Unexpected strict mode reserved word") \ T(UnexpectedStrictReserved, "Unexpected strict mode reserved word") \
T(UnexpectedSuper, "'super' keyword unexpected here") \ T(UnexpectedSuper, "'super' keyword unexpected here") \
......
...@@ -6309,6 +6309,7 @@ void Foreign::set_foreign_address(Address value) { ...@@ -6309,6 +6309,7 @@ void Foreign::set_foreign_address(Address value) {
ACCESSORS(JSGeneratorObject, function, JSFunction, kFunctionOffset) ACCESSORS(JSGeneratorObject, function, JSFunction, kFunctionOffset)
ACCESSORS(JSGeneratorObject, context, Context, kContextOffset) ACCESSORS(JSGeneratorObject, context, Context, kContextOffset)
ACCESSORS(JSGeneratorObject, receiver, Object, kReceiverOffset) ACCESSORS(JSGeneratorObject, receiver, Object, kReceiverOffset)
ACCESSORS(JSGeneratorObject, input, Object, kInputOffset)
SMI_ACCESSORS(JSGeneratorObject, continuation, kContinuationOffset) SMI_ACCESSORS(JSGeneratorObject, continuation, kContinuationOffset)
ACCESSORS(JSGeneratorObject, operand_stack, FixedArray, kOperandStackOffset) ACCESSORS(JSGeneratorObject, operand_stack, FixedArray, kOperandStackOffset)
......
...@@ -7203,6 +7203,9 @@ class JSGeneratorObject: public JSObject { ...@@ -7203,6 +7203,9 @@ class JSGeneratorObject: public JSObject {
// [receiver]: The receiver of the suspended computation. // [receiver]: The receiver of the suspended computation.
DECL_ACCESSORS(receiver, Object) DECL_ACCESSORS(receiver, Object)
// [input]: The most recent input value.
DECL_ACCESSORS(input, Object)
// [continuation]: Offset into code of continuation. // [continuation]: Offset into code of continuation.
// //
// A positive offset indicates a suspended generator. The special // A positive offset indicates a suspended generator. The special
...@@ -7231,7 +7234,8 @@ class JSGeneratorObject: public JSObject { ...@@ -7231,7 +7234,8 @@ class JSGeneratorObject: public JSObject {
static const int kFunctionOffset = JSObject::kHeaderSize; static const int kFunctionOffset = JSObject::kHeaderSize;
static const int kContextOffset = kFunctionOffset + kPointerSize; static const int kContextOffset = kFunctionOffset + kPointerSize;
static const int kReceiverOffset = kContextOffset + kPointerSize; static const int kReceiverOffset = kContextOffset + kPointerSize;
static const int kContinuationOffset = kReceiverOffset + kPointerSize; static const int kInputOffset = kReceiverOffset + kPointerSize;
static const int kContinuationOffset = kInputOffset + kPointerSize;
static const int kOperandStackOffset = kContinuationOffset + kPointerSize; static const int kOperandStackOffset = kContinuationOffset + kPointerSize;
static const int kSize = kOperandStackOffset + kPointerSize; static const int kSize = kOperandStackOffset + kPointerSize;
......
...@@ -2548,6 +2548,24 @@ ParserBase<Traits>::ParseMemberExpression(ExpressionClassifier* classifier, ...@@ -2548,6 +2548,24 @@ ParserBase<Traits>::ParseMemberExpression(ExpressionClassifier* classifier,
Consume(Token::FUNCTION); Consume(Token::FUNCTION);
int function_token_position = position(); int function_token_position = position();
if (FLAG_harmony_function_sent && Check(Token::PERIOD)) {
// function.sent
int pos = position();
ExpectContextualKeyword(CStrVector("sent"), CHECK_OK);
if (!is_generator()) {
// TODO(neis): allow escaping into closures?
ReportMessageAt(scanner()->location(),
MessageTemplate::kUnexpectedFunctionSent);
*ok = false;
return this->EmptyExpression();
}
return this->FunctionSentExpression(scope_, factory(), pos);
}
bool is_generator = Check(Token::MUL); bool is_generator = Check(Token::MUL);
IdentifierT name = this->EmptyIdentifier(); IdentifierT name = this->EmptyIdentifier();
bool is_strict_reserved_name = false; bool is_strict_reserved_name = false;
......
...@@ -643,6 +643,19 @@ Expression* ParserTraits::NewTargetExpression(Scope* scope, ...@@ -643,6 +643,19 @@ Expression* ParserTraits::NewTargetExpression(Scope* scope,
} }
Expression* ParserTraits::FunctionSentExpression(Scope* scope,
AstNodeFactory* factory,
int pos) {
// We desugar function.sent into %GeneratorGetInput(generator).
Zone* zone = parser_->zone();
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(1, zone);
VariableProxy* generator = factory->NewVariableProxy(
parser_->function_state_->generator_object_variable());
args->Add(generator, zone);
return factory->NewCallRuntime(Runtime::kGeneratorGetInput, args, pos);
}
Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope, Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope,
int pos, int end_pos, int pos, int end_pos,
LanguageMode mode) { LanguageMode mode) {
......
...@@ -513,6 +513,8 @@ class ParserTraits { ...@@ -513,6 +513,8 @@ class ParserTraits {
int pos); int pos);
Expression* NewTargetExpression(Scope* scope, AstNodeFactory* factory, Expression* NewTargetExpression(Scope* scope, AstNodeFactory* factory,
int pos); int pos);
Expression* FunctionSentExpression(Scope* scope, AstNodeFactory* factory,
int pos);
Expression* DefaultConstructor(bool call_super, Scope* scope, int pos, Expression* DefaultConstructor(bool call_super, Scope* scope, int pos,
int end_pos, LanguageMode language_mode); int end_pos, LanguageMode language_mode);
Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner, Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
......
...@@ -798,6 +798,12 @@ class PreParserTraits { ...@@ -798,6 +798,12 @@ class PreParserTraits {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
static PreParserExpression FunctionSentExpression(Scope* scope,
PreParserFactory* factory,
int pos) {
return PreParserExpression::Default();
}
static PreParserExpression DefaultConstructor(bool call_super, Scope* scope, static PreParserExpression DefaultConstructor(bool call_super, Scope* scope,
int pos, int end_pos) { int pos, int end_pos) {
return PreParserExpression::Default(); return PreParserExpression::Default();
......
...@@ -177,6 +177,16 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetReceiver) { ...@@ -177,6 +177,16 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetReceiver) {
} }
// Returns input of generator activation.
RUNTIME_FUNCTION(Runtime_GeneratorGetInput) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
return generator->input();
}
// Returns generator continuation as a PC offset, or the magic -1 or 0 values. // Returns generator continuation as a PC offset, or the magic -1 or 0 values.
RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) { RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) {
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -258,6 +258,7 @@ namespace internal { ...@@ -258,6 +258,7 @@ namespace internal {
F(GeneratorGetFunction, 1, 1) \ F(GeneratorGetFunction, 1, 1) \
F(GeneratorGetContext, 1, 1) \ F(GeneratorGetContext, 1, 1) \
F(GeneratorGetReceiver, 1, 1) \ F(GeneratorGetReceiver, 1, 1) \
F(GeneratorGetInput, 1, 1) \
F(GeneratorGetContinuation, 1, 1) \ F(GeneratorGetContinuation, 1, 1) \
F(GeneratorGetSourcePosition, 1, 1) \ F(GeneratorGetSourcePosition, 1, 1) \
F(GeneratorNext, 2, 1) \ F(GeneratorNext, 2, 1) \
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-function-sent
{
function* g() { return function.sent }
assertEquals({value: 42, done: true}, g().next(42));
}
{
function* g() {
try {
yield function.sent;
} finally {
yield function.sent;
return function.sent;
}
}
{
x = g();
assertEquals({value: 1, done: false}, x.next(1));
assertEquals({value: 2, done: false}, x.next(2));
assertEquals({value: 3, done: true}, x.next(3));
}
{
x = g();
assertEquals({value: 1, done: false}, x.next(1));
assertEquals({value: 2, done: false}, x.throw(2));
assertEquals({value: 3, done: true}, x.next(3));
}
}
{
function* inner() {
try {
yield function.sent;
} finally {
return 666;
}
}
function* g() {
yield function.sent;
yield* inner();
return function.sent;
}
{
x = g();
assertEquals({value: 1, done: false}, x.next(1));
assertEquals({value: undefined, done: false}, x.next(2));
assertEquals({value: 3, done: true}, x.next(3));
}
{
x = g();
assertEquals({value: 1, done: false}, x.next(1));
assertEquals({value: undefined, done: false}, x.next(2));
assertEquals({value: 42, done: true}, x.throw(42));
}
}
assertThrows("function f() { return function.sent }", SyntaxError);
assertThrows("() => { return function.sent }", SyntaxError);
assertThrows("() => { function.sent }", SyntaxError);
assertThrows("() => function.sent", SyntaxError);
assertThrows("({*f() { function.sent }})", SyntaxError);
assertDoesNotThrow("({*f() { return function.sent }})");
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