Commit 677bd40e authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Add AllocatePromiseResolveThenableJobInfo to TF

Also moves most of the runtime function into TF. There are lots of
runtime calls but they happen only for the debug case so it's fine.

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2611083002
Cr-Commit-Position: refs/heads/master@{#42109}
parent 0a4f5297
......@@ -647,6 +647,30 @@ void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context,
Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
}
Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobInfo(
Node* thenable, Node* then, Node* resolve, Node* reject, Node* context) {
Node* const info = Allocate(PromiseResolveThenableJobInfo::kSize);
StoreMapNoWriteBarrier(info,
Heap::kPromiseResolveThenableJobInfoMapRootIndex);
StoreObjectFieldNoWriteBarrier(
info, PromiseResolveThenableJobInfo::kThenableOffset, thenable);
StoreObjectFieldNoWriteBarrier(
info, PromiseResolveThenableJobInfo::kThenOffset, then);
StoreObjectFieldNoWriteBarrier(
info, PromiseResolveThenableJobInfo::kResolveOffset, resolve);
StoreObjectFieldNoWriteBarrier(
info, PromiseResolveThenableJobInfo::kRejectOffset, reject);
StoreObjectFieldNoWriteBarrier(info,
PromiseResolveThenableJobInfo::kDebugIdOffset,
SmiConstant(kDebugPromiseNoID));
StoreObjectFieldNoWriteBarrier(
info, PromiseResolveThenableJobInfo::kDebugNameOffset,
SmiConstant(kDebugNotActive));
StoreObjectFieldNoWriteBarrier(
info, PromiseResolveThenableJobInfo::kContextOffset, context);
return info;
}
void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Node* promise,
Node* result) {
......@@ -756,10 +780,33 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Bind(&do_enqueue);
{
// TODO(gsathya): Add fast path for native promises with unmodified
// PromiseThen (which don't need these resolving functions, but
// instead can just call resolve/reject directly).
Node* resolve = nullptr;
Node* reject = nullptr;
std::tie(resolve, reject) = CreatePromiseResolvingFunctions(
promise, FalseConstant(), native_context);
Node* const info = AllocatePromiseResolveThenableJobInfo(
result, var_then.value(), resolve, reject, context);
Label enqueue(this);
GotoUnless(IsDebugActive(), &enqueue);
Node* const debug_id = CallRuntime(Runtime::kDebugNextMicrotaskId, context);
Node* const debug_name = SmiConstant(kDebugPromiseResolveThenableJob);
CallRuntime(Runtime::kDebugAsyncTaskEvent, context,
SmiConstant(kDebugEnqueue), debug_id, debug_name);
StoreObjectField(info, PromiseResolveThenableJobInfo::kDebugIdOffset,
debug_id);
StoreObjectField(info, PromiseResolveThenableJobInfo::kDebugNameOffset,
debug_name);
GotoIf(TaggedIsSmi(result), &enqueue);
GotoUnless(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue);
// Mark the dependency of the new promise on the resolution
Node* const key =
HeapConstant(isolate->factory()->promise_handled_by_symbol());
......@@ -768,12 +815,10 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Goto(&enqueue);
// 12. Perform EnqueueJob("PromiseJobs",
// PromiseResolveThenableJob, « promise, resolution, thenAction
// »).
// PromiseResolveThenableJob, « promise, resolution, thenAction»).
Bind(&enqueue);
// TODO(gsathya): Move this to TF
CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise,
result, var_then.value());
CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, info);
Goto(&out);
}
......
......@@ -30,6 +30,10 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
// fields.
Node* AllocateAndSetJSPromise(Node* context, Node* status, Node* result);
Node* AllocatePromiseResolveThenableJobInfo(Node* result, Node* then,
Node* resolve, Node* reject,
Node* context);
Node* ThrowIfNotJSReceiver(Node* context, Node* value,
MessageTemplate::Template msg_template,
const char* method_name = nullptr);
......
......@@ -1011,23 +1011,6 @@ Handle<Struct> Factory::NewStruct(InstanceType type) {
Struct);
}
Handle<PromiseResolveThenableJobInfo> Factory::NewPromiseResolveThenableJobInfo(
Handle<JSReceiver> thenable, Handle<JSReceiver> then,
Handle<JSFunction> resolve, Handle<JSFunction> reject, int debug_id,
int debug_name, Handle<Context> context) {
Handle<PromiseResolveThenableJobInfo> result =
Handle<PromiseResolveThenableJobInfo>::cast(
NewStruct(PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE));
result->set_thenable(*thenable);
result->set_then(*then);
result->set_resolve(*resolve);
result->set_reject(*reject);
result->set_debug_id(debug_id);
result->set_debug_name(debug_name);
result->set_context(*context);
return result;
}
Handle<PromiseReactionJobInfo> Factory::NewPromiseReactionJobInfo(
Handle<JSPromise> promise, Handle<Object> value, Handle<Object> tasks,
Handle<Object> deferred_promise, Handle<Object> deferred_on_resolve,
......
......@@ -75,12 +75,6 @@ class V8_EXPORT_PRIVATE Factory final {
Handle<Object> deferred_promise, Handle<Object> deferred_on_resolve,
Handle<Object> deferred_on_reject, Handle<Context> context);
// Create a new PromiseResolveThenableJobInfo struct.
Handle<PromiseResolveThenableJobInfo> NewPromiseResolveThenableJobInfo(
Handle<JSReceiver> thenable, Handle<JSReceiver> then,
Handle<JSFunction> resolve, Handle<JSFunction> reject, int debug_id,
int debug_name, Handle<Context> context);
// Create a new PrototypeInfo struct.
Handle<PrototypeInfo> NewPrototypeInfo();
......
......@@ -27,42 +27,5 @@ void PromiseUtils::SetAlreadyVisited(Handle<Context> context) {
context->set(kAlreadyVisitedSlot, Smi::FromInt(1));
}
void PromiseUtils::CreateResolvingFunctions(Isolate* isolate,
Handle<JSObject> promise,
Handle<Object> debug_event,
Handle<JSFunction>* resolve,
Handle<JSFunction>* reject) {
DCHECK(debug_event->IsTrue(isolate) || debug_event->IsFalse(isolate));
Handle<Context> context =
isolate->factory()->NewPromiseResolvingFunctionContext(
kPromiseContextLength);
context->set_native_context(*isolate->native_context());
// We set the closure to be an empty function, same as native context.
context->set_closure(isolate->native_context()->closure());
context->set(kAlreadyVisitedSlot, Smi::kZero);
context->set(kPromiseSlot, *promise);
context->set(kDebugEventSlot, *debug_event);
Handle<SharedFunctionInfo> resolve_shared_fun(
isolate->native_context()->promise_resolve_shared_fun(), isolate);
Handle<JSFunction> resolve_fun =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
isolate->sloppy_function_without_prototype_map(), resolve_shared_fun,
isolate->native_context(), TENURED);
Handle<SharedFunctionInfo> reject_shared_fun(
isolate->native_context()->promise_reject_shared_fun(), isolate);
Handle<JSFunction> reject_fun =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
isolate->sloppy_function_without_prototype_map(), reject_shared_fun,
isolate->native_context(), TENURED);
resolve_fun->set_context(*context);
reject_fun->set_context(*context);
*resolve = resolve_fun;
*reject = reject_fun;
}
} // namespace internal
} // namespace v8
......@@ -33,12 +33,6 @@ class PromiseUtils : public AllStatic {
static Object* GetDebugEvent(Handle<Context> context);
static bool HasAlreadyVisited(Handle<Context> context);
static void SetAlreadyVisited(Handle<Context> context);
static void CreateResolvingFunctions(Isolate* isolate,
Handle<JSObject> promise,
Handle<Object> debug_event,
Handle<JSFunction>* resolve,
Handle<JSFunction>* reject);
};
class GetPromiseCapabilityExecutor : public AllStatic {
......
......@@ -195,32 +195,9 @@ RUNTIME_FUNCTION(Runtime_EnqueuePromiseReactionJob) {
RUNTIME_FUNCTION(Runtime_EnqueuePromiseResolveThenableJob) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, resolution, 1);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, then, 2);
// TODO(gsathya): Add fast path for native promises with unmodified
// PromiseThen (which don't need these resolving functions, but
// instead can just call resolve/reject directly).
Handle<JSFunction> resolve, reject;
PromiseUtils::CreateResolvingFunctions(
isolate, promise, isolate->factory()->false_value(), &resolve, &reject);
int debug_id = kDebugPromiseFirstID;
PromiseDebugActionName debug_name = kDebugNotActive;
if (isolate->debug()->is_active()) {
debug_id = isolate->GetNextDebugMicrotaskId();
debug_name = kDebugPromiseResolveThenableJob;
isolate->debug()->OnAsyncTaskEvent(kDebugEnqueue, debug_id, debug_name);
}
Handle<PromiseResolveThenableJobInfo> info =
isolate->factory()->NewPromiseResolveThenableJobInfo(
resolution, then, resolve, reject, debug_id, debug_name,
isolate->native_context());
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(PromiseResolveThenableJobInfo, info, 0);
isolate->EnqueueMicrotask(info);
return isolate->heap()->undefined_value();
}
......@@ -239,22 +216,6 @@ RUNTIME_FUNCTION(Runtime_RunMicrotasks) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_CreateResolvingFunctions) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
DCHECK_EQ(1, args.length());
Handle<JSFunction> resolve, reject;
PromiseUtils::CreateResolvingFunctions(
isolate, promise, isolate->factory()->true_value(), &resolve, &reject);
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2);
result->set(0, *resolve);
result->set(1, *reject);
return *result;
}
RUNTIME_FUNCTION(Runtime_PromiseStatus) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -288,10 +288,9 @@ namespace internal {
F(AllocateSeqTwoByteString, 1, 1) \
F(CheckIsBootstrapping, 0, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(CreateResolvingFunctions, 1, 1) \
F(EnqueueMicrotask, 1, 1) \
F(EnqueuePromiseReactionJob, 2, 1) \
F(EnqueuePromiseResolveThenableJob, 3, 1) \
F(EnqueuePromiseResolveThenableJob, 1, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(ExportExperimentalFromRuntime, 1, 1) \
F(ExportFromRuntime, 1, 1) \
......
......@@ -1874,6 +1874,47 @@ TEST(AllocatePromiseReactionJobInfo) {
CHECK_EQ(kDebugNotActive, promise_info->debug_name());
}
TEST(AllocatePromiseResolveThenableJobInfo) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler p(data.state());
Node* const context = p.Parameter(kNumParams + 2);
Node* const native_context = p.LoadNativeContext(context);
Node* const thenable = p.AllocateAndInitJSPromise(context);
Node* const then_str = p.HeapConstant(isolate->factory()->then_string());
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const then =
p.CallStub(getproperty_callable, context, thenable, then_str);
Node* resolve = nullptr;
Node* reject = nullptr;
std::tie(resolve, reject) = p.CreatePromiseResolvingFunctions(
thenable, p.FalseConstant(), native_context);
Node* const info = p.AllocatePromiseResolveThenableJobInfo(
thenable, then, resolve, reject, context);
p.Return(info);
Handle<Code> code = data.GenerateCode();
CHECK(!code.is_null());
FunctionTester ft(code, kNumParams);
Handle<Object> result =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result->IsPromiseResolveThenableJobInfo());
Handle<PromiseResolveThenableJobInfo> promise_info =
Handle<PromiseResolveThenableJobInfo>::cast(result);
CHECK(promise_info->thenable()->IsJSPromise());
CHECK(promise_info->then()->IsJSFunction());
CHECK(promise_info->resolve()->IsJSFunction());
CHECK(promise_info->reject()->IsJSFunction());
CHECK_EQ(kDebugPromiseNoID, promise_info->debug_id());
CHECK_EQ(kDebugNotActive, promise_info->debug_name());
CHECK(promise_info->context()->IsContext());
}
TEST(IsSymbol) {
Isolate* isolate(CcTest::InitIsolateOnce());
......
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