Index: src/builtins/builtins-promise.cc |
diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc |
index 49de29a92958c996c00d7cc22b160920622478f7..55cc04a93aaa7343ef731c7785efdde36b164862 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) { |
@@ -665,5 +641,209 @@ void Builtins::Generate_PromiseThen(compiler::CodeAssemblerState* state) { |
a.Return(result); |
} |
+namespace { |
+// RegExp fast path implementations rely on unmodified JSPromise instances. |
jgruber
2016/12/05 09:25:17
Promise
gsathya
2016/12/05 16:03:05
Done.
|
+// 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); |
+ |
+ // TODO(ishell): Update this check once map changes for constant field |
jgruber
2016/12/05 09:25:17
We can remove this TODO since the mentioned change
gsathya
2016/12/05 16:03:05
Done.
|
+ // tracking are landing. |
+ a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
+} |
+} // namespace |
+ |
+void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context, |
+ compiler::Node* promise, compiler::Node* result) { |
+ typedef CodeStubAssembler::Variable Variable; |
jgruber
2016/12/05 09:25:17
As mentioned offline, you might consider switching
gsathya
2016/12/05 16:03:05
I'm going to wait until I finish the rest of the b
jgruber
2016/12/07 12:59:38
Acknowledged, sgtm.
|
+ typedef CodeStubAssembler::Label Label; |
+ typedef compiler::Node Node; |
+ |
+ Isolate* isolate = a->isolate(); |
+ |
+ Variable var_reason(a, MachineRepresentation::kTagged); |
+ |
+ Label out(a), fulfill(a), if_cycle(a, Label::kDeferred), |
+ if_rejectpromise(a, Label::kDeferred); |
+ |
+ // 6. If SameValue(resolution, promise) is true, then |
+ a->GotoIf(a->WordEqual(promise, result), &if_cycle); |
jgruber
2016/12/05 09:25:17
SameValue is more involved than WordEqual. You can
gsathya
2016/12/05 16:03:05
Done.
|
+ |
+ // 7. If Type(resolution) is not Object, then |
+ a->GotoIf(a->TaggedIsSmi(result), &fulfill); |
+ Node* const result_instance_type = a->LoadMapInstanceType(a->LoadMap(result)); |
+ a->GotoUnless(a->IsJSReceiverInstanceType(result_instance_type), &fulfill); |
jgruber
2016/12/05 09:25:17
IsJSReceiver()
gsathya
2016/12/05 16:03:05
Done.
|
+ |
+ Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred); |
+ BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise); |
+ |
+ 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); |
+ |
+ Node* const native_context = a->LoadNativeContext(context); |
+ Node* const then = |
+ a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); |
+ |
+ a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, |
+ result, then); |
+ a->Goto(&out); |
+ |
+ 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); |
+ a->Goto(&out); |
+ } |
+ |
+ a->Bind(&if_rejected); |
+ { |
+ a->CallRuntime(Runtime::kPromiseReject, context, promise, |
+ thenable_value, a->FalseConstant()); |
+ 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); |
+ |
+ // 12. Perform EnqueueJob("PromiseJobs", |
+ // PromiseResolveThenableJob, « promise, resolution, thenAction |
+ // »). |
+ a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, |
+ result, then); |
+ 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); |
+ } |
+ |
+ a->Bind(&out); |
+ a->Return(a->UndefinedConstant()); |
+} |
+ |
+// 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); |
+ Isolate* isolate = a.isolate(); |
+ |
+ 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); |
jgruber
2016/12/05 09:25:17
Is line 825 below the only place where we write in
gsathya
2016/12/05 16:03:05
PromiseUtils::CreateResolvingFunctions and Promise
jgruber
2016/12/07 12:59:38
Ok. IMHO the fact that we use a Smi 0/1 encoding i
|
+ |
+ // 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); |
+ |
+ a.Bind(&out); |
+ a.Return(a.UndefinedConstant()); |
+} |
+ |
+void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) { |
+ CodeStubAssembler a(state); |
+ typedef compiler::Node Node; |
+ |
+ Node* const promise = a.Parameter(1); |
+ Node* const result = a.Parameter(2); |
+ Node* const context = a.Parameter(5); |
+ |
+ InternalResolvePromise(&a, context, promise, result); |
+} |
+ |
} // namespace internal |
} // namespace v8 |