Commit feb545ce authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[async-generators] Also avoid throwaway promise here.

This extends the previously introduced logic for implementing await
without having to allocate the throwaway promise and the additional
closures and context, to also cover await and yield inside of async
generators.

Bug: v8:7253
Change-Id: I011583a7714bbd148c54e5f204e2076630008db0
Reviewed-on: https://chromium-review.googlesource.com/924003
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51361}
parent 4a90e486
......@@ -1615,38 +1615,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kAsyncGeneratorAwaitUncaught, 1, false);
native_context()->set_async_generator_await_uncaught(*await_uncaught);
Handle<SharedFunctionInfo> info = SimpleCreateSharedFunctionInfo(
isolate, Builtins::kAsyncGeneratorAwaitResolveClosure,
factory->empty_string(), 1);
native_context()->set_async_generator_await_resolve_shared_fun(*info);
info = SimpleCreateSharedFunctionInfo(
isolate, Builtins::kAsyncGeneratorAwaitRejectClosure,
factory->empty_string(), 1);
native_context()->set_async_generator_await_reject_shared_fun(*info);
info = SimpleCreateSharedFunctionInfo(
isolate, Builtins::kAsyncGeneratorYieldResolveClosure,
factory->empty_string(), 1);
native_context()->set_async_generator_yield_resolve_shared_fun(*info);
info = SimpleCreateSharedFunctionInfo(
isolate, Builtins::kAsyncGeneratorReturnResolveClosure,
factory->empty_string(), 1);
native_context()->set_async_generator_return_resolve_shared_fun(*info);
info = SimpleCreateSharedFunctionInfo(
isolate, Builtins::kAsyncGeneratorReturnClosedResolveClosure,
factory->empty_string(), 1);
native_context()->set_async_generator_return_closed_resolve_shared_fun(
*info);
info = SimpleCreateSharedFunctionInfo(
isolate, Builtins::kAsyncGeneratorReturnClosedRejectClosure,
factory->empty_string(), 1);
native_context()->set_async_generator_return_closed_reject_shared_fun(
*info);
}
{ // --- A r r a y ---
......
......@@ -88,39 +88,9 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
CSA_SLOW_ASSERT(this, IsJSGeneratorObject(generator));
CSA_SLOW_ASSERT(this, IsJSPromise(outer_promise));
Node* const native_context = LoadNativeContext(context);
Node* const promise = AllocateAndInitJSPromise(native_context);
Node* const promise_reactions =
LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
Node* const fulfill_handler = HeapConstant(
Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitFulfill)
.code());
Node* const reject_handler = HeapConstant(
Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitReject)
.code());
Node* const reaction = AllocatePromiseReaction(
promise_reactions, generator, fulfill_handler, reject_handler);
StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reaction);
PromiseSetHasHandler(promise);
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « value »).
CallBuiltin(Builtins::kResolvePromise, native_context, promise, awaited);
{
Label done(this);
GotoIfNot(IsDebugActive(), &done);
CallRuntime(Runtime::kSetProperty, native_context, generator,
LoadRoot(Heap::kgenerator_outer_promise_symbolRootIndex),
outer_promise, SmiConstant(LanguageMode::kStrict));
if (is_predicted_as_caught) {
GotoIf(TaggedIsSmi(awaited), &done);
GotoIfNot(IsJSPromise(awaited), &done);
PromiseSetHandledHint(awaited);
}
Goto(&done);
BIND(&done);
}
Await(context, generator, awaited, outer_promise,
Builtins::kAsyncFunctionAwaitFulfill,
Builtins::kAsyncFunctionAwaitReject, is_predicted_as_caught);
// Return outer promise to avoid adding an load of the outer promise before
// suspending in BytecodeGenerator.
......
......@@ -13,171 +13,67 @@ namespace internal {
using compiler::Node;
namespace {
// Describe fields of Context associated with the AsyncIterator unwrap closure.
class ValueUnwrapContext {
public:
enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
};
} // namespace
Node* AsyncBuiltinsAssembler::Await(
Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length, const ContextInitializer& init_closure_context,
Node* on_resolve_context_index, Node* on_reject_context_index,
void AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
Node* outer_promise,
Builtins::Name fulfill_builtin,
Builtins::Name reject_builtin,
Node* is_predicted_as_caught) {
DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
CSA_SLOW_ASSERT(this, Word32Or(IsJSAsyncGeneratorObject(generator),
IsJSGeneratorObject(generator)));
CSA_SLOW_ASSERT(this, IsJSPromise(outer_promise));
CSA_SLOW_ASSERT(this, IsBoolean(is_predicted_as_caught));
Node* const native_context = LoadNativeContext(context);
static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length);
static const int kThrowawayPromiseOffset =
kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kResolveClosureOffset =
kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
static const int kTotalSize =
kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
Node* const base = AllocateInNewSpace(kTotalSize);
Node* const closure_context = base;
{
// Initialize closure context
InitializeFunctionContext(native_context, closure_context, context_length);
init_closure_context(closure_context);
}
// Let promiseCapability be ! NewPromiseCapability(%Promise%).
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
Node* const promise_map =
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
// Assert that the JSPromise map has an instance size is
// JSPromise::kSizeWithEmbedderFields.
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
kPointerSize)));
Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
{
// Initialize Promise
StoreMapNoWriteBarrier(wrapped_value, promise_map);
InitializeJSObjectFromMap(
wrapped_value, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
PromiseInit(wrapped_value);
}
Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
{
// Initialize throwawayPromise
StoreMapNoWriteBarrier(throwaway, promise_map);
InitializeJSObjectFromMap(
throwaway, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
PromiseInit(throwaway);
}
Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
{
// Initialize resolve handler
InitializeNativeClosure(closure_context, native_context, on_resolve,
on_resolve_context_index);
}
Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
{
// Initialize reject handler
InitializeNativeClosure(closure_context, native_context, on_reject,
on_reject_context_index);
}
{
// Add PromiseHooks if needed
Label next(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next);
CallRuntime(Runtime::kPromiseHookInit, context, wrapped_value,
outer_promise);
CallRuntime(Runtime::kPromiseHookInit, context, throwaway, wrapped_value);
Goto(&next);
BIND(&next);
}
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
// The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done
PromiseSetHasHandler(throwaway);
Label do_perform_promise_then(this);
GotoIfNot(IsDebugActive(), &do_perform_promise_then);
{
Label common(this);
GotoIf(TaggedIsSmi(value), &common);
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
{
// Mark the reject handler callback to be a forwarding edge, rather
// than a meaningful catch handler
Node* const key =
HeapConstant(factory()->promise_forwarding_handler_symbol());
CallRuntime(Runtime::kSetProperty, context, on_reject, key,
TrueConstant(), SmiConstant(LanguageMode::kStrict));
GotoIf(IsFalse(is_predicted_as_caught), &common);
// TODO(bmeurer): This could be optimized and folded into a single allocation.
Node* const promise = AllocateAndInitJSPromise(native_context);
Node* const promise_reactions =
LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
Node* const fulfill_handler =
HeapConstant(Builtins::CallableFor(isolate(), fulfill_builtin).code());
Node* const reject_handler =
HeapConstant(Builtins::CallableFor(isolate(), reject_builtin).code());
Node* const reaction = AllocatePromiseReaction(
promise_reactions, generator, fulfill_handler, reject_handler);
StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reaction);
PromiseSetHasHandler(promise);
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « value »).
CallBuiltin(Builtins::kResolvePromise, native_context, promise, value);
// When debugging, we need to link from the {generator} to the
// {outer_promise} of the async function/generator.
Label done(this);
GotoIfNot(IsDebugActive(), &done);
CallRuntime(Runtime::kSetProperty, native_context, generator,
LoadRoot(Heap::kgenerator_outer_promise_symbolRootIndex),
outer_promise, SmiConstant(LanguageMode::kStrict));
GotoIf(IsFalse(is_predicted_as_caught), &done);
GotoIf(TaggedIsSmi(value), &done);
GotoIfNot(IsJSPromise(value), &done);
PromiseSetHandledHint(value);
}
Goto(&common);
BIND(&common);
// Mark the dependency to outer Promise in case the throwaway Promise is
// found on the Promise stack
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise,
SmiConstant(LanguageMode::kStrict));
}
Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then);
return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
on_resolve, on_reject, throwaway);
Goto(&done);
BIND(&done);
}
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
Node* native_context,
Node* function,
Node* context_index) {
Node* const function_map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
// Ensure that we don't have to initialize prototype_or_initial_map field of
// JSFunction.
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
IntPtrConstant(JSFunction::kSizeWithoutPrototype /
kPointerSize)));
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
StoreMapNoWriteBarrier(function, function_map);
StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldRoot(function, JSObject::kElementsOffset,
Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldRoot(function, JSFunction::kFeedbackVectorOffset,
Heap::kUndefinedCellRootIndex);
Node* shared_info = LoadContextElement(native_context, context_index);
CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
StoreObjectFieldNoWriteBarrier(
function, JSFunction::kSharedFunctionInfoOffset, shared_info);
StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
Node* const code =
LoadObjectField(shared_info, SharedFunctionInfo::kCodeOffset);
StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
void AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
Node* outer_promise,
Builtins::Name fulfill_builtin,
Builtins::Name reject_builtin,
bool is_predicted_as_caught) {
return Await(context, generator, value, outer_promise, fulfill_builtin,
reject_builtin, BooleanConstant(is_predicted_as_caught));
}
namespace {
// Describe fields of Context associated with the AsyncIterator unwrap closure.
class ValueUnwrapContext {
public:
enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
};
} // namespace
Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
Node* done) {
Node* const map = LoadContextElement(
......
......@@ -16,48 +16,23 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
: PromiseBuiltinsAssembler(state) {}
protected:
typedef std::function<void(Node*)> ContextInitializer;
// Perform steps to resume generator after `value` is resolved.
// `on_reject_context_index` is an index into the Native Context, which should
// point to a SharedFunctioninfo instance used to create the closure. The
// value following the reject index should be a similar value for the resolve
// closure. Returns the Promise-wrapped `value`.
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
Node* on_resolve_context_index, Node* on_reject_context_index,
void Await(Node* context, Node* generator, Node* value, Node* outer_promise,
Builtins::Name fulfill_builtin, Builtins::Name reject_builtin,
Node* is_predicted_as_caught);
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
Node* is_predicted_as_caught) {
return Await(context, generator, value, outer_promise, context_length,
init_closure_context, IntPtrConstant(on_resolve_context_index),
IntPtrConstant(on_reject_context_index),
is_predicted_as_caught);
}
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
bool is_predicted_as_caught) {
return Await(context, generator, value, outer_promise, context_length,
init_closure_context, on_resolve_context_index,
on_reject_context_index,
BooleanConstant(is_predicted_as_caught));
}
void Await(Node* context, Node* generator, Node* value, Node* outer_promise,
Builtins::Name fulfill_builtin, Builtins::Name reject_builtin,
bool is_predicted_as_caught);
// Return a new built-in function object as defined in
// Async Iterator Value Unwrap Functions
Node* CreateUnwrapClosure(Node* const native_context, Node* const done);
private:
void InitializeNativeClosure(Node* context, Node* native_context,
Node* function, Node* context_index);
Node* AllocateAsyncIteratorValueUnwrapContext(Node* native_context,
Node* done);
Node* AllocateAwaitPromiseJobTask(Node* generator, Node* fulfill_handler,
Node* reject_handler, Node* promise,
Node* context);
};
} // namespace internal
......
......@@ -1173,6 +1173,17 @@ namespace internal {
\
/* AsyncGenerator */ \
\
/* Await (proposal-async-iteration/#await), with resume behaviour */ \
/* specific to Async Generators. Internal / Not exposed to JS code. */ \
TFJ(AsyncGeneratorAwaitCaught, 2, kGenerator, kAwaited) \
TFJ(AsyncGeneratorAwaitUncaught, 2, kGenerator, kAwaited) \
TFC(AsyncGeneratorAwaitFulfill, PromiseReactionHandler, 1) \
TFC(AsyncGeneratorAwaitReject, PromiseReactionHandler, 1) \
TFC(AsyncGeneratorYieldFulfill, PromiseReactionHandler, 1) \
TFC(AsyncGeneratorReturnClosedFulfill, PromiseReactionHandler, 1) \
TFC(AsyncGeneratorReturnClosedReject, PromiseReactionHandler, 1) \
TFC(AsyncGeneratorReturnFulfill, PromiseReactionHandler, 1) \
\
TFS(AsyncGeneratorResolve, kGenerator, kValue, kDone) \
TFS(AsyncGeneratorReject, kGenerator, kValue) \
TFS(AsyncGeneratorYield, kGenerator, kValue, kIsCaught) \
......@@ -1195,17 +1206,6 @@ namespace internal {
TFJ(AsyncGeneratorPrototypeThrow, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
\
/* Await (proposal-async-iteration/#await), with resume behaviour */ \
/* specific to Async Generators. Internal / Not exposed to JS code. */ \
TFJ(AsyncGeneratorAwaitCaught, 2, kGenerator, kAwaited) \
TFJ(AsyncGeneratorAwaitUncaught, 2, kGenerator, kAwaited) \
TFJ(AsyncGeneratorAwaitResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorAwaitRejectClosure, 1, kValue) \
TFJ(AsyncGeneratorYieldResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnClosedResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnClosedRejectClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnResolveClosure, 1, kValue) \
\
/* Async-from-Sync Iterator */ \
\
/* %AsyncFromSyncIteratorPrototype% */ \
......
......@@ -235,6 +235,8 @@ bool Builtins::IsLazy(int index) {
case kArrayReduceRightLoopLazyDeoptContinuation:
case kArraySomeLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
case kArraySomeLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
case kAsyncGeneratorAwaitCaught: // https://crbug.com/v8/6786.
case kAsyncGeneratorAwaitUncaught: // https://crbug.com/v8/6786.
case kCheckOptimizationMarker:
case kCompileLazy:
case kDeserializeLazy:
......
......@@ -4561,6 +4561,10 @@ Node* CodeStubAssembler::IsJSArrayMap(Node* map) {
return IsJSArrayInstanceType(LoadMapInstanceType(map));
}
Node* CodeStubAssembler::IsJSAsyncGeneratorObject(Node* object) {
return HasInstanceType(object, JS_ASYNC_GENERATOR_OBJECT_TYPE);
}
Node* CodeStubAssembler::IsFixedArray(Node* object) {
return HasInstanceType(object, FIXED_ARRAY_TYPE);
}
......
......@@ -1121,6 +1121,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsJSArrayInstanceType(Node* instance_type);
Node* IsJSArrayMap(Node* object);
Node* IsJSArray(Node* object);
Node* IsJSAsyncGeneratorObject(Node* object);
Node* IsJSFunctionInstanceType(Node* instance_type);
Node* IsJSFunctionMap(Node* object);
Node* IsJSFunction(Node* object);
......
......@@ -211,18 +211,6 @@ enum ContextLookupFlags {
async_generator_function_function) \
V(ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN, SharedFunctionInfo, \
async_iterator_value_unwrap_shared_fun) \
V(ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN, SharedFunctionInfo, \
async_generator_await_reject_shared_fun) \
V(ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
async_generator_await_resolve_shared_fun) \
V(ASYNC_GENERATOR_YIELD_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
async_generator_yield_resolve_shared_fun) \
V(ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_resolve_shared_fun) \
V(ASYNC_GENERATOR_RETURN_CLOSED_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_closed_resolve_shared_fun) \
V(ASYNC_GENERATOR_RETURN_CLOSED_REJECT_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_closed_reject_shared_fun) \
V(ATOMICS_OBJECT, JSObject, atomics_object) \
V(BIGINT_FUNCTION_INDEX, JSFunction, bigint_function) \
V(BIGINT64_ARRAY_FUN_INDEX, JSFunction, bigint64_array_fun) \
......
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