Commit 2041c504 authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Move Promise.prototype.catch to TF

This patch also refactors most of PromiseThen into InternalPromiseThen to
be reused with PromiseCatch and also changes InternalResolvePromise to
return and not branch.

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2596553002
Cr-Commit-Position: refs/heads/master@{#41902}
parent 91a7a916
......@@ -1872,16 +1872,15 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
Handle<JSFunction> promise_then =
SimpleCreateFunction(isolate, isolate->factory()->then_string(),
Builtins::kPromiseThen, 2, true);
JSObject::AddProperty(prototype, isolate->factory()->then_string(),
promise_then, DONT_ENUM);
SimpleInstallFunction(prototype, isolate->factory()->then_string(),
Builtins::kPromiseThen, 2, true);
InstallWithIntrinsicDefaultProto(isolate, promise_then,
Context::PROMISE_THEN_INDEX);
// TODO(gsathya): Move to TF
SimpleInstallFunction(prototype, "catch", Builtins::kIllegal, 1, true,
DONT_ENUM);
Handle<JSFunction> promise_catch = SimpleInstallFunction(
prototype, "catch", Builtins::kPromiseCatch, 1, true, DONT_ENUM);
InstallWithIntrinsicDefaultProto(isolate, promise_catch,
Context::PROMISE_CATCH_INDEX);
SimpleInstallGetter(promise_fun, factory->symbol_species_string(),
factory->species_symbol(), Builtins::kReturnReceiver,
......
......@@ -180,6 +180,66 @@ void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise,
StoreObjectField(promise, offset, new_elements);
}
Node* PromiseBuiltinsAssembler::InternalPromiseThen(Node* context,
Node* promise,
Node* on_resolve,
Node* on_reject) {
Isolate* isolate = this->isolate();
// 2. If IsPromise(promise) is false, throw a TypeError exception.
ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
"Promise.prototype.then");
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
Node* constructor = SpeciesConstructor(context, promise, promise_fun);
// 4. Let resultCapability be ? NewPromiseCapability(C).
Callable call_callable = CodeFactory::Call(isolate);
Label fast_promise_capability(this), promise_capability(this),
perform_promise_then(this);
Variable var_deferred(this, MachineRepresentation::kTagged);
Branch(WordEqual(promise_fun, constructor), &fast_promise_capability,
&promise_capability);
// TODO(gsathya): Remove deferred object and move
// NewPromiseCapabability functions to TF.
Bind(&fast_promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const promise_internal_capability = LoadContextElement(
native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX);
Node* const capability =
CallJS(call_callable, context, promise_internal_capability,
UndefinedConstant(), promise);
var_deferred.Bind(capability);
Goto(&perform_promise_then);
}
Bind(&promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const new_promise_capability = LoadContextElement(
native_context, Context::NEW_PROMISE_CAPABILITY_INDEX);
Node* const capability =
CallJS(call_callable, context, new_promise_capability,
UndefinedConstant(), constructor);
var_deferred.Bind(capability);
Goto(&perform_promise_then);
}
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
Bind(&perform_promise_then);
Node* const result = InternalPerformPromiseThen(
context, promise, on_resolve, on_reject, var_deferred.value());
return result;
}
Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context,
Node* promise,
Node* on_resolve,
......@@ -378,15 +438,14 @@ void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise,
void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Node* promise,
Node* result,
Label* out) {
Node* result) {
Isolate* isolate = this->isolate();
Variable var_reason(this, MachineRepresentation::kTagged),
var_then(this, MachineRepresentation::kTagged);
Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred),
if_rejectpromise(this, Label::kDeferred);
if_rejectpromise(this, Label::kDeferred), out(this);
Label cycle_check(this);
GotoUnless(IsPromiseHookEnabled(), &cycle_check);
......@@ -438,7 +497,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
CallRuntime(Runtime::kPromiseFulfill, context, promise,
SmiConstant(v8::Promise::kFulfilled), thenable_value);
PromiseSetHasHandler(promise);
Goto(out);
Goto(&out);
}
Bind(&if_rejected);
......@@ -457,7 +516,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
CallRuntime(Runtime::kPromiseReject, context, promise, thenable_value,
FalseConstant());
PromiseSetHasHandler(result);
Goto(out);
Goto(&out);
}
}
}
......@@ -500,7 +559,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Bind(&enqueue);
CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise,
result, var_then.value());
Goto(out);
Goto(&out);
}
// 7.b Return FulfillPromise(promise, resolution).
......@@ -508,7 +567,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
{
CallRuntime(Runtime::kPromiseFulfill, context, promise,
SmiConstant(v8::Promise::kFulfilled), result);
Goto(out);
Goto(&out);
}
Bind(&if_cycle);
......@@ -528,8 +587,10 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
{
CallRuntime(Runtime::kPromiseReject, context, promise, var_reason.value(),
TrueConstant());
Goto(out);
Goto(&out);
}
Bind(&out);
}
// ES#sec-promise-reject-functions
......@@ -762,65 +823,17 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
Return(result);
}
// ES#sec-promise.prototype.then
// Promise.prototype.catch ( onFulfilled, onRejected )
TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) {
// 1. Let promise be the this value.
Node* const promise = Parameter(0);
Node* const on_resolve = Parameter(1);
Node* const on_reject = Parameter(2);
Node* const context = Parameter(5);
Isolate* isolate = this->isolate();
// 2. If IsPromise(promise) is false, throw a TypeError exception.
ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
"Promise.prototype.then");
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
Node* constructor = SpeciesConstructor(context, promise, promise_fun);
// 4. Let resultCapability be ? NewPromiseCapability(C).
Callable call_callable = CodeFactory::Call(isolate);
Label fast_promise_capability(this), promise_capability(this),
perform_promise_then(this);
Variable var_deferred(this, MachineRepresentation::kTagged);
Branch(WordEqual(promise_fun, constructor), &fast_promise_capability,
&promise_capability);
// TODO(gsathya): Remove deferred object and move
// NewPromiseCapabability functions to TF.
Bind(&fast_promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const promise_internal_capability = LoadContextElement(
native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX);
Node* const capability =
CallJS(call_callable, context, promise_internal_capability,
UndefinedConstant(), promise);
var_deferred.Bind(capability);
Goto(&perform_promise_then);
}
Bind(&promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const new_promise_capability = LoadContextElement(
native_context, Context::NEW_PROMISE_CAPABILITY_INDEX);
Node* const capability =
CallJS(call_callable, context, new_promise_capability,
UndefinedConstant(), constructor);
var_deferred.Bind(capability);
Goto(&perform_promise_then);
}
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
Bind(&perform_promise_then);
Node* const result = InternalPerformPromiseThen(
context, promise, on_resolve, on_reject, var_deferred.value());
Node* const result =
InternalPromiseThen(context, promise, on_resolve, on_reject);
Return(result);
}
......@@ -849,7 +862,8 @@ TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) {
Node* const promise = LoadFixedArrayElement(
context, IntPtrConstant(PromiseUtils::kPromiseSlot));
InternalResolvePromise(context, promise, value, &out);
InternalResolvePromise(context, promise, value);
Return(UndefinedConstant());
Bind(&out);
Return(UndefinedConstant());
......@@ -860,10 +874,7 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
Node* const result = Parameter(2);
Node* const context = Parameter(5);
Label out(this);
InternalResolvePromise(context, promise, result, &out);
Bind(&out);
InternalResolvePromise(context, promise, result);
Return(UndefinedConstant());
}
......@@ -945,8 +956,8 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
Branch(IsUndefined(on_resolve), &if_internalhandler, &if_customhandler);
Bind(&if_internalhandler);
InternalResolvePromise(context, deferred_promise, result,
&promisehook_after);
InternalResolvePromise(context, deferred_promise, result);
Goto(&promisehook_after);
Bind(&if_customhandler);
{
......@@ -990,5 +1001,38 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
}
}
// ES#sec-promise.prototype.catch
// Promise.prototype.catch ( onRejected )
TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) {
// 1. Let promise be the this value.
Node* const promise = Parameter(0);
Node* const on_resolve = UndefinedConstant();
Node* const on_reject = Parameter(1);
Node* const context = Parameter(4);
Label if_internalthen(this), if_customthen(this, Label::kDeferred);
BranchIfFastPath(context, promise, &if_internalthen, &if_customthen);
Bind(&if_internalthen);
{
Node* const result =
InternalPromiseThen(context, promise, on_resolve, on_reject);
Return(result);
}
Bind(&if_customthen);
{
Isolate* isolate = this->isolate();
Node* const then_str = HeapConstant(isolate->factory()->then_string());
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const then =
CallStub(getproperty_callable, context, promise, then_str);
Callable call_callable = CodeFactory::Call(isolate);
Node* const result =
CallJS(call_callable, context, then, promise, on_resolve, on_reject);
Return(result);
}
}
} // namespace internal
} // namespace v8
......@@ -29,12 +29,14 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void AppendPromiseCallback(int offset, compiler::Node* promise,
compiler::Node* value);
Node* InternalPromiseThen(Node* context, Node* promise, Node* on_resolve,
Node* on_reject);
Node* InternalPerformPromiseThen(Node* context, Node* promise,
Node* on_resolve, Node* on_reject,
Node* deferred);
void InternalResolvePromise(Node* context, Node* promise, Node* result,
Label* out);
void InternalResolvePromise(Node* context, Node* promise, Node* result);
void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified,
Label* if_ismodified);
......
......@@ -571,6 +571,7 @@ namespace internal {
TFJ(PromiseResolveClosure, 1) \
CPP(PromiseRejectClosure) \
TFJ(PromiseThen, 2) \
TFJ(PromiseCatch, 1) \
TFJ(PromiseCreateAndSet, 2) \
TFJ(PerformPromiseThen, 4) \
TFJ(ResolvePromise, 2) \
......
......@@ -142,12 +142,6 @@ function PromiseReject(r) {
}
}
// ES#sec-promise.prototype.catch
// Promise.prototype.catch ( onRejected )
function PromiseCatch(onReject) {
return this.then(UNDEFINED, onReject);
}
// Combinators.
// ES#sec-promise.resolve
......@@ -345,10 +339,7 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
"resolve", PromiseResolve
]);
%SetCode(GlobalPromise.prototype.catch, PromiseCatch);
%InstallToContext([
"promise_catch", GlobalPromise.prototype.catch,
"promise_create", PromiseCreate,
"promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
"promise_reject", DoRejectPromise,
......
......@@ -49,7 +49,7 @@ function getStack(error) {
map(line => line.replace(/^\s*at (@?[a-zA-Z0-9_\.\[\]]+)(.*)/, "$1"));
// remove `Promise.then()` invocation by assertEqualsAsync()
if (stack[1] === "assertEqualsAsync") return [];
if (stack[2] === "assertEqualsAsync") return [];
return stack.reverse();
}
......@@ -96,6 +96,6 @@ assertEqualsAsync(
}),
"should call Promise[@@Species] after non-internal Then");
assertEquals([
"@@Species: [@testThenOnReturnedPromise > FakePromise]",
"@@Species: [@testThenOnReturnedPromise > Promise.then > FakePromise]",
"Then: foo"
], log);
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