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

Unified Diff: src/builtins/builtins-promise.cc

Issue 2541283002: [promises] Port ResolvePromise to TF (Closed)
Patch Set: fix nits Created 4 years 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/builtins/builtins.h ('k') | src/contexts.h » ('j') | src/js/promise.js » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/builtins/builtins-promise.cc
diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc
index b7a93a517f253422eeeaef9f0635e97b79141053..aa38002218e744fd87a221fe753230a78487f337 100644
--- a/src/builtins/builtins-promise.cc
+++ b/src/builtins/builtins-promise.cc
@@ -11,30 +11,6 @@
namespace v8 {
namespace internal {
-// ES#sec-promise-resolve-functions
-// Promise Resolve Functions
-BUILTIN(PromiseResolveClosure) {
- HandleScope scope(isolate);
-
- Handle<Context> context(isolate->context(), isolate);
-
- if (PromiseUtils::HasAlreadyVisited(context)) {
- return isolate->heap()->undefined_value();
- }
-
- PromiseUtils::SetAlreadyVisited(context);
- Handle<JSObject> promise = handle(PromiseUtils::GetPromise(context), isolate);
- Handle<Object> value = args.atOrUndefined(isolate, 1);
-
- MaybeHandle<Object> maybe_result;
- Handle<Object> argv[] = {promise, value};
- RETURN_FAILURE_ON_EXCEPTION(
- isolate, Execution::Call(isolate, isolate->promise_resolve(),
- isolate->factory()->undefined_value(),
- arraysize(argv), argv));
- return isolate->heap()->undefined_value();
-}
-
// ES#sec-promise-reject-functions
// Promise Reject Functions
BUILTIN(PromiseRejectClosure) {
@@ -86,6 +62,7 @@ void PromiseInit(CodeStubAssembler* a, compiler::Node* promise,
CSA_ASSERT(a, a->TaggedIsSmi(status));
a->StoreObjectField(promise, JSPromise::kStatusOffset, status);
a->StoreObjectField(promise, JSPromise::kResultOffset, result);
+ a->StoreObjectField(promise, JSPromise::kFlagsOffset, a->SmiConstant(0));
}
void Builtins::Generate_PromiseConstructor(
@@ -286,6 +263,7 @@ compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate,
a->Bind(&out);
return var_value_map.value();
}
+
} // namespace
void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) {
@@ -307,6 +285,24 @@ void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) {
}
namespace {
+
+compiler::Node* PromiseHasHandler(CodeStubAssembler* a,
+ compiler::Node* promise) {
+ typedef compiler::Node Node;
+
+ Node* const flags = a->LoadObjectField(promise, JSPromise::kFlagsOffset);
+ return a->IsSetWord(a->SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
+}
+
+void PromiseSetHasHandler(CodeStubAssembler* a, compiler::Node* promise) {
+ typedef compiler::Node Node;
+
+ Node* const flags = a->LoadObjectField(promise, JSPromise::kFlagsOffset);
+ Node* const new_flags =
+ a->WordOr(flags, a->IntPtrConstant(1 << JSPromise::kHasHandlerBit));
+ a->StoreObjectField(promise, JSPromise::kFlagsOffset, a->SmiTag(new_flags));
+}
+
compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate,
compiler::Node* context,
compiler::Node* object,
@@ -404,7 +400,6 @@ compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a,
typedef CodeStubAssembler::Variable Variable;
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
-
Isolate* isolate = a->isolate();
Node* const native_context = a->LoadNativeContext(context);
@@ -536,16 +531,11 @@ compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a,
a->Bind(&reject);
{
- Callable getproperty_callable = CodeFactory::GetProperty(isolate);
- Node* const key =
- a->HeapConstant(isolate->factory()->promise_has_handler_symbol());
- Node* const has_handler =
- a->CallStub(getproperty_callable, context, promise, key);
-
+ Node* const has_handler = PromiseHasHandler(a, promise);
Label enqueue(a);
// TODO(gsathya): Fold these runtime calls and move to TF.
- a->GotoIf(a->WordEqual(has_handler, a->TrueConstant()), &enqueue);
+ a->GotoIf(has_handler, &enqueue);
a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
a->Goto(&enqueue);
@@ -562,11 +552,7 @@ compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a,
}
a->Bind(&out);
- // TODO(gsathya): Protect with debug check.
- a->CallRuntime(
- Runtime::kSetProperty, context, promise,
- a->HeapConstant(isolate->factory()->promise_has_handler_symbol()),
- a->TrueConstant(), a->SmiConstant(STRICT));
+ PromiseSetHasHandler(a, promise);
// TODO(gsathya): This call will be removed once we don't have to
// deal with deferred objects.
@@ -667,5 +653,244 @@ void Builtins::Generate_PromiseThen(compiler::CodeAssemblerState* state) {
a.Return(result);
}
+namespace {
+
+// Promise fast path implementations rely on unmodified JSPromise instances.
+// We use a fairly coarse granularity for this and simply check whether both
+// the promise itself is unmodified (i.e. its map has not changed) and its
+// prototype is unmodified.
+// TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp
+void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context,
+ compiler::Node* promise,
+ CodeStubAssembler::Label* if_isunmodified,
+ CodeStubAssembler::Label* if_ismodified) {
+ typedef compiler::Node Node;
+
+ // TODO(gsathya): Assert if promise is receiver
+ Node* const map = a->LoadMap(promise);
+ Node* const native_context = a->LoadNativeContext(context);
+ Node* const promise_fun =
+ a->LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
+ Node* const initial_map =
+ a->LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
+ Node* const has_initialmap = a->WordEqual(map, initial_map);
+
+ a->GotoUnless(has_initialmap, if_ismodified);
+
+ Node* const initial_proto_initial_map = a->LoadContextElement(
+ native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX);
+ Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map));
+ Node* const proto_has_initialmap =
+ a->WordEqual(proto_map, initial_proto_initial_map);
+
+ a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
+}
+
+void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context,
+ compiler::Node* promise, compiler::Node* result,
+ CodeStubAssembler::Label* out) {
+ typedef CodeStubAssembler::Variable Variable;
+ typedef CodeStubAssembler::Label Label;
+ typedef compiler::Node Node;
+
+ Isolate* isolate = a->isolate();
+
+ Variable var_reason(a, MachineRepresentation::kTagged),
+ var_then(a, MachineRepresentation::kTagged);
+
+ Label do_enqueue(a), fulfill(a), if_cycle(a, Label::kDeferred),
+ if_rejectpromise(a, Label::kDeferred);
+
+ // 6. If SameValue(resolution, promise) is true, then
+ a->GotoIf(a->SameValue(promise, result, context), &if_cycle);
+
+ // 7. If Type(resolution) is not Object, then
+ a->GotoIf(a->TaggedIsSmi(result), &fulfill);
+ a->GotoUnless(a->IsJSReceiver(result), &fulfill);
+
+ Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred);
+ BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise);
+
+ // Resolution is a native promise and if it's already resolved or
+ // rejected, shortcircuit the resolution procedure by directly
+ // reusing the value from the promise.
+ a->Bind(&if_nativepromise);
+ {
+ Node* const thenable_status =
+ a->LoadObjectField(result, JSPromise::kStatusOffset);
+ Node* const thenable_value =
+ a->LoadObjectField(result, JSPromise::kResultOffset);
+
+ Label if_isnotpending(a);
+ a->GotoUnless(a->SmiEqual(a->SmiConstant(kPromisePending), thenable_status),
+ &if_isnotpending);
+
+ // TODO(gsathya): Use a marker here instead of the actual then
+ // callback, and check for the marker in PromiseResolveThenableJob
+ // and perform PromiseThen.
+ Node* const native_context = a->LoadNativeContext(context);
+ Node* const then =
+ a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
+ var_then.Bind(then);
+ a->Goto(&do_enqueue);
+
+ a->Bind(&if_isnotpending);
+ {
+ Label if_fulfilled(a), if_rejected(a);
+ a->Branch(a->SmiEqual(a->SmiConstant(kPromiseFulfilled), thenable_status),
+ &if_fulfilled, &if_rejected);
+
+ a->Bind(&if_fulfilled);
+ {
+ a->CallRuntime(Runtime::kPromiseFulfill, context, promise,
+ a->SmiConstant(kPromiseFulfilled), thenable_value);
+ PromiseSetHasHandler(a, promise);
+ a->Goto(out);
+ }
+
+ a->Bind(&if_rejected);
+ {
+ Label reject(a);
+ Node* const has_handler = PromiseHasHandler(a, result);
+
+ // Promise has already been rejected, but had no handler.
+ // Revoke previously triggered reject event.
+ a->GotoIf(has_handler, &reject);
+ a->CallRuntime(Runtime::kPromiseRevokeReject, context, result);
+ a->Goto(&reject);
+
+ a->Bind(&reject);
+ // Don't cause a debug event as this case is forwarding a rejection
+ a->CallRuntime(Runtime::kPromiseReject, context, promise,
+ thenable_value, a->FalseConstant());
+ PromiseSetHasHandler(a, result);
+ a->Goto(out);
+ }
+ }
+ }
+
+ a->Bind(&if_notnativepromise);
+ {
+ // 8. Let then be Get(resolution, "then").
+ Node* const then_str = a->HeapConstant(isolate->factory()->then_string());
+ Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
+ Node* const then =
+ a->CallStub(getproperty_callable, context, result, then_str);
+
+ // 9. If then is an abrupt completion, then
+ a->GotoIfException(then, &if_rejectpromise, &var_reason);
+
+ // 11. If IsCallable(thenAction) is false, then
+ a->GotoIf(a->TaggedIsSmi(then), &fulfill);
+ Node* const then_map = a->LoadMap(then);
+ a->GotoUnless(a->IsCallableMap(then_map), &fulfill);
+ var_then.Bind(then);
+ a->Goto(&do_enqueue);
+ }
+
+ a->Bind(&do_enqueue);
+ {
+ Label enqueue(a);
+ a->GotoUnless(a->IsDebugActive(), &enqueue);
+ a->GotoIf(a->TaggedIsSmi(result), &enqueue);
+ a->GotoUnless(a->HasInstanceType(result, JS_PROMISE_TYPE), &enqueue);
+ // Mark the dependency of the new promise on the resolution
+ Node* const key =
+ a->HeapConstant(isolate->factory()->promise_handled_by_symbol());
+ a->CallRuntime(Runtime::kSetProperty, context, result, key, promise,
+ a->SmiConstant(STRICT));
+ a->Goto(&enqueue);
+
+ // 12. Perform EnqueueJob("PromiseJobs",
+ // PromiseResolveThenableJob, « promise, resolution, thenAction
+ // »).
+ a->Bind(&enqueue);
+ a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise,
+ result, var_then.value());
+ a->Goto(out);
+ }
+ // 7.b Return FulfillPromise(promise, resolution).
+ a->Bind(&fulfill);
+ {
+ a->CallRuntime(Runtime::kPromiseFulfill, context, promise,
+ a->SmiConstant(kPromiseFulfilled), result);
+ a->Goto(out);
+ }
+
+ a->Bind(&if_cycle);
+ {
+ // 6.a Let selfResolutionError be a newly created TypeError object.
+ Node* const message_id = a->SmiConstant(MessageTemplate::kPromiseCyclic);
+ Node* const error =
+ a->CallRuntime(Runtime::kNewTypeError, context, message_id, result);
+ var_reason.Bind(error);
+
+ // 6.b Return RejectPromise(promise, selfResolutionError).
+ a->Goto(&if_rejectpromise);
+ }
+
+ // 9.a Return RejectPromise(promise, then.[[Value]]).
+ a->Bind(&if_rejectpromise);
+ {
+ a->CallRuntime(Runtime::kPromiseReject, context, promise,
+ var_reason.value(), a->TrueConstant());
+ a->Goto(out);
+ }
+}
+
+} // namespace
+
+// ES#sec-promise-resolve-functions
+// Promise Resolve Functions
+void Builtins::Generate_PromiseResolveClosure(
+ compiler::CodeAssemblerState* state) {
+ CodeStubAssembler a(state);
+ typedef compiler::Node Node;
+ typedef CodeStubAssembler::Label Label;
+
+ Node* const value = a.Parameter(1);
+ Node* const context = a.Parameter(4);
+
+ Label out(&a);
+
+ // 3. Let alreadyResolved be F.[[AlreadyResolved]].
+ Node* const has_already_visited_slot =
+ a.IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot);
+
+ Node* const has_already_visited =
+ a.LoadFixedArrayElement(context, has_already_visited_slot);
+
+ // 4. If alreadyResolved.[[Value]] is true, return undefined.
+ a.GotoIf(a.SmiEqual(has_already_visited, a.SmiConstant(1)), &out);
+
+ // 5.Set alreadyResolved.[[Value]] to true.
+ a.StoreFixedArrayElement(context, has_already_visited_slot, a.SmiConstant(1));
+
+ // 2. Let promise be F.[[Promise]].
+ Node* const promise = a.LoadFixedArrayElement(
+ context, a.IntPtrConstant(PromiseUtils::kPromiseSlot));
+
+ InternalResolvePromise(&a, context, promise, value, &out);
+
+ a.Bind(&out);
+ a.Return(a.UndefinedConstant());
+}
+
+void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) {
+ CodeStubAssembler a(state);
+ typedef compiler::Node Node;
+ typedef CodeStubAssembler::Label Label;
+
+ Node* const promise = a.Parameter(1);
+ Node* const result = a.Parameter(2);
+ Node* const context = a.Parameter(5);
+
+ Label out(&a);
+ InternalResolvePromise(&a, context, promise, result, &out);
+
+ a.Bind(&out);
+ a.Return(a.UndefinedConstant());
+}
+
} // namespace internal
} // namespace v8
« no previous file with comments | « src/builtins/builtins.h ('k') | src/contexts.h » ('j') | src/js/promise.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698