[promises] Port Promise.prototype.then to Torque.

Bug: v8:9838
Change-Id: Ib2741501330629a29c1f3fe4f4a93a73982ab4de
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1929400
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65368}
This commit is contained in:
Joshua Litt 2019-12-06 05:48:49 -08:00 committed by Commit Bot
parent 406622277a
commit bed702fa07
5 changed files with 94 additions and 109 deletions

View File

@ -973,6 +973,7 @@ torque_files = [
"src/builtins/object.tq",
"src/builtins/promise-abstract-operations.tq",
"src/builtins/promise-constructor.tq",
"src/builtins/promise-then.tq",
"src/builtins/proxy-constructor.tq",
"src/builtins/proxy-delete-property.tq",
"src/builtins/proxy-get-property.tq",

View File

@ -718,8 +718,6 @@ namespace internal {
TFJ(PromiseConstructorLazyDeoptContinuation, 4, kReceiver, kPromise, \
kReject, kException, kResult) \
CPP(IsPromise) \
/* ES #sec-promise.prototype.then */ \
TFJ(PromisePrototypeThen, 2, kReceiver, kOnFulfilled, kOnRejected) \
/* ES #sec-promise.prototype.catch */ \
TFJ(PromisePrototypeCatch, 1, kReceiver, kOnRejected) \
/* ES #sec-promisereactionjob */ \

View File

@ -412,10 +412,8 @@ void PromiseBuiltinsAssembler::GotoIfNotPromiseResolveLookupChainIntact(
}
void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) {
CSA_ASSERT(this, IsNativeContext(native_context));
CSA_ASSERT(this, IsJSPromiseMap(promise_map));
TNode<NativeContext> native_context, TNode<Map> promise_map, Label* if_fast,
Label* if_slow) {
TNode<Object> promise_prototype =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfForceSlowPath(if_slow);
@ -528,101 +526,6 @@ TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
Return(promise);
}
// ES#sec-promise.prototype.then
// Promise.prototype.then ( onFulfilled, onRejected )
TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) {
// 1. Let promise be the this value.
const TNode<Object> maybe_promise = CAST(Parameter(Descriptor::kReceiver));
const TNode<Object> on_fulfilled = CAST(Parameter(Descriptor::kOnFulfilled));
const TNode<Object> on_rejected = CAST(Parameter(Descriptor::kOnRejected));
const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// 2. If IsPromise(promise) is false, throw a TypeError exception.
ThrowIfNotInstanceType(context, maybe_promise, JS_PROMISE_TYPE,
"Promise.prototype.then");
TNode<JSPromise> js_promise = CAST(maybe_promise);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
Label fast_promise_capability(this), slow_constructor(this, Label::kDeferred),
slow_promise_capability(this, Label::kDeferred);
const TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<JSFunction> promise_fun =
CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
const TNode<Map> promise_map = LoadMap(js_promise);
BranchIfPromiseSpeciesLookupChainIntact(
native_context, promise_map, &fast_promise_capability, &slow_constructor);
BIND(&slow_constructor);
TNode<JSReceiver> constructor =
SpeciesConstructor(native_context, js_promise, promise_fun);
Branch(TaggedEqual(constructor, promise_fun), &fast_promise_capability,
&slow_promise_capability);
// 4. Let resultCapability be ? NewPromiseCapability(C).
Label perform_promise_then(this);
TVARIABLE(Object, var_result_promise);
TVARIABLE(HeapObject, var_result_promise_or_capability);
BIND(&fast_promise_capability);
{
const TNode<JSPromise> result_promise =
AllocateAndInitJSPromise(context, js_promise);
var_result_promise_or_capability = result_promise;
var_result_promise = result_promise;
Goto(&perform_promise_then);
}
BIND(&slow_promise_capability);
{
const TNode<Oddball> debug_event = TrueConstant();
const TNode<PromiseCapability> capability = CAST(CallBuiltin(
Builtins::kNewPromiseCapability, context, constructor, debug_event));
var_result_promise =
LoadObjectField(capability, PromiseCapability::kPromiseOffset);
var_result_promise_or_capability = capability;
Goto(&perform_promise_then);
}
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
BIND(&perform_promise_then);
{
// We do some work of the PerformPromiseThen operation here, in that
// we check the handlers and turn non-callable handlers into undefined.
// This is because this is the one and only callsite of PerformPromiseThen
// that has to do this.
// 3. If IsCallable(onFulfilled) is false, then
// a. Set onFulfilled to undefined.
TVARIABLE(Object, var_on_fulfilled, on_fulfilled);
Label if_fulfilled_done(this), if_fulfilled_notcallable(this);
GotoIf(TaggedIsSmi(on_fulfilled), &if_fulfilled_notcallable);
Branch(IsCallable(CAST(on_fulfilled)), &if_fulfilled_done,
&if_fulfilled_notcallable);
BIND(&if_fulfilled_notcallable);
var_on_fulfilled = UndefinedConstant();
Goto(&if_fulfilled_done);
BIND(&if_fulfilled_done);
// 4. If IsCallable(onRejected) is false, then
// a. Set onRejected to undefined.
TVARIABLE(Object, var_on_rejected, on_rejected);
Label if_rejected_done(this), if_rejected_notcallable(this);
GotoIf(TaggedIsSmi(on_rejected), &if_rejected_notcallable);
Branch(IsCallable(CAST(on_rejected)), &if_rejected_done,
&if_rejected_notcallable);
BIND(&if_rejected_notcallable);
var_on_rejected = UndefinedConstant();
Goto(&if_rejected_done);
BIND(&if_rejected_done);
PerformPromiseThenImpl(context, js_promise, CAST(var_on_fulfilled.value()),
CAST(var_on_rejected.value()),
var_result_promise_or_capability.value());
Return(var_result_promise.value());
}
}
// ES#sec-promise.prototype.catch
// Promise.prototype.catch ( onRejected )
TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {

View File

@ -72,6 +72,14 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
TNode<Object> executor, Label* if_noaccess);
void PromiseInit(Node* promise);
// We can shortcut the SpeciesConstructor on {promise_map} if it's
// [[Prototype]] is the (initial) Promise.prototype and the @@species
// protector is intact, as that guards the lookup path for the "constructor"
// property on JSPromise instances which have the %PromisePrototype%.
void BranchIfPromiseSpeciesLookupChainIntact(
TNode<NativeContext> native_context, TNode<Map> promise_map,
Label* if_fast, Label* if_slow);
protected:
void PromiseSetHasHandler(Node* promise);
void PromiseSetHandledHint(Node* promise);
@ -87,14 +95,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
SloppyTNode<Object> constructor,
Label* if_slow);
// We can shortcut the SpeciesConstructor on {promise_map} if it's
// [[Prototype]] is the (initial) Promise.prototype and the @@species
// protector is intact, as that guards the lookup path for the "constructor"
// property on JSPromise instances which have the %PromisePrototype%.
void BranchIfPromiseSpeciesLookupChainIntact(Node* native_context,
Node* promise_map,
Label* if_fast, Label* if_slow);
// We can skip the "then" lookup on {receiver_map} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then() protector
// is intact, as that guards the lookup path for the "then" property

