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