[promises] Port remaining promise code to Torque.
Bug: v8:9838 Change-Id: Idc6bda122354a54dd24e39b0356f35b0f54ef089 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2012596 Commit-Queue: Joshua Litt <joshualitt@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#66031}
This commit is contained in:
parent
01646bc89c
commit
f22c213304
1
BUILD.gn
1
BUILD.gn
@ -987,6 +987,7 @@ torque_files = [
|
|||||||
"src/builtins/promise-all-element-closure.tq",
|
"src/builtins/promise-all-element-closure.tq",
|
||||||
"src/builtins/promise-constructor.tq",
|
"src/builtins/promise-constructor.tq",
|
||||||
"src/builtins/promise-finally.tq",
|
"src/builtins/promise-finally.tq",
|
||||||
|
"src/builtins/promise-misc.tq",
|
||||||
"src/builtins/promise-race.tq",
|
"src/builtins/promise-race.tq",
|
||||||
"src/builtins/promise-reaction-job.tq",
|
"src/builtins/promise-reaction-job.tq",
|
||||||
"src/builtins/promise-resolve.tq",
|
"src/builtins/promise-resolve.tq",
|
||||||
|
@ -148,7 +148,7 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
|
|||||||
// presently executing, then this method will loop through, processing each
|
// presently executing, then this method will loop through, processing each
|
||||||
// request from front to back.
|
// request from front to back.
|
||||||
// This loop resides in AsyncGeneratorResumeNext.
|
// This loop resides in AsyncGeneratorResumeNext.
|
||||||
TNode<JSPromise> promise = AllocateAndInitJSPromise(context);
|
TNode<JSPromise> promise = NewJSPromise(context);
|
||||||
|
|
||||||
Label if_receiverisincompatible(this, Label::kDeferred);
|
Label if_receiverisincompatible(this, Label::kDeferred);
|
||||||
GotoIf(TaggedIsSmi(receiver), &if_receiverisincompatible);
|
GotoIf(TaggedIsSmi(receiver), &if_receiverisincompatible);
|
||||||
|
@ -105,7 +105,7 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
|
|||||||
const char* operation_name, Label::Type reject_label_type,
|
const char* operation_name, Label::Type reject_label_type,
|
||||||
base::Optional<TNode<Object>> initial_exception_value) {
|
base::Optional<TNode<Object>> initial_exception_value) {
|
||||||
const TNode<NativeContext> native_context = LoadNativeContext(context);
|
const TNode<NativeContext> native_context = LoadNativeContext(context);
|
||||||
const TNode<JSPromise> promise = AllocateAndInitJSPromise(context);
|
const TNode<JSPromise> promise = NewJSPromise(context);
|
||||||
|
|
||||||
TVARIABLE(
|
TVARIABLE(
|
||||||
Object, var_exception,
|
Object, var_exception,
|
||||||
|
@ -20,234 +20,22 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
using Node = compiler::Node;
|
void PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(
|
||||||
using IteratorRecord = TorqueStructIteratorRecord;
|
TNode<JSPromise> promise) {
|
||||||
using PromiseResolvingFunctions = TorqueStructPromiseResolvingFunctions;
|
|
||||||
|
|
||||||
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateJSPromise(
|
|
||||||
TNode<Context> context) {
|
|
||||||
const TNode<NativeContext> native_context = LoadNativeContext(context);
|
|
||||||
const TNode<JSFunction> promise_fun =
|
|
||||||
CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
|
|
||||||
CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
|
|
||||||
const TNode<Map> promise_map = LoadObjectField<Map>(
|
|
||||||
promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
|
|
||||||
const TNode<HeapObject> promise =
|
|
||||||
Allocate(JSPromise::kSizeWithEmbedderFields);
|
|
||||||
StoreMapNoWriteBarrier(promise, promise_map);
|
|
||||||
StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
|
|
||||||
RootIndex::kEmptyFixedArray);
|
|
||||||
StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
|
|
||||||
RootIndex::kEmptyFixedArray);
|
|
||||||
return CAST(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseBuiltinsAssembler::PromiseInit(TNode<JSPromise> promise) {
|
|
||||||
STATIC_ASSERT(v8::Promise::kPending == 0);
|
|
||||||
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset,
|
|
||||||
SmiConstant(Smi::zero()));
|
|
||||||
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
|
|
||||||
SmiConstant(Smi::zero()));
|
|
||||||
for (int offset = JSPromise::kHeaderSize;
|
for (int offset = JSPromise::kHeaderSize;
|
||||||
offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
|
offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
|
||||||
StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::zero()));
|
StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::zero()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndInitJSPromise(
|
TNode<HeapObject> PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
|
||||||
TNode<Context> context) {
|
TNode<Context> context) {
|
||||||
return AllocateAndInitJSPromise(context, UndefinedConstant());
|
return Allocate(PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndInitJSPromise(
|
TNode<HeapObject> PromiseBuiltinsAssembler::AllocateJSPromise(
|
||||||
TNode<Context> context, TNode<Object> parent) {
|
TNode<Context> context) {
|
||||||
const TNode<JSPromise> instance = AllocateJSPromise(context);
|
return Allocate(JSPromise::kSizeWithEmbedderFields);
|
||||||
PromiseInit(instance);
|
|
||||||
|
|
||||||
Label out(this);
|
|
||||||
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
|
|
||||||
CallRuntime(Runtime::kPromiseHookInit, context, instance, parent);
|
|
||||||
Goto(&out);
|
|
||||||
|
|
||||||
BIND(&out);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
|
|
||||||
TNode<Context> context, v8::Promise::PromiseState status,
|
|
||||||
TNode<Object> result) {
|
|
||||||
DCHECK_NE(Promise::kPending, status);
|
|
||||||
|
|
||||||
const TNode<JSPromise> instance = AllocateJSPromise(context);
|
|
||||||
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kReactionsOrResultOffset,
|
|
||||||
result);
|
|
||||||
STATIC_ASSERT(JSPromise::kStatusShift == 0);
|
|
||||||
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset,
|
|
||||||
SmiConstant(status));
|
|
||||||
for (int offset = JSPromise::kHeaderSize;
|
|
||||||
offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
|
|
||||||
StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Label out(this);
|
|
||||||
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
|
|
||||||
CallRuntime(Runtime::kPromiseHookInit, context, instance,
|
|
||||||
UndefinedConstant());
|
|
||||||
Goto(&out);
|
|
||||||
|
|
||||||
BIND(&out);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<BoolT> PromiseBuiltinsAssembler::PromiseHasHandler(
|
|
||||||
TNode<JSPromise> promise) {
|
|
||||||
const TNode<Smi> flags =
|
|
||||||
LoadObjectField<Smi>(promise, JSPromise::kFlagsOffset);
|
|
||||||
return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<PromiseReaction> PromiseBuiltinsAssembler::AllocatePromiseReaction(
|
|
||||||
TNode<Object> next, TNode<HeapObject> promise_or_capability,
|
|
||||||
TNode<HeapObject> fulfill_handler, TNode<HeapObject> reject_handler) {
|
|
||||||
const TNode<HeapObject> reaction = Allocate(PromiseReaction::kSize);
|
|
||||||
StoreMapNoWriteBarrier(reaction, RootIndex::kPromiseReactionMap);
|
|
||||||
StoreObjectFieldNoWriteBarrier(reaction, PromiseReaction::kNextOffset, next);
|
|
||||||
StoreObjectFieldNoWriteBarrier(reaction,
|
|
||||||
PromiseReaction::kPromiseOrCapabilityOffset,
|
|
||||||
promise_or_capability);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
reaction, PromiseReaction::kFulfillHandlerOffset, fulfill_handler);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
reaction, PromiseReaction::kRejectHandlerOffset, reject_handler);
|
|
||||||
return CAST(reaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<PromiseReactionJobTask>
|
|
||||||
PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
|
|
||||||
TNode<Map> map, TNode<Context> context, TNode<Object> argument,
|
|
||||||
TNode<HeapObject> handler, TNode<HeapObject> promise_or_capability) {
|
|
||||||
const TNode<HeapObject> microtask =
|
|
||||||
Allocate(PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks);
|
|
||||||
StoreMapNoWriteBarrier(microtask, map);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseReactionJobTask::kArgumentOffset, argument);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseReactionJobTask::kContextOffset, context);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseReactionJobTask::kHandlerOffset, handler);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset,
|
|
||||||
promise_or_capability);
|
|
||||||
return CAST(microtask);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<PromiseResolveThenableJobTask>
|
|
||||||
PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
|
|
||||||
TNode<JSPromise> promise_to_resolve, TNode<JSReceiver> then,
|
|
||||||
TNode<JSReceiver> thenable, TNode<Context> context) {
|
|
||||||
const TNode<HeapObject> microtask =
|
|
||||||
Allocate(PromiseResolveThenableJobTask::kSize);
|
|
||||||
StoreMapNoWriteBarrier(microtask,
|
|
||||||
RootIndex::kPromiseResolveThenableJobTaskMap);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseResolveThenableJobTask::kContextOffset, context);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset,
|
|
||||||
promise_to_resolve);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseResolveThenableJobTask::kThenOffset, then);
|
|
||||||
StoreObjectFieldNoWriteBarrier(
|
|
||||||
microtask, PromiseResolveThenableJobTask::kThenableOffset, thenable);
|
|
||||||
return CAST(microtask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact(
|
|
||||||
TNode<NativeContext> native_context, TNode<Object> constructor,
|
|
||||||
Label* if_fast, Label* if_slow) {
|
|
||||||
GotoIfForceSlowPath(if_slow);
|
|
||||||
TNode<Object> promise_fun =
|
|
||||||
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
|
|
||||||
GotoIfNot(TaggedEqual(promise_fun, constructor), if_slow);
|
|
||||||
Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseBuiltinsAssembler::GotoIfNotPromiseResolveLookupChainIntact(
|
|
||||||
TNode<NativeContext> native_context, TNode<Object> constructor,
|
|
||||||
Label* if_slow) {
|
|
||||||
Label if_fast(this);
|
|
||||||
BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast,
|
|
||||||
if_slow);
|
|
||||||
BIND(&if_fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
|
|
||||||
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);
|
|
||||||
GotoIfNot(TaggedEqual(LoadMapPrototype(promise_map), promise_prototype),
|
|
||||||
if_slow);
|
|
||||||
Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
|
|
||||||
TNode<NativeContext> native_context, TNode<Map> receiver_map,
|
|
||||||
Label* if_fast, Label* if_slow) {
|
|
||||||
GotoIfForceSlowPath(if_slow);
|
|
||||||
GotoIfNot(IsJSPromiseMap(receiver_map), if_slow);
|
|
||||||
const TNode<Object> promise_prototype =
|
|
||||||
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
|
|
||||||
GotoIfNot(TaggedEqual(LoadMapPrototype(receiver_map), promise_prototype),
|
|
||||||
if_slow);
|
|
||||||
Branch(IsPromiseThenProtectorCellInvalid(), if_slow, if_fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
|
|
||||||
TNode<Context> context, TNode<Context> native_context,
|
|
||||||
TNode<Object> promise_constructor, TNode<Object> executor,
|
|
||||||
Label* if_noaccess) {
|
|
||||||
TVARIABLE(HeapObject, var_executor);
|
|
||||||
var_executor = CAST(executor);
|
|
||||||
Label has_access(this), call_runtime(this, Label::kDeferred);
|
|
||||||
|
|
||||||
// If executor is a bound function, load the bound function until we've
|
|
||||||
// reached an actual function.
|
|
||||||
Label found_function(this), loop_over_bound_function(this, &var_executor);
|
|
||||||
Goto(&loop_over_bound_function);
|
|
||||||
BIND(&loop_over_bound_function);
|
|
||||||
{
|
|
||||||
TNode<Uint16T> executor_type = LoadInstanceType(var_executor.value());
|
|
||||||
GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function);
|
|
||||||
GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE),
|
|
||||||
&call_runtime);
|
|
||||||
var_executor = LoadObjectField<HeapObject>(
|
|
||||||
var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset);
|
|
||||||
Goto(&loop_over_bound_function);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the context from the function and compare it to the Promise
|
|
||||||
// constructor's context. If they match, everything is fine, otherwise, bail
|
|
||||||
// out to the runtime.
|
|
||||||
BIND(&found_function);
|
|
||||||
{
|
|
||||||
TNode<Context> function_context = LoadObjectField<Context>(
|
|
||||||
var_executor.value(), JSFunction::kContextOffset);
|
|
||||||
TNode<NativeContext> native_function_context =
|
|
||||||
LoadNativeContext(function_context);
|
|
||||||
Branch(TaggedEqual(native_context, native_function_context), &has_access,
|
|
||||||
&call_runtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&call_runtime);
|
|
||||||
{
|
|
||||||
Branch(TaggedEqual(CallRuntime(Runtime::kAllowDynamicFunction, context,
|
|
||||||
promise_constructor),
|
|
||||||
TrueConstant()),
|
|
||||||
&has_access, if_noaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&has_access);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -17,112 +17,11 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
public:
|
public:
|
||||||
explicit PromiseBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
explicit PromiseBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||||
: CodeStubAssembler(state) {}
|
: CodeStubAssembler(state) {}
|
||||||
// These allocate and initialize a promise with pending state and
|
void ZeroOutEmbedderOffsets(TNode<JSPromise> promise);
|
||||||
// undefined fields.
|
|
||||||
//
|
|
||||||
// This uses undefined as the parent promise for the promise init
|
|
||||||
// hook.
|
|
||||||
TNode<JSPromise> AllocateAndInitJSPromise(TNode<Context> context);
|
|
||||||
// This uses the given parent as the parent promise for the promise
|
|
||||||
// init hook.
|
|
||||||
TNode<JSPromise> AllocateAndInitJSPromise(TNode<Context> context,
|
|
||||||
TNode<Object> parent);
|
|
||||||
|
|
||||||
// This allocates and initializes a promise with the given state and
|
TNode<HeapObject> AllocateJSPromise(TNode<Context> context);
|
||||||
// fields.
|
|
||||||
TNode<JSPromise> AllocateAndSetJSPromise(TNode<Context> context,
|
|
||||||
v8::Promise::PromiseState status,
|
|
||||||
TNode<Object> result);
|
|
||||||
|
|
||||||
TNode<PromiseReaction> AllocatePromiseReaction(
|
TNode<HeapObject> AllocatePromiseReactionJobTask(TNode<Context> context);
|
||||||
TNode<Object> next, TNode<HeapObject> promise_or_capability,
|
|
||||||
TNode<HeapObject> fulfill_handler, TNode<HeapObject> reject_handler);
|
|
||||||
|
|
||||||
TNode<PromiseReactionJobTask> AllocatePromiseReactionJobTask(
|
|
||||||
TNode<Map> map, TNode<Context> context, TNode<Object> argument,
|
|
||||||
TNode<HeapObject> handler, TNode<HeapObject> promise_or_capability);
|
|
||||||
|
|
||||||
TNode<PromiseResolveThenableJobTask> AllocatePromiseResolveThenableJobTask(
|
|
||||||
TNode<JSPromise> promise_to_resolve, TNode<JSReceiver> then,
|
|
||||||
TNode<JSReceiver> thenable, TNode<Context> context);
|
|
||||||
TNode<BoolT> PromiseHasHandler(TNode<JSPromise> promise);
|
|
||||||
|
|
||||||
void BranchIfAccessCheckFailed(TNode<Context> context,
|
|
||||||
TNode<Context> native_context,
|
|
||||||
TNode<Object> promise_constructor,
|
|
||||||
TNode<Object> executor, Label* if_noaccess);
|
|
||||||
void PromiseInit(TNode<JSPromise> 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);
|
|
||||||
|
|
||||||
template <typename... TArgs>
|
|
||||||
TNode<Object> InvokeThen(TNode<NativeContext> native_context,
|
|
||||||
TNode<Object> receiver, TArgs... args) {
|
|
||||||
TVARIABLE(Object, var_result);
|
|
||||||
Label if_fast(this), if_slow(this, Label::kDeferred),
|
|
||||||
done(this, &var_result);
|
|
||||||
GotoIf(TaggedIsSmi(receiver), &if_slow);
|
|
||||||
const TNode<Map> receiver_map = LoadMap(CAST(receiver));
|
|
||||||
// We can skip the "then" lookup on {receiver} 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
|
|
||||||
// on JSPromise instances which have the (initial) %PromisePrototype%.
|
|
||||||
BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast,
|
|
||||||
&if_slow);
|
|
||||||
|
|
||||||
BIND(&if_fast);
|
|
||||||
{
|
|
||||||
const TNode<Object> then =
|
|
||||||
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
|
|
||||||
var_result =
|
|
||||||
CallJS(CodeFactory::CallFunction(
|
|
||||||
isolate(), ConvertReceiverMode::kNotNullOrUndefined),
|
|
||||||
native_context, then, receiver, args...);
|
|
||||||
Goto(&done);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&if_slow);
|
|
||||||
{
|
|
||||||
const TNode<Object> then = GetProperty(
|
|
||||||
native_context, receiver, isolate()->factory()->then_string());
|
|
||||||
var_result =
|
|
||||||
CallJS(CodeFactory::Call(isolate(),
|
|
||||||
ConvertReceiverMode::kNotNullOrUndefined),
|
|
||||||
native_context, then, receiver, args...);
|
|
||||||
Goto(&done);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&done);
|
|
||||||
return var_result.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// We can skip the "resolve" lookup on {constructor} if it's the (initial)
|
|
||||||
// Promise constructor and the Promise.resolve() protector is intact, as
|
|
||||||
// that guards the lookup path for the "resolve" property on the %Promise%
|
|
||||||
// intrinsic object.
|
|
||||||
void BranchIfPromiseResolveLookupChainIntact(
|
|
||||||
TNode<NativeContext> native_context, TNode<Object> constructor,
|
|
||||||
Label* if_fast, Label* if_slow);
|
|
||||||
void GotoIfNotPromiseResolveLookupChainIntact(
|
|
||||||
TNode<NativeContext> native_context, TNode<Object> constructor,
|
|
||||||
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
|
|
||||||
// on JSPromise instances which have the (initial) %PromisePrototype%.
|
|
||||||
void BranchIfPromiseThenLookupChainIntact(TNode<NativeContext> native_context,
|
|
||||||
TNode<Map> receiver_map,
|
|
||||||
Label* if_fast, Label* if_slow);
|
|
||||||
|
|
||||||
TNode<JSPromise> AllocateJSPromise(TNode<Context> context);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -36,6 +36,7 @@ extern macro IsExtensibleMap(Map): bool;
|
|||||||
extern macro IsJSPrimitiveWrapper(HeapObject): bool;
|
extern macro IsJSPrimitiveWrapper(HeapObject): bool;
|
||||||
extern macro IsPromiseCapability(HeapObject): bool;
|
extern macro IsPromiseCapability(HeapObject): bool;
|
||||||
extern macro IsPromiseReaction(HeapObject): bool;
|
extern macro IsPromiseReaction(HeapObject): bool;
|
||||||
|
extern macro IsPromiseReactionJobTask(HeapObject): bool;
|
||||||
extern macro IsPromiseRejectReactionJobTask(HeapObject): bool;
|
extern macro IsPromiseRejectReactionJobTask(HeapObject): bool;
|
||||||
extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool;
|
extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool;
|
||||||
extern macro IsSharedFunctionInfo(HeapObject): bool;
|
extern macro IsSharedFunctionInfo(HeapObject): bool;
|
||||||
@ -643,6 +644,14 @@ Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cast<PromiseReactionJobTask>(o: HeapObject):
|
||||||
|
PromiseReactionJobTask labels CastError {
|
||||||
|
if (IsPromiseReactionJobTask(o)) {
|
||||||
|
return %RawDownCast<PromiseReactionJobTask>(o);
|
||||||
|
}
|
||||||
|
goto CastError;
|
||||||
|
}
|
||||||
|
|
||||||
Cast<PromiseFulfillReactionJobTask>(o: HeapObject):
|
Cast<PromiseFulfillReactionJobTask>(o: HeapObject):
|
||||||
PromiseFulfillReactionJobTask labels CastError {
|
PromiseFulfillReactionJobTask labels CastError {
|
||||||
if (IsPromiseFulfillReactionJobTask(o)) {
|
if (IsPromiseFulfillReactionJobTask(o)) {
|
||||||
|
@ -246,9 +246,6 @@ namespace promise {
|
|||||||
const kPromiseBuiltinsDebugEventSlot: constexpr ContextSlot
|
const kPromiseBuiltinsDebugEventSlot: constexpr ContextSlot
|
||||||
generates 'PromiseBuiltins::kDebugEventSlot';
|
generates 'PromiseBuiltins::kDebugEventSlot';
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Context): JSPromise;
|
|
||||||
|
|
||||||
@export
|
@export
|
||||||
macro CreatePromiseCapabilitiesExecutorContext(
|
macro CreatePromiseCapabilitiesExecutorContext(
|
||||||
nativeContext: NativeContext, capability: PromiseCapability): Context {
|
nativeContext: NativeContext, capability: PromiseCapability): Context {
|
||||||
@ -306,7 +303,7 @@ namespace promise {
|
|||||||
if (TaggedEqual(
|
if (TaggedEqual(
|
||||||
constructor,
|
constructor,
|
||||||
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX])) {
|
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX])) {
|
||||||
const promise = AllocateAndInitJSPromise(nativeContext);
|
const promise = NewJSPromise();
|
||||||
|
|
||||||
const pair =
|
const pair =
|
||||||
CreatePromiseResolvingFunctions(promise, debugEvent, nativeContext);
|
CreatePromiseResolvingFunctions(promise, debugEvent, nativeContext);
|
||||||
@ -409,14 +406,6 @@ namespace promise {
|
|||||||
return ResolvePromise(context, promise, resolution);
|
return ResolvePromise(context, promise, resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::AllocatePromiseReaction(
|
|
||||||
Object, HeapObject, HeapObject, HeapObject): PromiseReaction;
|
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
|
|
||||||
Map, Context, Object, HeapObject, HeapObject): PromiseReactionJobTask;
|
|
||||||
|
|
||||||
@export
|
@export
|
||||||
transitioning macro PerformPromiseThenImpl(implicit context: Context)(
|
transitioning macro PerformPromiseThenImpl(implicit context: Context)(
|
||||||
promise: JSPromise, onFulfilled: Callable|Undefined,
|
promise: JSPromise, onFulfilled: Callable|Undefined,
|
||||||
@ -427,13 +416,14 @@ namespace promise {
|
|||||||
// PromiseReaction holding both the onFulfilled and onRejected callbacks.
|
// PromiseReaction holding both the onFulfilled and onRejected callbacks.
|
||||||
// Once the {promise} is resolved we decide on the concrete handler to
|
// Once the {promise} is resolved we decide on the concrete handler to
|
||||||
// push onto the microtask queue.
|
// push onto the microtask queue.
|
||||||
const promiseReactions = promise.reactions_or_result;
|
const promiseReactions =
|
||||||
const reaction = AllocatePromiseReaction(
|
UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);
|
||||||
|
const reaction = NewPromiseReaction(
|
||||||
promiseReactions, resultPromiseOrCapability, onFulfilled, onRejected);
|
promiseReactions, resultPromiseOrCapability, onFulfilled, onRejected);
|
||||||
promise.reactions_or_result = reaction;
|
promise.reactions_or_result = reaction;
|
||||||
} else {
|
} else {
|
||||||
let map: Map;
|
let map: Map;
|
||||||
let handler: HeapObject;
|
let handler: Callable|Undefined = Undefined;
|
||||||
let handlerContext: Context;
|
let handlerContext: Context;
|
||||||
if (promise.Status() == PromiseState::kFulfilled) {
|
if (promise.Status() == PromiseState::kFulfilled) {
|
||||||
map = PromiseFulfillReactionJobTaskMapConstant();
|
map = PromiseFulfillReactionJobTaskMapConstant();
|
||||||
@ -451,7 +441,7 @@ namespace promise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reactionsOrResult = promise.reactions_or_result;
|
const reactionsOrResult = promise.reactions_or_result;
|
||||||
const microtask = AllocatePromiseReactionJobTask(
|
const microtask = NewPromiseReactionJobTask(
|
||||||
map, handlerContext, reactionsOrResult, handler,
|
map, handlerContext, reactionsOrResult, handler,
|
||||||
resultPromiseOrCapability);
|
resultPromiseOrCapability);
|
||||||
EnqueueMicrotask(handlerContext, microtask);
|
EnqueueMicrotask(handlerContext, microtask);
|
||||||
@ -469,10 +459,6 @@ namespace promise {
|
|||||||
return resultPromise;
|
return resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
|
|
||||||
Context, constexpr PromiseState, JSAny): JSPromise;
|
|
||||||
|
|
||||||
// https://tc39.es/ecma262/#sec-promise-reject-functions
|
// https://tc39.es/ecma262/#sec-promise-reject-functions
|
||||||
transitioning javascript builtin
|
transitioning javascript builtin
|
||||||
PromiseReject(js-implicit context: NativeContext, receiver: JSAny)(
|
PromiseReject(js-implicit context: NativeContext, receiver: JSAny)(
|
||||||
@ -484,8 +470,7 @@ namespace promise {
|
|||||||
|
|
||||||
const promiseFun = context[NativeContextSlot::PROMISE_FUNCTION_INDEX];
|
const promiseFun = context[NativeContextSlot::PROMISE_FUNCTION_INDEX];
|
||||||
if (promiseFun == receiver) {
|
if (promiseFun == receiver) {
|
||||||
const promise =
|
const promise = NewJSPromise(PromiseState::kRejected, reason);
|
||||||
AllocateAndSetJSPromise(context, PromiseState::kRejected, reason);
|
|
||||||
runtime::PromiseRejectEventFromStack(promise, reason);
|
runtime::PromiseRejectEventFromStack(promise, reason);
|
||||||
return promise;
|
return promise;
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,18 +25,13 @@ namespace promise {
|
|||||||
const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature
|
const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature
|
||||||
generates 'v8::Isolate::kPromiseConstructorReturnedUndefined';
|
generates 'v8::Isolate::kPromiseConstructorReturnedUndefined';
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
|
|
||||||
Context, Context, Object, Object): void labels NoAccess;
|
|
||||||
|
|
||||||
extern macro
|
extern macro
|
||||||
IsDebugActive(): bool;
|
IsDebugActive(): bool;
|
||||||
|
|
||||||
transitioning macro
|
transitioning macro
|
||||||
HasAccessCheckFailed(
|
HasAccessCheckFailed(implicit context: Context)(
|
||||||
context: Context, nativeContext: Context, promiseFun: Object,
|
nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool {
|
||||||
executor: Object): bool {
|
BranchIfAccessCheckFailed(nativeContext, promiseFun, executor)
|
||||||
BranchIfAccessCheckFailed(context, nativeContext, promiseFun, executor)
|
|
||||||
otherwise return true;
|
otherwise return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -44,8 +39,6 @@ namespace promise {
|
|||||||
extern macro ConstructorBuiltinsAssembler::EmitFastNewObject(
|
extern macro ConstructorBuiltinsAssembler::EmitFastNewObject(
|
||||||
Context, JSFunction, JSReceiver): JSObject;
|
Context, JSFunction, JSReceiver): JSObject;
|
||||||
|
|
||||||
extern macro PromiseBuiltinsAssembler::PromiseInit(JSPromise): void;
|
|
||||||
|
|
||||||
extern macro
|
extern macro
|
||||||
PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool;
|
PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool;
|
||||||
|
|
||||||
@ -68,7 +61,7 @@ namespace promise {
|
|||||||
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
|
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
|
||||||
|
|
||||||
// Silently fail if the stack looks fishy.
|
// Silently fail if the stack looks fishy.
|
||||||
if (HasAccessCheckFailed(context, context, promiseFun, executor)) {
|
if (HasAccessCheckFailed(context, promiseFun, executor)) {
|
||||||
IncrementUseCounter(
|
IncrementUseCounter(
|
||||||
context, SmiConstant(kPromiseConstructorReturnedUndefined));
|
context, SmiConstant(kPromiseConstructorReturnedUndefined));
|
||||||
return Undefined;
|
return Undefined;
|
||||||
@ -76,7 +69,7 @@ namespace promise {
|
|||||||
|
|
||||||
let result: JSPromise;
|
let result: JSPromise;
|
||||||
if (promiseFun == newTarget) {
|
if (promiseFun == newTarget) {
|
||||||
result = AllocateAndInitJSPromise(context);
|
result = NewJSPromise();
|
||||||
} else {
|
} else {
|
||||||
result = UnsafeCast<JSPromise>(EmitFastNewObject(
|
result = UnsafeCast<JSPromise>(EmitFastNewObject(
|
||||||
context, promiseFun, UnsafeCast<JSReceiver>(newTarget)));
|
context, promiseFun, UnsafeCast<JSReceiver>(newTarget)));
|
||||||
|
@ -48,11 +48,6 @@ namespace promise {
|
|||||||
return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext);
|
return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern transitioning macro
|
|
||||||
PromiseBuiltinsAssembler::InvokeThen(NativeContext, JSAny, JSAny): JSAny;
|
|
||||||
extern transitioning macro PromiseBuiltinsAssembler::InvokeThen(
|
|
||||||
NativeContext, JSAny, JSAny, JSAny): JSAny;
|
|
||||||
|
|
||||||
transitioning javascript builtin
|
transitioning javascript builtin
|
||||||
PromiseCatchFinally(js-implicit context: Context, receiver: JSAny)(
|
PromiseCatchFinally(js-implicit context: Context, receiver: JSAny)(
|
||||||
reason: JSAny): JSAny {
|
reason: JSAny): JSAny {
|
||||||
|
236
src/builtins/promise-misc.tq
Normal file
236
src/builtins/promise-misc.tq
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
// 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.h'
|
||||||
|
#include 'src/builtins/builtins-promise-gen.h'
|
||||||
|
|
||||||
|
namespace runtime {
|
||||||
|
extern transitioning runtime
|
||||||
|
AllowDynamicFunction(implicit context: Context)(JSAny): JSAny;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe functions that should be used very carefully.
|
||||||
|
namespace promise_internal {
|
||||||
|
extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise):
|
||||||
|
void;
|
||||||
|
|
||||||
|
extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject;
|
||||||
|
|
||||||
|
extern macro PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
|
||||||
|
Context): HeapObject;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace promise {
|
||||||
|
extern macro IsFunctionWithPrototypeSlotMap(Map): bool;
|
||||||
|
|
||||||
|
@export
|
||||||
|
macro PromiseHasHandler(promise: JSPromise): bool {
|
||||||
|
return promise.HasHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@export
|
||||||
|
macro PromiseInit(promise: JSPromise): void {
|
||||||
|
assert(PromiseState::kPending == 0);
|
||||||
|
promise.reactions_or_result = kZero;
|
||||||
|
promise.flags = 0;
|
||||||
|
promise_internal::ZeroOutEmbedderOffsets(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro InnerNewJSPromise(implicit context: Context)(): JSPromise {
|
||||||
|
const nativeContext = LoadNativeContext(context);
|
||||||
|
const promiseFun = UnsafeCast<JSFunction>(
|
||||||
|
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
|
||||||
|
assert(IsFunctionWithPrototypeSlotMap(promiseFun.map));
|
||||||
|
const promiseMap = UnsafeCast<Map>(promiseFun.prototype_or_initial_map);
|
||||||
|
const promiseHeapObject = promise_internal::AllocateJSPromise(context);
|
||||||
|
promiseHeapObject.map = promiseMap;
|
||||||
|
const promise = UnsafeCast<JSPromise>(promiseHeapObject);
|
||||||
|
promise.properties_or_hash = kEmptyFixedArray;
|
||||||
|
promise.elements = kEmptyFixedArray;
|
||||||
|
promise.reactions_or_result = kZero;
|
||||||
|
promise.flags = 0;
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro NewPromiseReactionJobTask(implicit context: Context)(
|
||||||
|
map: Map, handlerContext: Context, argument: Object,
|
||||||
|
handler: Callable|Undefined,
|
||||||
|
promiseOrCapability: JSPromise|PromiseCapability|
|
||||||
|
Undefined): PromiseReactionJobTask {
|
||||||
|
const taskHeapObject =
|
||||||
|
promise_internal::AllocatePromiseReactionJobTask(context);
|
||||||
|
taskHeapObject.map = map;
|
||||||
|
const jobTask = UnsafeCast<PromiseReactionJobTask>(taskHeapObject);
|
||||||
|
jobTask.argument = argument;
|
||||||
|
jobTask.context = handlerContext;
|
||||||
|
jobTask.handler = handler;
|
||||||
|
jobTask.promise_or_capability = promiseOrCapability;
|
||||||
|
return jobTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These allocate and initialize a promise with pending state and
|
||||||
|
// undefined fields.
|
||||||
|
//
|
||||||
|
// This uses the given parent as the parent promise for the promise
|
||||||
|
// init hook.
|
||||||
|
@export
|
||||||
|
transitioning macro NewJSPromise(implicit context: Context)(parent: Object):
|
||||||
|
JSPromise {
|
||||||
|
const instance = InnerNewJSPromise();
|
||||||
|
PromiseInit(instance);
|
||||||
|
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
|
||||||
|
runtime::PromiseHookInit(instance, parent);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This uses undefined as the parent promise for the promise init
|
||||||
|
// hook.
|
||||||
|
@export
|
||||||
|
transitioning macro NewJSPromise(implicit context: Context)(): JSPromise {
|
||||||
|
return NewJSPromise(Undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This allocates and initializes a promise with the given state and
|
||||||
|
// fields.
|
||||||
|
@export
|
||||||
|
transitioning macro NewJSPromise(implicit context: Context)(
|
||||||
|
status: constexpr PromiseState, result: JSAny): JSPromise {
|
||||||
|
assert(status != PromiseState::kPending);
|
||||||
|
assert(kJSPromiseStatusShift == 0);
|
||||||
|
|
||||||
|
const instance = InnerNewJSPromise();
|
||||||
|
instance.reactions_or_result = result;
|
||||||
|
instance.SetStatus(status);
|
||||||
|
promise_internal::ZeroOutEmbedderOffsets(instance);
|
||||||
|
|
||||||
|
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
|
||||||
|
runtime::PromiseHookInit(instance, Undefined);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro NewPromiseReaction(implicit context: Context)(
|
||||||
|
next: Zero|PromiseReaction,
|
||||||
|
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
|
||||||
|
fulfillHandler: Callable|Undefined,
|
||||||
|
rejectHandler: Callable|Undefined): PromiseReaction {
|
||||||
|
return new PromiseReaction{
|
||||||
|
map: PromiseReactionMapConstant(),
|
||||||
|
next: next,
|
||||||
|
reject_handler: rejectHandler,
|
||||||
|
fulfill_handler: fulfillHandler,
|
||||||
|
promise_or_capability: promiseOrCapability
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extern macro PromiseResolveThenableJobTaskMapConstant(): Map;
|
||||||
|
|
||||||
|
macro NewPromiseResolveThenableJobTask(implicit context: Context)(
|
||||||
|
promiseToResolve: JSPromise, then: JSReceiver, thenable: JSReceiver,
|
||||||
|
thenableContext: Context): PromiseResolveThenableJobTask {
|
||||||
|
return new PromiseResolveThenableJobTask{
|
||||||
|
map: PromiseResolveThenableJobTaskMapConstant(),
|
||||||
|
context: thenableContext,
|
||||||
|
promise_to_resolve: promiseToResolve,
|
||||||
|
then: then,
|
||||||
|
thenable: thenable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InvokeThenOneArgFunctor {
|
||||||
|
transitioning
|
||||||
|
macro Call(
|
||||||
|
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
|
||||||
|
_arg2: JSAny): JSAny {
|
||||||
|
return Call(nativeContext, then, receiver, arg1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InvokeThenTwoArgFunctor {
|
||||||
|
transitioning
|
||||||
|
macro Call(
|
||||||
|
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
|
||||||
|
arg2: JSAny): JSAny {
|
||||||
|
return Call(nativeContext, then, receiver, arg1, arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitioning
|
||||||
|
macro InvokeThen<F: type>(implicit context: Context)(
|
||||||
|
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny,
|
||||||
|
callFunctor: F): JSAny {
|
||||||
|
// We can skip the "then" lookup on {receiver} 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
|
||||||
|
// on JSPromise instances which have the (initial) %PromisePrototype%.
|
||||||
|
if (!Is<Smi>(receiver) &&
|
||||||
|
IsPromiseThenLookupChainIntact(
|
||||||
|
nativeContext, UnsafeCast<HeapObject>(receiver).map)) {
|
||||||
|
const then = UnsafeCast<JSAny>(
|
||||||
|
nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]);
|
||||||
|
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
|
||||||
|
} else
|
||||||
|
deferred {
|
||||||
|
const then = UnsafeCast<JSAny>(GetProperty(receiver, kThenString));
|
||||||
|
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitioning
|
||||||
|
macro InvokeThen(implicit context: Context)(
|
||||||
|
nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny {
|
||||||
|
return InvokeThen(
|
||||||
|
nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{});
|
||||||
|
}
|
||||||
|
|
||||||
|
transitioning
|
||||||
|
macro InvokeThen(implicit context: Context)(
|
||||||
|
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny,
|
||||||
|
arg2: JSAny): JSAny {
|
||||||
|
return InvokeThen(
|
||||||
|
nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{});
|
||||||
|
}
|
||||||
|
|
||||||
|
transitioning
|
||||||
|
macro BranchIfAccessCheckFailed(implicit context: Context)(
|
||||||
|
nativeContext: NativeContext, promiseConstructor: JSAny,
|
||||||
|
executor: JSAny): void labels IfNoAccess {
|
||||||
|
try {
|
||||||
|
// If executor is a bound function, load the bound function until we've
|
||||||
|
// reached an actual function.
|
||||||
|
let foundExecutor = executor;
|
||||||
|
while (true) {
|
||||||
|
typeswitch (foundExecutor) {
|
||||||
|
case (f: JSFunction): {
|
||||||
|
// Load the context from the function and compare it to the Promise
|
||||||
|
// constructor's context. If they match, everything is fine,
|
||||||
|
// otherwise, bail out to the runtime.
|
||||||
|
const functionContext = f.context;
|
||||||
|
const nativeFunctionContext = LoadNativeContext(functionContext);
|
||||||
|
if (TaggedEqual(nativeContext, nativeFunctionContext)) {
|
||||||
|
goto HasAccess;
|
||||||
|
} else {
|
||||||
|
goto CallRuntime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case (b: JSBoundFunction): {
|
||||||
|
foundExecutor = b.bound_target_function;
|
||||||
|
}
|
||||||
|
case (Object): {
|
||||||
|
goto CallRuntime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label CallRuntime deferred {
|
||||||
|
const result = runtime::AllowDynamicFunction(promiseConstructor);
|
||||||
|
if (result != True) {
|
||||||
|
goto IfNoAccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label HasAccess {}
|
||||||
|
}
|
||||||
|
}
|
@ -64,7 +64,7 @@ namespace promise {
|
|||||||
if (promiseFun == constructor) {
|
if (promiseFun == constructor) {
|
||||||
// This adds a fast path for native promises that don't need to
|
// This adds a fast path for native promises that don't need to
|
||||||
// create NewPromiseCapability.
|
// create NewPromiseCapability.
|
||||||
const result = AllocateAndInitJSPromise(context);
|
const result = NewJSPromise();
|
||||||
ResolvePromise(context, result, value);
|
ResolvePromise(context, result, value);
|
||||||
return result;
|
return result;
|
||||||
} else
|
} else
|
||||||
@ -85,10 +85,6 @@ namespace promise {
|
|||||||
|
|
||||||
const kThenString: String = ThenStringConstant();
|
const kThenString: String = ThenStringConstant();
|
||||||
|
|
||||||
extern macro PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
|
|
||||||
JSPromise, JSReceiver, JSReceiver,
|
|
||||||
Context): PromiseResolveThenableJobTask;
|
|
||||||
|
|
||||||
transitioning builtin
|
transitioning builtin
|
||||||
ResolvePromise(implicit context:
|
ResolvePromise(implicit context:
|
||||||
Context)(promise: JSPromise, resolution: JSAny): JSAny {
|
Context)(promise: JSPromise, resolution: JSAny): JSAny {
|
||||||
@ -182,7 +178,7 @@ namespace promise {
|
|||||||
// 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
|
// 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
|
||||||
// «promise, resolution, thenAction»).
|
// «promise, resolution, thenAction»).
|
||||||
const nativeContext = LoadNativeContext(context);
|
const nativeContext = LoadNativeContext(context);
|
||||||
const task = AllocatePromiseResolveThenableJobTask(
|
const task = NewPromiseResolveThenableJobTask(
|
||||||
promise, UnsafeCast<JSReceiver>(then),
|
promise, UnsafeCast<JSReceiver>(then),
|
||||||
UnsafeCast<JSReceiver>(resolution), nativeContext);
|
UnsafeCast<JSReceiver>(resolution), nativeContext);
|
||||||
return EnqueueMicrotask(nativeContext, task);
|
return EnqueueMicrotask(nativeContext, task);
|
||||||
|
@ -6,22 +6,16 @@
|
|||||||
|
|
||||||
namespace promise {
|
namespace promise {
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
|
|
||||||
NativeContext, Map): never labels IsIntact,
|
|
||||||
IsNotIntact;
|
|
||||||
|
|
||||||
macro
|
macro
|
||||||
IsPromiseSpeciesLookupChainIntact(
|
IsPromiseSpeciesLookupChainIntact(
|
||||||
nativeContext: NativeContext, promiseMap: Map): bool {
|
nativeContext: NativeContext, promiseMap: Map): bool {
|
||||||
BranchIfPromiseSpeciesLookupChainIntact(nativeContext, promiseMap)
|
const promisePrototype =
|
||||||
otherwise return true, return false;
|
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
|
||||||
|
if (IsForceSlowPath()) return false;
|
||||||
|
if (promiseMap.prototype != promisePrototype) return false;
|
||||||
|
return !IsPromiseSpeciesProtectorCellInvalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern macro
|
|
||||||
PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Context, Object):
|
|
||||||
JSPromise;
|
|
||||||
|
|
||||||
// https://tc39.es/ecma262/#sec-promise.prototype.then
|
// https://tc39.es/ecma262/#sec-promise.prototype.then
|
||||||
transitioning javascript builtin
|
transitioning javascript builtin
|
||||||
PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
|
PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
|
||||||
@ -54,7 +48,7 @@ namespace promise {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
label AllocateAndInit {
|
label AllocateAndInit {
|
||||||
const resultJSPromise = AllocateAndInitJSPromise(context, promise);
|
const resultJSPromise = NewJSPromise(promise);
|
||||||
resultPromiseOrCapability = resultJSPromise;
|
resultPromiseOrCapability = resultJSPromise;
|
||||||
resultPromise = resultJSPromise;
|
resultPromise = resultJSPromise;
|
||||||
}
|
}
|
||||||
|
@ -6010,6 +6010,13 @@ TNode<BoolT> CodeStubAssembler::IsPromiseReaction(
|
|||||||
return HasInstanceType(object, PROMISE_REACTION_TYPE);
|
return HasInstanceType(object, PROMISE_REACTION_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TNode<BoolT> CodeStubAssembler::IsPromiseReactionJobTask(
|
||||||
|
TNode<HeapObject> object) {
|
||||||
|
TNode<Uint16T> instance_type = LoadInstanceType(object);
|
||||||
|
return IsInRange(instance_type, FIRST_PROMISE_REACTION_JOB_TASK_TYPE,
|
||||||
|
LAST_PROMISE_REACTION_JOB_TASK_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
TNode<BoolT> CodeStubAssembler::IsPromiseRejectReactionJobTask(
|
TNode<BoolT> CodeStubAssembler::IsPromiseRejectReactionJobTask(
|
||||||
SloppyTNode<HeapObject> object) {
|
SloppyTNode<HeapObject> object) {
|
||||||
return HasInstanceType(object, PROMISE_REJECT_REACTION_JOB_TASK_TYPE);
|
return HasInstanceType(object, PROMISE_REJECT_REACTION_JOB_TASK_TYPE);
|
||||||
|
@ -129,6 +129,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
|
|||||||
V(PromiseReactionMap, promise_reaction_map, PromiseReactionMap) \
|
V(PromiseReactionMap, promise_reaction_map, PromiseReactionMap) \
|
||||||
V(PromiseRejectReactionJobTaskMap, promise_reject_reaction_job_task_map, \
|
V(PromiseRejectReactionJobTaskMap, promise_reject_reaction_job_task_map, \
|
||||||
PromiseRejectReactionJobTaskMap) \
|
PromiseRejectReactionJobTaskMap) \
|
||||||
|
V(PromiseResolveThenableJobTaskMap, promise_resolve_thenable_job_task_map, \
|
||||||
|
PromiseResolveThenableJobTaskMap) \
|
||||||
V(prototype_string, prototype_string, PrototypeString) \
|
V(prototype_string, prototype_string, PrototypeString) \
|
||||||
V(PrototypeInfoMap, prototype_info_map, PrototypeInfoMap) \
|
V(PrototypeInfoMap, prototype_info_map, PrototypeInfoMap) \
|
||||||
V(replace_symbol, replace_symbol, ReplaceSymbol) \
|
V(replace_symbol, replace_symbol, ReplaceSymbol) \
|
||||||
@ -2548,6 +2550,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
|||||||
TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object);
|
TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object);
|
||||||
TNode<BoolT> IsPropertyCell(SloppyTNode<HeapObject> object);
|
TNode<BoolT> IsPropertyCell(SloppyTNode<HeapObject> object);
|
||||||
TNode<BoolT> IsPromiseReaction(SloppyTNode<HeapObject> object);
|
TNode<BoolT> IsPromiseReaction(SloppyTNode<HeapObject> object);
|
||||||
|
TNode<BoolT> IsPromiseReactionJobTask(TNode<HeapObject> object);
|
||||||
TNode<BoolT> IsPromiseRejectReactionJobTask(SloppyTNode<HeapObject> object);
|
TNode<BoolT> IsPromiseRejectReactionJobTask(SloppyTNode<HeapObject> object);
|
||||||
TNode<BoolT> IsPromiseFulfillReactionJobTask(SloppyTNode<HeapObject> object);
|
TNode<BoolT> IsPromiseFulfillReactionJobTask(SloppyTNode<HeapObject> object);
|
||||||
TNode<BoolT> IsPrototypeInitialArrayPrototype(SloppyTNode<Context> context,
|
TNode<BoolT> IsPrototypeInitialArrayPrototype(SloppyTNode<Context> context,
|
||||||
|
@ -2449,7 +2449,7 @@ TEST(IsPromiseHookEnabled) {
|
|||||||
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AllocateAndInitJSPromise) {
|
TEST(NewJSPromise) {
|
||||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||||
|
|
||||||
const int kNumParams = 1;
|
const int kNumParams = 1;
|
||||||
@ -2457,7 +2457,7 @@ TEST(AllocateAndInitJSPromise) {
|
|||||||
PromiseBuiltinsAssembler m(asm_tester.state());
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
||||||
|
|
||||||
Node* const context = m.Parameter(kNumParams + 2);
|
Node* const context = m.Parameter(kNumParams + 2);
|
||||||
const TNode<JSPromise> promise = m.AllocateAndInitJSPromise(m.CAST(context));
|
const TNode<JSPromise> promise = m.NewJSPromise(m.CAST(context));
|
||||||
m.Return(promise);
|
m.Return(promise);
|
||||||
|
|
||||||
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
||||||
@ -2466,7 +2466,7 @@ TEST(AllocateAndInitJSPromise) {
|
|||||||
CHECK(result->IsJSPromise());
|
CHECK(result->IsJSPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AllocateAndSetJSPromise) {
|
TEST(NewJSPromise2) {
|
||||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||||
|
|
||||||
const int kNumParams = 1;
|
const int kNumParams = 1;
|
||||||
@ -2474,8 +2474,8 @@ TEST(AllocateAndSetJSPromise) {
|
|||||||
PromiseBuiltinsAssembler m(asm_tester.state());
|
PromiseBuiltinsAssembler m(asm_tester.state());
|
||||||
|
|
||||||
Node* const context = m.Parameter(kNumParams + 2);
|
Node* const context = m.Parameter(kNumParams + 2);
|
||||||
const TNode<JSPromise> promise = m.AllocateAndSetJSPromise(
|
const TNode<JSPromise> promise =
|
||||||
m.CAST(context), v8::Promise::kRejected, m.SmiConstant(1));
|
m.NewJSPromise(m.CAST(context), v8::Promise::kRejected, m.SmiConstant(1));
|
||||||
m.Return(promise);
|
m.Return(promise);
|
||||||
|
|
||||||
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
||||||
@ -2538,7 +2538,7 @@ TEST(PromiseHasHandler) {
|
|||||||
|
|
||||||
Node* const context = m.Parameter(kNumParams + 2);
|
Node* const context = m.Parameter(kNumParams + 2);
|
||||||
const TNode<JSPromise> promise =
|
const TNode<JSPromise> promise =
|
||||||
m.AllocateAndInitJSPromise(m.CAST(context), m.UndefinedConstant());
|
m.NewJSPromise(m.CAST(context), m.UndefinedConstant());
|
||||||
m.Return(m.SelectBooleanConstant(m.PromiseHasHandler(promise)));
|
m.Return(m.SelectBooleanConstant(m.PromiseHasHandler(promise)));
|
||||||
|
|
||||||
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
||||||
@ -2557,7 +2557,7 @@ TEST(CreatePromiseResolvingFunctionsContext) {
|
|||||||
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
||||||
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
||||||
const TNode<JSPromise> promise =
|
const TNode<JSPromise> promise =
|
||||||
m.AllocateAndInitJSPromise(context, m.UndefinedConstant());
|
m.NewJSPromise(context, m.UndefinedConstant());
|
||||||
const TNode<Context> promise_context =
|
const TNode<Context> promise_context =
|
||||||
m.CreatePromiseResolvingFunctionsContext(
|
m.CreatePromiseResolvingFunctionsContext(
|
||||||
context, promise, m.BooleanConstant(false), native_context);
|
context, promise, m.BooleanConstant(false), native_context);
|
||||||
@ -2585,7 +2585,7 @@ TEST(CreatePromiseResolvingFunctions) {
|
|||||||
Node* const context = m.Parameter(kNumParams + 2);
|
Node* const context = m.Parameter(kNumParams + 2);
|
||||||
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
||||||
const TNode<JSPromise> promise =
|
const TNode<JSPromise> promise =
|
||||||
m.AllocateAndInitJSPromise(m.CAST(context), m.UndefinedConstant());
|
m.NewJSPromise(m.CAST(context), m.UndefinedConstant());
|
||||||
PromiseResolvingFunctions funcs = m.CreatePromiseResolvingFunctions(
|
PromiseResolvingFunctions funcs = m.CreatePromiseResolvingFunctions(
|
||||||
m.CAST(context), promise, m.BooleanConstant(false), native_context);
|
m.CAST(context), promise, m.BooleanConstant(false), native_context);
|
||||||
Node *resolve = funcs.resolve, *reject = funcs.reject;
|
Node *resolve = funcs.resolve, *reject = funcs.reject;
|
||||||
@ -2675,7 +2675,7 @@ TEST(AllocateFunctionWithMapAndContext) {
|
|||||||
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
||||||
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
||||||
const TNode<JSPromise> promise =
|
const TNode<JSPromise> promise =
|
||||||
m.AllocateAndInitJSPromise(context, m.UndefinedConstant());
|
m.NewJSPromise(context, m.UndefinedConstant());
|
||||||
TNode<Context> promise_context = m.CreatePromiseResolvingFunctionsContext(
|
TNode<Context> promise_context = m.CreatePromiseResolvingFunctionsContext(
|
||||||
context, promise, m.BooleanConstant(false), native_context);
|
context, promise, m.BooleanConstant(false), native_context);
|
||||||
TNode<Object> resolve_info = m.LoadContextElement(
|
TNode<Object> resolve_info = m.LoadContextElement(
|
||||||
|
Loading…
Reference in New Issue
Block a user