[promises] Port ResolvePromise to Torque.
Bug: v8:9838 Change-Id: I9cfa7af623af3b387962ea4fa90cfc599612f976 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1958961 Reviewed-by: Maya Lekova <mslekova@chromium.org> Commit-Queue: Joshua Litt <joshualitt@chromium.org> Cr-Commit-Position: refs/heads/master@{#65465}
This commit is contained in:
parent
a50b1ea17f
commit
8d84d11d8b
@ -711,9 +711,6 @@ namespace internal {
|
||||
TFS(ForInFilter, kKey, kObject) \
|
||||
\
|
||||
/* Promise */ \
|
||||
/* ES #sec-promise-resolve-functions */ \
|
||||
/* Starting at step 6 of "Promise Resolve Functions" */ \
|
||||
TFS(ResolvePromise, kPromise, kResolution) \
|
||||
TFJ(PromiseGetCapabilitiesExecutor, 2, kReceiver, kResolve, kReject) \
|
||||
TFJ(PromiseConstructorLazyDeoptContinuation, 4, kReceiver, kPromise, \
|
||||
kReject, kException, kResult) \
|
||||
|
@ -699,115 +699,6 @@ TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
|
||||
var_catch_finally.value()));
|
||||
}
|
||||
|
||||
// ES #sec-promise-resolve-functions
|
||||
TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
|
||||
const TNode<JSPromise> promise = CAST(Parameter(Descriptor::kPromise));
|
||||
const TNode<Object> resolution = CAST(Parameter(Descriptor::kResolution));
|
||||
const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
|
||||
Label do_enqueue(this), if_fulfill(this), if_reject(this, Label::kDeferred),
|
||||
if_runtime(this, Label::kDeferred);
|
||||
TVARIABLE(Object, var_reason);
|
||||
TVARIABLE(JSReceiver, var_then);
|
||||
|
||||
// If promise hook is enabled or the debugger is active, let
|
||||
// the runtime handle this operation, which greatly reduces
|
||||
// the complexity here and also avoids a couple of back and
|
||||
// forth between JavaScript and C++ land.
|
||||
GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
|
||||
&if_runtime);
|
||||
|
||||
// 6. If SameValue(resolution, promise) is true, then
|
||||
// We can use pointer comparison here, since the {promise} is guaranteed
|
||||
// to be a JSPromise inside this function and thus is reference comparable.
|
||||
GotoIf(TaggedEqual(promise, resolution), &if_runtime);
|
||||
|
||||
// 7. If Type(resolution) is not Object, then
|
||||
GotoIf(TaggedIsSmi(resolution), &if_fulfill);
|
||||
TNode<Map> resolution_map = LoadMap(CAST(resolution));
|
||||
GotoIfNot(IsJSReceiverMap(resolution_map), &if_fulfill);
|
||||
|
||||
// We can skip the "then" lookup on {resolution} if its [[Prototype]]
|
||||
// is the (initial) Promise.prototype and the Promise#then protector
|
||||
// is intact, as that guards the lookup path for the "then" property
|
||||
// on JSPromise instances which have the (initial) %PromisePrototype%.
|
||||
Label if_fast(this), if_receiver(this), if_slow(this, Label::kDeferred);
|
||||
const TNode<NativeContext> native_context = LoadNativeContext(context);
|
||||
GotoIfForceSlowPath(&if_slow);
|
||||
GotoIf(IsPromiseThenProtectorCellInvalid(), &if_slow);
|
||||
GotoIfNot(IsJSPromiseMap(resolution_map), &if_receiver);
|
||||
const TNode<Object> promise_prototype =
|
||||
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
|
||||
Branch(TaggedEqual(LoadMapPrototype(resolution_map), promise_prototype),
|
||||
&if_fast, &if_slow);
|
||||
|
||||
BIND(&if_fast);
|
||||
{
|
||||
// The {resolution} is a native Promise in this case.
|
||||
var_then =
|
||||
CAST(LoadContextElement(native_context, Context::PROMISE_THEN_INDEX));
|
||||
Goto(&do_enqueue);
|
||||
}
|
||||
|
||||
BIND(&if_receiver);
|
||||
{
|
||||
// We can skip the lookup of "then" if the {resolution} is a (newly
|
||||
// created) IterResultObject, as the Promise#then() protector also
|
||||
// ensures that the intrinsic %ObjectPrototype% doesn't contain any
|
||||
// "then" property. This helps to avoid negative lookups on iterator
|
||||
// results from async generators.
|
||||
CSA_ASSERT(this, IsJSReceiverMap(resolution_map));
|
||||
CSA_ASSERT(this, Word32BinaryNot(IsPromiseThenProtectorCellInvalid()));
|
||||
const TNode<Object> iterator_result_map =
|
||||
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
|
||||
Branch(TaggedEqual(resolution_map, iterator_result_map), &if_fulfill,
|
||||
&if_slow);
|
||||
}
|
||||
|
||||
BIND(&if_slow);
|
||||
{
|
||||
// 8. Let then be Get(resolution, "then").
|
||||
TNode<Object> then =
|
||||
GetProperty(context, resolution, isolate()->factory()->then_string());
|
||||
|
||||
// 9. If then is an abrupt completion, then
|
||||
GotoIfException(then, &if_reject, &var_reason);
|
||||
|
||||
// 11. If IsCallable(thenAction) is false, then
|
||||
GotoIf(TaggedIsSmi(then), &if_fulfill);
|
||||
const TNode<Map> then_map = LoadMap(CAST(then));
|
||||
GotoIfNot(IsCallableMap(then_map), &if_fulfill);
|
||||
var_then = CAST(then);
|
||||
Goto(&do_enqueue);
|
||||
}
|
||||
|
||||
BIND(&do_enqueue);
|
||||
{
|
||||
// 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
|
||||
// «promise, resolution, thenAction»).
|
||||
const TNode<PromiseResolveThenableJobTask> task =
|
||||
AllocatePromiseResolveThenableJobTask(promise, var_then.value(),
|
||||
CAST(resolution), native_context);
|
||||
TailCallBuiltin(Builtins::kEnqueueMicrotask, native_context, task);
|
||||
}
|
||||
|
||||
BIND(&if_fulfill);
|
||||
{
|
||||
// 7.b Return FulfillPromise(promise, resolution).
|
||||
TailCallBuiltin(Builtins::kFulfillPromise, context, promise, resolution);
|
||||
}
|
||||
|
||||
BIND(&if_runtime);
|
||||
Return(CallRuntime(Runtime::kResolvePromise, context, promise, resolution));
|
||||
|
||||
BIND(&if_reject);
|
||||
{
|
||||
// 9.a Return RejectPromise(promise, then.[[Value]]).
|
||||
TailCallBuiltin(Builtins::kRejectPromise, context, promise,
|
||||
var_reason.value(), FalseConstant());
|
||||
}
|
||||
}
|
||||
|
||||
TNode<Object> PromiseBuiltinsAssembler::PerformPromiseAll(
|
||||
Node* context, Node* constructor, Node* capability,
|
||||
const IteratorRecord& iterator,
|
||||
|
@ -55,7 +55,7 @@ namespace promise {
|
||||
ResolvePromise(Context, JSPromise, JSAny): JSAny;
|
||||
|
||||
extern transitioning builtin
|
||||
EnqueueMicrotask(Context, PromiseReactionJobTask): Undefined;
|
||||
EnqueueMicrotask(Context, Microtask): Undefined;
|
||||
|
||||
macro
|
||||
ExtractHandlerContext(implicit context: Context)(handler: Callable|Undefined):
|
||||
|
@ -4,6 +4,11 @@
|
||||
|
||||
#include 'src/builtins/builtins-promise-gen.h'
|
||||
|
||||
namespace runtime {
|
||||
extern transitioning runtime
|
||||
ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny;
|
||||
}
|
||||
|
||||
namespace promise {
|
||||
const kCalledOnNonObject: constexpr MessageTemplate
|
||||
generates 'MessageTemplate::kCalledOnNonObject';
|
||||
@ -76,4 +81,114 @@ namespace promise {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern macro IsJSReceiverMap(Map): bool;
|
||||
|
||||
extern macro IsPromiseThenProtectorCellInvalid(): bool;
|
||||
|
||||
extern macro ThenStringConstant(): String;
|
||||
|
||||
const kThenString: String = ThenStringConstant();
|
||||
|
||||
extern macro PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
|
||||
JSPromise, JSReceiver, JSReceiver,
|
||||
Context): PromiseResolveThenableJobTask;
|
||||
|
||||
transitioning builtin
|
||||
ResolvePromise(implicit context:
|
||||
Context)(promise: JSPromise, resolution: JSAny): JSAny {
|
||||
// 6. If SameValue(resolution, promise) is true, then
|
||||
// If promise hook is enabled or the debugger is active, let
|
||||
// the runtime handle this operation, which greatly reduces
|
||||
// the complexity here and also avoids a couple of back and
|
||||
// forth between JavaScript and C++ land.
|
||||
// We also let the runtime handle it if promise == resolution.
|
||||
// We can use pointer comparison here, since the {promise} is guaranteed
|
||||
// to be a JSPromise inside this function and thus is reference comparable.
|
||||
if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
|
||||
TaggedEqual(promise, resolution))
|
||||
deferred {
|
||||
return runtime::ResolvePromise(promise, resolution);
|
||||
}
|
||||
|
||||
let then: Object = Undefined;
|
||||
try {
|
||||
// 7. If Type(resolution) is not Object, then
|
||||
// 7.b Return FulfillPromise(promise, resolution).
|
||||
if (TaggedIsSmi(resolution)) {
|
||||
return FulfillPromise(promise, resolution);
|
||||
}
|
||||
|
||||
const heapResolution = UnsafeCast<HeapObject>(resolution);
|
||||
const resolutionMap = heapResolution.map;
|
||||
if (!IsJSReceiverMap(resolutionMap)) {
|
||||
return FulfillPromise(promise, resolution);
|
||||
}
|
||||
|
||||
// We can skip the "then" lookup on {resolution} if its [[Prototype]]
|
||||
// is the (initial) Promise.prototype and the Promise#then protector
|
||||
// is intact, as that guards the lookup path for the "then" property
|
||||
// on JSPromise instances which have the (initial) %PromisePrototype%.
|
||||
if (IsForceSlowPath()) {
|
||||
goto Slow;
|
||||
}
|
||||
|
||||
if (IsPromiseThenProtectorCellInvalid()) {
|
||||
goto Slow;
|
||||
}
|
||||
|
||||
const nativeContext = LoadNativeContext(context);
|
||||
if (!IsJSPromiseMap(resolutionMap)) {
|
||||
// We can skip the lookup of "then" if the {resolution} is a (newly
|
||||
// created) IterResultObject, as the Promise#then() protector also
|
||||
// ensures that the intrinsic %ObjectPrototype% doesn't contain any
|
||||
// "then" property. This helps to avoid negative lookups on iterator
|
||||
// results from async generators.
|
||||
assert(IsJSReceiverMap(resolutionMap));
|
||||
assert(!IsPromiseThenProtectorCellInvalid());
|
||||
if (resolutionMap == nativeContext[ITERATOR_RESULT_MAP_INDEX]) {
|
||||
return FulfillPromise(promise, resolution);
|
||||
} else {
|
||||
goto Slow;
|
||||
}
|
||||
}
|
||||
|
||||
const promisePrototype = nativeContext[PROMISE_PROTOTYPE_INDEX];
|
||||
if (resolutionMap.prototype == promisePrototype) {
|
||||
// The {resolution} is a native Promise in this case.
|
||||
then = nativeContext[PROMISE_THEN_INDEX];
|
||||
goto Enqueue;
|
||||
}
|
||||
goto Slow;
|
||||
}
|
||||
label Slow deferred {
|
||||
// 8. Let then be Get(resolution, "then").
|
||||
// 9. If then is an abrupt completion, then
|
||||
// 9.a Return RejectPromise(promise, then.[[Value]]).
|
||||
try {
|
||||
then = GetProperty(resolution, kThenString);
|
||||
} catch (e) {
|
||||
return RejectPromise(promise, e, False);
|
||||
}
|
||||
|
||||
// 11. If IsCallable(thenAction) is false, then
|
||||
if (TaggedIsSmi(then)) {
|
||||
return FulfillPromise(promise, resolution);
|
||||
}
|
||||
|
||||
if (!IsCallable(UnsafeCast<HeapObject>(then))) {
|
||||
return FulfillPromise(promise, resolution);
|
||||
}
|
||||
goto Enqueue;
|
||||
}
|
||||
label Enqueue {
|
||||
// 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
|
||||
// «promise, resolution, thenAction»).
|
||||
const nativeContext = LoadNativeContext(context);
|
||||
const task = AllocatePromiseResolveThenableJobTask(
|
||||
promise, UnsafeCast<JSReceiver>(then),
|
||||
UnsafeCast<JSReceiver>(resolution), nativeContext);
|
||||
return EnqueueMicrotask(nativeContext, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user