View File

@ -0,0 +1,83 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-promise-gen.h'
namespace promise {
extern macro
PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
NativeContext, Map): never labels IsIntact,
IsNotIntact;
macro
IsPromiseSpeciesLookupChainIntact(
nativeContext: NativeContext, promiseMap: Map): bool {
BranchIfPromiseSpeciesLookupChainIntact(nativeContext, promiseMap)
otherwise return true, return false;
}
extern macro
PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Context, Object):
JSPromise;
// https://tc39.es/ecma262/#sec-promise.prototype.then
transitioning javascript builtin
PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
onFulfilled: JSAny, onRejected: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. If IsPromise(promise) is false, throw a TypeError exception.
const promise = Cast<JSPromise>(receiver) otherwise ThrowTypeError(
kIncompatibleMethodReceiver, 'Promise.prototype.then', receiver);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const promiseFun = UnsafeCast<JSFunction>(context[PROMISE_FUNCTION_INDEX]);
// 4. Let resultCapability be ? NewPromiseCapability(C).
let resultPromiseOrCapability: JSPromise|PromiseCapability;
let resultPromise: JSAny;
try {
if (IsPromiseSpeciesLookupChainIntact(context, promise.map)) {
goto AllocateAndInit;
}
const constructor = SpeciesConstructor(promise, promiseFun);
if (TaggedEqual(constructor, promiseFun)) {
goto AllocateAndInit;
} else {
const promiseCapability = NewPromiseCapability(constructor, True);
resultPromiseOrCapability = promiseCapability;
resultPromise = promiseCapability.promise;
}
}
label AllocateAndInit {
const resultJSPromise = AllocateAndInitJSPromise(context, promise);
resultPromiseOrCapability = resultJSPromise;
resultPromise = resultJSPromise;
}
// We do some work of the PerformPromiseThen operation here, in that
// we check the handlers and turn non-callable handlers into undefined.
// This is because this is the one and only callsite of PerformPromiseThen
// that has to do this.
// 3. If IsCallable(onFulfilled) is false, then
// a. Set onFulfilled to undefined.
const onFulfilled = TaggedIsCallable(onFulfilled) ?
UnsafeCast<Callable>(onFulfilled) :
Undefined;
// 4. If IsCallable(onRejected) is false, then
// a. Set onRejected to undefined.
const onRejected = TaggedIsCallable(onRejected) ?
UnsafeCast<Callable>(onRejected) :
Undefined;
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
PerformPromiseThenImpl(
promise, onFulfilled, onRejected, resultPromiseOrCapability);
return resultPromise;
}
}