Commit b361b59f authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Move promise constructor to TFS

BUG=v8:5343,chromium:660947,chromium:658194

Review-Url: https://codereview.chromium.org/2497523002
Cr-Commit-Position: refs/heads/master@{#41438}
parent df9deb53
......@@ -1800,6 +1800,76 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
to_primitive->shared()->set_length(1);
}
{ // -- P r o m i s e
// Set catch prediction
Handle<Code> promise_code = isolate->builtins()->PromiseConstructor();
promise_code->set_is_promise_rejection(true);
Handle<JSObject> prototype =
factory->NewJSObject(isolate->object_function(), TENURED);
Handle<JSFunction> promise_fun = InstallFunction(
global, "Promise", JS_PROMISE_TYPE, JSObject::kHeaderSize, prototype,
Builtins::kPromiseConstructor);
InstallWithIntrinsicDefaultProto(isolate, promise_fun,
Context::PROMISE_FUNCTION_INDEX);
Handle<SharedFunctionInfo> shared(promise_fun->shared(), isolate);
shared->SetConstructStub(*isolate->builtins()->JSBuiltinsConstructStub());
shared->set_instance_class_name(isolate->heap()->Object_string());
shared->set_internal_formal_parameter_count(1);
shared->set_length(1);
// Install the "constructor" property on the {prototype}.
JSObject::AddProperty(prototype, factory->constructor_string(), promise_fun,
DONT_ENUM);
// Install the @@toStringTag property on the {prototype}.
JSObject::AddProperty(
prototype, factory->to_string_tag_symbol(), factory->Promise_string(),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
{ // Internal: PromiseInternalConstructor
Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kPromiseInternalConstructor, 0, false);
InstallWithIntrinsicDefaultProto(
isolate, function, Context::PROMISE_INTERNAL_CONSTRUCTOR_INDEX);
}
{ // Internal: IsPromise
Handle<JSFunction> function = SimpleCreateFunction(
isolate, factory->empty_string(), Builtins::kIsPromise, 1, false);
InstallWithIntrinsicDefaultProto(isolate, function,
Context::IS_PROMISE_INDEX);
}
{
Handle<Code> code =
handle(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure),
isolate);
Handle<SharedFunctionInfo> info =
factory->NewSharedFunctionInfo(factory->empty_string(), code, false);
info->set_internal_formal_parameter_count(1);
info->set_length(1);
native_context()->set_promise_resolve_shared_fun(*info);
code =
handle(isolate->builtins()->builtin(Builtins::kPromiseRejectClosure),
isolate);
info =
factory->NewSharedFunctionInfo(factory->empty_string(), code, false);
info->set_internal_formal_parameter_count(2);
info->set_length(1);
native_context()->set_promise_reject_shared_fun(*info);
}
Handle<JSFunction> create_resolving_functions =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kCreateResolvingFunctions, 2, false);
native_context()->set_create_resolving_functions(
*create_resolving_functions);
}
{ // -- R e g E x p
// Builtin functions for RegExp.prototype.
Handle<JSObject> prototype =
......@@ -3580,46 +3650,6 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
concat->shared()->set_length(1);
}
// Set up the Promise constructor.
{
Handle<String> key = factory()->Promise_string();
Handle<JSFunction> function = Handle<JSFunction>::cast(
JSReceiver::GetProperty(global_object, key).ToHandleChecked());
JSFunction::EnsureHasInitialMap(function);
function->initial_map()->set_instance_type(JS_PROMISE_TYPE);
function->shared()->SetConstructStub(
*isolate()->builtins()->JSBuiltinsConstructStub());
InstallWithIntrinsicDefaultProto(isolate(), function,
Context::PROMISE_FUNCTION_INDEX);
{
Handle<Code> code = handle(
isolate()->builtins()->builtin(Builtins::kPromiseResolveClosure),
isolate());
Handle<SharedFunctionInfo> info =
isolate()->factory()->NewSharedFunctionInfo(factory()->empty_string(),
code, false);
info->set_internal_formal_parameter_count(1);
info->set_length(1);
native_context()->set_promise_resolve_shared_fun(*info);
code = handle(
isolate()->builtins()->builtin(Builtins::kPromiseRejectClosure),
isolate());
info = isolate()->factory()->NewSharedFunctionInfo(
factory()->empty_string(), code, false);
info->set_internal_formal_parameter_count(2);
info->set_length(1);
native_context()->set_promise_reject_shared_fun(*info);
}
Handle<JSFunction> create_resolving_functions =
SimpleCreateFunction(isolate(), factory()->empty_string(),
Builtins::kCreateResolvingFunctions, 2, false);
native_context()->set_create_resolving_functions(
*create_resolving_functions);
}
InstallBuiltinFunctionIds();
// Create a map for accessor property descriptors (a variant of JSObject
......
......@@ -5,6 +5,7 @@
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/promise-utils.h"
namespace v8 {
......@@ -80,5 +81,163 @@ BUILTIN(CreateResolvingFunctions) {
NOT_TENURED);
}
void Builtins::Generate_PromiseConstructor(
compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef CodeStubAssembler::Variable Variable;
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* const executor = a.Parameter(1);
Node* const new_target = a.Parameter(2);
Node* const context = a.Parameter(4);
Isolate* isolate = a.isolate();
Label if_targetisundefined(&a, Label::kDeferred);
a.GotoIf(a.IsUndefined(new_target), &if_targetisundefined);
Label if_notcallable(&a, Label::kDeferred);
a.GotoIf(a.TaggedIsSmi(executor), &if_notcallable);
Node* const executor_map = a.LoadMap(executor);
a.GotoUnless(a.IsCallableMap(executor_map), &if_notcallable);
Node* const native_context = a.LoadNativeContext(context);
Node* const promise_fun =
a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const is_debug_active = a.IsDebugActive();
Label if_targetisnotmodified(&a), if_targetismodified(&a, Label::kDeferred),
run_executor(&a), debug_push(&a, Label::kDeferred);
a.Branch(a.WordEqual(promise_fun, new_target), &if_targetisnotmodified,
&if_targetismodified);
Variable var_result(&a, MachineRepresentation::kTagged),
var_reject_call(&a, MachineRepresentation::kTagged),
var_reason(&a, MachineRepresentation::kTagged);
a.Bind(&if_targetisnotmodified);
{
Node* const initial_map = a.LoadObjectField(
promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const instance = a.AllocateJSObjectFromMap(initial_map);
var_result.Bind(instance);
a.Branch(is_debug_active, &debug_push, &run_executor);
}
a.Bind(&if_targetismodified);
{
Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate);
Node* const instance =
a.CallStub(fast_new_object_stub, context, promise_fun, new_target);
var_result.Bind(instance);
a.Branch(is_debug_active, &debug_push, &run_executor);
}
a.Bind(&debug_push);
{
a.CallRuntime(Runtime::kDebugPushPromise, context, var_result.value());
a.Goto(&run_executor);
}
a.Bind(&run_executor);
{
Label out(&a), if_rejectpromise(&a), debug_pop(&a, Label::kDeferred);
Node* const key = a.LoadRoot(Heap::kpromise_state_symbolRootIndex);
Node* const value = a.SmiConstant(kPromisePending);
Node* const language_mode = a.SmiConstant(STRICT);
// TODO(ishell): Use SetProperty stub once available.
a.CallRuntime(Runtime::kSetProperty, context, var_result.value(), key,
value, language_mode);
Node* const resolving_functions = a.CallRuntime(
Runtime::kCreateResolvingFunctions, context, var_result.value());
Node* const resolve =
a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(0));
Node* const reject =
a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(1));
Callable call_callable = CodeFactory::Call(isolate);
Node* const maybe_exception =
a.CallJS(call_callable, context, executor, a.UndefinedConstant(),
resolve, reject);
a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
a.Branch(is_debug_active, &debug_pop, &out);
a.Bind(&if_rejectpromise);
{
Callable call_callable = CodeFactory::Call(isolate);
a.CallJS(call_callable, context, reject, a.UndefinedConstant(),
var_reason.value());
a.Branch(is_debug_active, &debug_pop, &out);
}
a.Bind(&debug_pop);
{
a.CallRuntime(Runtime::kDebugPopPromise, context);
a.Goto(&out);
}
a.Bind(&out);
a.Return(var_result.value());
}
// 1. If NewTarget is undefined, throw a TypeError exception.
a.Bind(&if_targetisundefined);
{
Node* const message_id = a.SmiConstant(MessageTemplate::kNotAPromise);
a.CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target);
a.Return(a.UndefinedConstant()); // Never reached.
}
// 2. If IsCallable(executor) is false, throw a TypeError exception.
a.Bind(&if_notcallable);
{
Node* const message_id =
a.SmiConstant(MessageTemplate::kResolverNotAFunction);
a.CallRuntime(Runtime::kThrowTypeError, context, message_id, executor);
a.Return(a.UndefinedConstant()); // Never reached.
}
}
void Builtins::Generate_PromiseInternalConstructor(
compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
CodeStubAssembler a(state);
Node* const context = a.Parameter(3);
Node* const native_context = a.LoadNativeContext(context);
Node* const promise_fun =
a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const initial_map =
a.LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const instance = a.AllocateJSObjectFromMap(initial_map);
a.Return(instance);
}
// TODO(gsathya): Refactor promise.js::IsPromise to use this.
void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
Node* const maybe_promise = a.Parameter(1);
Label if_ispromise(&a), if_isnotpromise(&a, Label::kDeferred);
a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_isnotpromise);
a.Branch(a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE), &if_ispromise,
&if_isnotpromise);
a.Bind(&if_ispromise);
a.Return(a.BooleanConstant(true));
a.Bind(&if_isnotpromise);
a.Return(a.BooleanConstant(false));
}
} // namespace internal
} // namespace v8
......@@ -559,6 +559,9 @@ namespace internal {
TFS(ForInFilter, BUILTIN, kNoExtraICState, ForInFilter) \
\
/* Promise */ \
TFJ(PromiseConstructor, 1) \
TFJ(PromiseInternalConstructor, 0) \
TFJ(IsPromise, 1) \
CPP(CreateResolvingFunctions) \
CPP(PromiseResolveClosure) \
CPP(PromiseRejectClosure) \
......
......@@ -34,33 +34,36 @@ enum ContextLookupFlags {
// must always be allocated via Heap::AllocateContext() or
// Factory::NewContext.
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
V(IS_ARRAYLIKE, JSFunction, is_arraylike) \
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
V(GET_TEMPLATE_CALL_SITE_INDEX, JSFunction, get_template_call_site) \
V(MAKE_ERROR_INDEX, JSFunction, make_error) \
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
V(OBJECT_DEFINE_PROPERTIES, JSFunction, object_define_properties) \
V(OBJECT_DEFINE_PROPERTY, JSFunction, object_define_property) \
V(OBJECT_FREEZE, JSFunction, object_freeze) \
V(OBJECT_GET_PROTOTYPE_OF, JSFunction, object_get_prototype_of) \
V(OBJECT_IS_EXTENSIBLE, JSFunction, object_is_extensible) \
V(OBJECT_IS_FROZEN, JSFunction, object_is_frozen) \
V(OBJECT_IS_SEALED, JSFunction, object_is_sealed) \
V(OBJECT_KEYS, JSFunction, object_keys) \
V(REGEXP_INTERNAL_MATCH, JSFunction, regexp_internal_match) \
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
V(REFLECT_DEFINE_PROPERTY_INDEX, JSFunction, reflect_define_property) \
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
V(MATH_POW_INDEX, JSFunction, math_pow) \
V(CREATE_RESOLVING_FUNCTION_INDEX, JSFunction, create_resolving_functions)
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
V(IS_ARRAYLIKE, JSFunction, is_arraylike) \
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
V(GET_TEMPLATE_CALL_SITE_INDEX, JSFunction, get_template_call_site) \
V(MAKE_ERROR_INDEX, JSFunction, make_error) \
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
V(OBJECT_DEFINE_PROPERTIES, JSFunction, object_define_properties) \
V(OBJECT_DEFINE_PROPERTY, JSFunction, object_define_property) \
V(OBJECT_FREEZE, JSFunction, object_freeze) \
V(OBJECT_GET_PROTOTYPE_OF, JSFunction, object_get_prototype_of) \
V(OBJECT_IS_EXTENSIBLE, JSFunction, object_is_extensible) \
V(OBJECT_IS_FROZEN, JSFunction, object_is_frozen) \
V(OBJECT_IS_SEALED, JSFunction, object_is_sealed) \
V(OBJECT_KEYS, JSFunction, object_keys) \
V(REGEXP_INTERNAL_MATCH, JSFunction, regexp_internal_match) \
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
V(REFLECT_DEFINE_PROPERTY_INDEX, JSFunction, reflect_define_property) \
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
V(MATH_POW_INDEX, JSFunction, math_pow) \
V(CREATE_RESOLVING_FUNCTION_INDEX, JSFunction, create_resolving_functions) \
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
promise_internal_constructor) \
V(IS_PROMISE_INDEX, JSFunction, is_promise)
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
......
......@@ -1335,6 +1335,10 @@ HandlerTable::CatchPrediction PredictException(JavaScriptFrame* frame) {
frame->Summarize(&summaries);
for (const FrameSummary& summary : summaries) {
Handle<AbstractCode> code = summary.abstract_code();
if (code->IsCode() && code->kind() == AbstractCode::BUILTIN &&
code->GetCode()->is_promise_rejection()) {
return HandlerTable::PROMISE;
}
if (code->kind() == AbstractCode::OPTIMIZED_FUNCTION) {
DCHECK(summary.function()->shared()->asm_function());
DCHECK(!FLAG_turbo_asm_deoptimization);
......
......@@ -35,6 +35,7 @@ var SpeciesConstructor;
var speciesSymbol = utils.ImportNow("species_symbol");
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
var ObjectHasOwnProperty;
var GlobalPromise = global.Promise;
utils.Import(function(from) {
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
......@@ -52,35 +53,6 @@ const kRejected = +2;
const kResolveCallback = 0;
const kRejectCallback = 1;
// ES#sec-promise-executor
// Promise ( executor )
var GlobalPromise = function Promise(executor) {
if (executor === promiseRawSymbol) {
return %_NewObject(GlobalPromise, new.target);
}
if (IS_UNDEFINED(new.target)) throw %make_type_error(kNotAPromise, this);
if (!IS_CALLABLE(executor)) {
throw %make_type_error(kResolverNotAFunction, executor);
}
var promise = PromiseInit(%_NewObject(GlobalPromise, new.target));
// Calling the reject function would be a new exception, so debugEvent = true
// TODO(gsathya): Remove container for callbacks when this is moved
// to CPP/TF.
var callbacks = %create_resolving_functions(promise, true);
var debug_is_active = DEBUG_IS_ACTIVE;
try {
if (debug_is_active) %DebugPushPromise(promise);
executor(callbacks[kResolveCallback], callbacks[kRejectCallback]);
} %catch (e) { // Natives syntax to mark this catch block.
%_Call(callbacks[kRejectCallback], UNDEFINED, e);
} finally {
if (debug_is_active) %DebugPopPromise();
}
return promise;
}
// Core functionality.
function PromiseSet(promise, status, value) {
......@@ -109,7 +81,7 @@ function PromiseSet(promise, status, value) {
}
function PromiseCreateAndSet(status, value) {
var promise = new GlobalPromise(promiseRawSymbol);
var promise = %promise_internal_constructor();
// If debug is active, notify about the newly created promise first.
if (DEBUG_IS_ACTIVE) PromiseSet(promise, kPending, UNDEFINED);
return PromiseSet(promise, status, value);
......@@ -208,13 +180,14 @@ SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
// For bootstrapper.
// Only used by utils
// ES#sec-ispromise IsPromise ( x )
function IsPromise(x) {
return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol);
}
function PromiseCreate() {
return PromiseInit(new GlobalPromise(promiseRawSymbol));
return PromiseInit(%promise_internal_constructor());
}
// ES#sec-promise-resolve-functions
......@@ -385,8 +358,7 @@ function PerformPromiseThen(promise, onResolve, onReject, resultCapability) {
// Promise.prototype.then ( onFulfilled, onRejected )
// Multi-unwrapped chaining with thenable coercion.
function PromiseThen(onResolve, onReject) {
var status = GET_PRIVATE(this, promiseStateSymbol);
if (IS_UNDEFINED(status)) {
if (!IsPromise(this)) {
throw %make_type_error(kNotAPromise, this);
}
......@@ -601,10 +573,6 @@ function PromiseSpecies() {
// -------------------------------------------------------------------
// Install exported functions.
%AddNamedProperty(global, 'Promise', GlobalPromise, DONT_ENUM);
%AddNamedProperty(GlobalPromise.prototype, toStringTagSymbol, "Promise",
DONT_ENUM | READ_ONLY);
utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
"reject", PromiseReject,
"all", PromiseAll,
......
......@@ -5132,6 +5132,19 @@ inline void Code::set_is_construct_stub(bool value) {
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
inline bool Code::is_promise_rejection() {
DCHECK(kind() == BUILTIN);
return IsPromiseRejectionField::decode(
READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
}
inline void Code::set_is_promise_rejection(bool value) {
DCHECK(kind() == BUILTIN);
int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
int updated = IsPromiseRejectionField::update(previous, value);
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
bool Code::has_deoptimization_support() {
DCHECK_EQ(FUNCTION, kind());
unsigned flags = READ_UINT32_FIELD(this, kFullCodeFlags);
......
......@@ -5574,6 +5574,11 @@ class Code: public HeapObject {
inline bool marked_for_deoptimization();
inline void set_marked_for_deoptimization(bool flag);
// [is_promise_rejection]: For kind BUILTIN tells whether the exception
// thrown by the code will lead to promise rejection.
inline bool is_promise_rejection();
inline void set_is_promise_rejection(bool flag);
// [constant_pool]: The constant pool for this function.
inline Address constant_pool();
......@@ -5850,9 +5855,10 @@ class Code: public HeapObject {
static const int kCanHaveWeakObjects = kIsTurbofannedBit + 1;
// Could be moved to overlap previous bits when we need more space.
static const int kIsConstructStub = kCanHaveWeakObjects + 1;
static const int kIsPromiseRejection = kIsConstructStub + 1;
STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32);
STATIC_ASSERT(kIsConstructStub + 1 <= 32);
STATIC_ASSERT(kIsPromiseRejection + 1 <= 32);
class StackSlotsField: public BitField<int,
kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT
......@@ -5864,6 +5870,8 @@ class Code: public HeapObject {
: public BitField<bool, kCanHaveWeakObjects, 1> {}; // NOLINT
class IsConstructStubField : public BitField<bool, kIsConstructStub, 1> {
}; // NOLINT
class IsPromiseRejectionField
: public BitField<bool, kIsPromiseRejection, 1> {}; // NOLINT
// KindSpecificFlags2 layout (ALL)
static const int kIsCrankshaftedBit = 0;
......
......@@ -189,5 +189,20 @@ RUNTIME_FUNCTION(Runtime_RunMicrotasks) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_CreateResolvingFunctions) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
Handle<JSFunction> resolve, reject;
PromiseUtils::CreateResolvingFunctions(
isolate, promise, isolate->factory()->true_value(), &resolve, &reject);
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2);
result->set(0, *resolve);
result->set(1, *reject);
return *result;
}
} // namespace internal
} // namespace v8
......@@ -289,6 +289,7 @@ namespace internal {
F(AllocateSeqTwoByteString, 1, 1) \
F(CheckIsBootstrapping, 0, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(CreateResolvingFunctions, 1, 1) \
F(EnqueueMicrotask, 1, 1) \
F(EnqueuePromiseReactionJob, 4, 1) \
F(EnqueuePromiseResolveThenableJob, 3, 1) \
......
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