Commit 8d84d11d authored by Joshua Litt's avatar Joshua Litt Committed by Commit Bot

[promises] Port ResolvePromise to Torque.

Bug: v8:9838
Change-Id: I9cfa7af623af3b387962ea4fa90cfc599612f976
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1958961Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65465}
parent a50b1ea1
......@@ -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);
}
}
}
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