Object
-- New JSObject for promises: JSPromise Builtins -- PromiseThen TFJ -- PromiseCreateAndSet TFJ for internal use -- PerformPromiseThen TFJ for internal use -- PromiseInit for initial promise setup -- SpeciesConstructor for use in PromiseThen -- ThrowIfNotJSReceiver for use in SpeciesConstructor -- AppendPromiseCallback to update FixedArray with new callback -- InternalPerformPromiseThen Promises.js -- Cleanup unused symbols -- Remove PerformPromiseThen -- Remove PromiseThen -- Remove PromiseSet -- Remove PromiseAttachCallbacks Runtime -- PromiseSet to set promise inobject values -- Refactor functions to use FixedArrays for callbacks instead of JSArray -- Runtime_PromiseStatus to return promise status -- Runtime_PromiseResult to return promise result -- Runtime_PromiseDeferred to return deferred attached to promise -- Runtime_PromiseRejectReactions to return reject reactions attached to promise This CL results in a 13.07% improvement in the promises benchmark (over 5 runs). BUG=v8:5343 Review-Url: https://codereview.chromium.org/2536463002 Cr-Commit-Position: refs/heads/master@{#41503}
This commit is contained in:
parent
7036eec6f7
commit
30b564c76f
@ -1795,9 +1795,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
|
||||
Handle<JSObject> prototype =
|
||||
factory->NewJSObject(isolate->object_function(), TENURED);
|
||||
Handle<JSFunction> promise_fun = InstallFunction(
|
||||
global, "Promise", JS_PROMISE_TYPE, JSObject::kHeaderSize, prototype,
|
||||
Builtins::kPromiseConstructor);
|
||||
Handle<JSFunction> promise_fun =
|
||||
InstallFunction(global, "Promise", JS_PROMISE_TYPE, JSPromise::kSize,
|
||||
prototype, Builtins::kPromiseConstructor);
|
||||
InstallWithIntrinsicDefaultProto(isolate, promise_fun,
|
||||
Context::PROMISE_FUNCTION_INDEX);
|
||||
|
||||
@ -1816,6 +1816,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
prototype, factory->to_string_tag_symbol(), factory->Promise_string(),
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
|
||||
|
||||
SimpleInstallFunction(prototype, "then", Builtins::kPromiseThen, 2, true,
|
||||
DONT_ENUM);
|
||||
|
||||
{ // Internal: PromiseInternalConstructor
|
||||
Handle<JSFunction> function =
|
||||
SimpleCreateFunction(isolate, factory->empty_string(),
|
||||
@ -1824,6 +1827,14 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
isolate, function, Context::PROMISE_INTERNAL_CONSTRUCTOR_INDEX);
|
||||
}
|
||||
|
||||
{ // Internal: PromiseCreateAndSet
|
||||
Handle<JSFunction> function =
|
||||
SimpleCreateFunction(isolate, factory->empty_string(),
|
||||
Builtins::kPromiseCreateAndSet, 2, false);
|
||||
InstallWithIntrinsicDefaultProto(isolate, function,
|
||||
Context::PROMISE_CREATE_AND_SET_INDEX);
|
||||
}
|
||||
|
||||
{ // Internal: IsPromise
|
||||
Handle<JSFunction> function = SimpleCreateFunction(
|
||||
isolate, factory->empty_string(), Builtins::kIsPromise, 1, false);
|
||||
@ -1831,6 +1842,14 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
Context::IS_PROMISE_INDEX);
|
||||
}
|
||||
|
||||
{ // Internal: PerformPromiseThen
|
||||
Handle<JSFunction> function =
|
||||
SimpleCreateFunction(isolate, factory->empty_string(),
|
||||
Builtins::kPerformPromiseThen, 4, false);
|
||||
InstallWithIntrinsicDefaultProto(isolate, function,
|
||||
Context::PERFORM_PROMISE_THEN_INDEX);
|
||||
}
|
||||
|
||||
{
|
||||
Handle<Code> code =
|
||||
handle(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure),
|
||||
|
@ -81,6 +81,13 @@ BUILTIN(CreateResolvingFunctions) {
|
||||
NOT_TENURED);
|
||||
}
|
||||
|
||||
void PromiseInit(CodeStubAssembler* a, compiler::Node* promise,
|
||||
compiler::Node* status, compiler::Node* result) {
|
||||
CSA_ASSERT(a, a->TaggedIsSmi(status));
|
||||
a->StoreObjectField(promise, JSPromise::kStatusOffset, status);
|
||||
a->StoreObjectField(promise, JSPromise::kResultOffset, result);
|
||||
}
|
||||
|
||||
void Builtins::Generate_PromiseConstructor(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
CodeStubAssembler a(state);
|
||||
@ -108,9 +115,9 @@ void Builtins::Generate_PromiseConstructor(
|
||||
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);
|
||||
run_executor(&a), debug_push(&a, Label::kDeferred), init(&a);
|
||||
|
||||
a.Branch(a.WordEqual(promise_fun, new_target), &if_targetisnotmodified,
|
||||
&if_targetismodified);
|
||||
|
||||
@ -125,7 +132,7 @@ void Builtins::Generate_PromiseConstructor(
|
||||
|
||||
Node* const instance = a.AllocateJSObjectFromMap(initial_map);
|
||||
var_result.Bind(instance);
|
||||
a.Branch(is_debug_active, &debug_push, &run_executor);
|
||||
a.Goto(&init);
|
||||
}
|
||||
|
||||
a.Bind(&if_targetismodified);
|
||||
@ -135,6 +142,13 @@ void Builtins::Generate_PromiseConstructor(
|
||||
a.CallStub(fast_new_object_stub, context, promise_fun, new_target);
|
||||
|
||||
var_result.Bind(instance);
|
||||
a.Goto(&init);
|
||||
}
|
||||
|
||||
a.Bind(&init);
|
||||
{
|
||||
PromiseInit(&a, var_result.value(), a.SmiConstant(kPromisePending),
|
||||
a.UndefinedConstant());
|
||||
a.Branch(is_debug_active, &debug_push, &run_executor);
|
||||
}
|
||||
|
||||
@ -148,12 +162,7 @@ void Builtins::Generate_PromiseConstructor(
|
||||
{
|
||||
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);
|
||||
// TODO(gsathya): Move this to TF.
|
||||
Node* const resolving_functions = a.CallRuntime(
|
||||
Runtime::kCreateResolvingFunctions, context, var_result.value());
|
||||
Node* const resolve =
|
||||
@ -216,26 +225,441 @@ void Builtins::Generate_PromiseInternalConstructor(
|
||||
Node* const initial_map =
|
||||
a.LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
|
||||
Node* const instance = a.AllocateJSObjectFromMap(initial_map);
|
||||
|
||||
PromiseInit(&a, instance, a.SmiConstant(kPromisePending),
|
||||
a.UndefinedConstant());
|
||||
a.Return(instance);
|
||||
}
|
||||
|
||||
void Builtins::Generate_PromiseCreateAndSet(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
typedef compiler::Node Node;
|
||||
CodeStubAssembler a(state);
|
||||
|
||||
Node* const status = a.Parameter(1);
|
||||
Node* const result = a.Parameter(2);
|
||||
Node* const context = a.Parameter(5);
|
||||
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);
|
||||
|
||||
PromiseInit(&a, instance, status, result);
|
||||
a.Return(instance);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate,
|
||||
compiler::Node* context,
|
||||
compiler::Node* value,
|
||||
MessageTemplate::Template msg_template) {
|
||||
typedef compiler::Node Node;
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
|
||||
Label out(a), throw_exception(a, Label::kDeferred);
|
||||
Variable var_value_map(a, MachineRepresentation::kTagged);
|
||||
|
||||
a->GotoIf(a->TaggedIsSmi(value), &throw_exception);
|
||||
|
||||
// Load the instance type of the {value}.
|
||||
var_value_map.Bind(a->LoadMap(value));
|
||||
Node* const value_instance_type =
|
||||
a->LoadMapInstanceType(var_value_map.value());
|
||||
|
||||
a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out,
|
||||
&throw_exception);
|
||||
|
||||
// The {value} is not a compatible receiver for this method.
|
||||
a->Bind(&throw_exception);
|
||||
{
|
||||
Node* const message_id = a->SmiConstant(msg_template);
|
||||
a->CallRuntime(Runtime::kThrowTypeError, context, message_id);
|
||||
var_value_map.Bind(a->UndefinedConstant());
|
||||
a->Goto(&out); // Never reached.
|
||||
}
|
||||
|
||||
a->Bind(&out);
|
||||
return var_value_map.value();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
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);
|
||||
Label if_notpromise(&a, Label::kDeferred);
|
||||
|
||||
a.Branch(a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE), &if_ispromise,
|
||||
&if_isnotpromise);
|
||||
a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_notpromise);
|
||||
|
||||
a.Bind(&if_ispromise);
|
||||
a.Return(a.BooleanConstant(true));
|
||||
Node* const result = a.SelectBooleanConstant(
|
||||
a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE));
|
||||
a.Return(result);
|
||||
|
||||
a.Bind(&if_isnotpromise);
|
||||
a.Return(a.BooleanConstant(false));
|
||||
a.Bind(&if_notpromise);
|
||||
a.Return(a.FalseConstant());
|
||||
}
|
||||
|
||||
namespace {
|
||||
compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate,
|
||||
compiler::Node* context,
|
||||
compiler::Node* object,
|
||||
compiler::Node* default_constructor) {
|
||||
typedef compiler::Node Node;
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
|
||||
Variable var_result(a, MachineRepresentation::kTagged);
|
||||
var_result.Bind(default_constructor);
|
||||
|
||||
// 2. Let C be ? Get(O, "constructor").
|
||||
Node* const constructor_str =
|
||||
a->HeapConstant(isolate->factory()->constructor_string());
|
||||
Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
|
||||
Node* const constructor =
|
||||
a->CallStub(getproperty_callable, context, object, constructor_str);
|
||||
|
||||
// 3. If C is undefined, return defaultConstructor.
|
||||
Label out(a);
|
||||
a->GotoIf(a->IsUndefined(constructor), &out);
|
||||
|
||||
// 4. If Type(C) is not Object, throw a TypeError exception.
|
||||
ThrowIfNotJSReceiver(a, a->isolate(), context, constructor,
|
||||
MessageTemplate::kConstructorNotReceiver);
|
||||
|
||||
// 5. Let S be ? Get(C, @@species).
|
||||
Node* const species_symbol =
|
||||
a->HeapConstant(isolate->factory()->species_symbol());
|
||||
Node* const species =
|
||||
a->CallStub(getproperty_callable, context, constructor, species_symbol);
|
||||
|
||||
// 6. If S is either undefined or null, return defaultConstructor.
|
||||
a->GotoIf(a->IsUndefined(species), &out);
|
||||
a->GotoIf(a->WordEqual(species, a->NullConstant()), &out);
|
||||
|
||||
// 7. If IsConstructor(S) is true, return S.
|
||||
Label throw_error(a);
|
||||
Node* species_bitfield = a->LoadMapBitField(a->LoadMap(species));
|
||||
a->GotoUnless(
|
||||
a->Word32Equal(a->Word32And(species_bitfield,
|
||||
a->Int32Constant((1 << Map::kIsConstructor))),
|
||||
a->Int32Constant(1 << Map::kIsConstructor)),
|
||||
&throw_error);
|
||||
var_result.Bind(species);
|
||||
a->Goto(&out);
|
||||
|
||||
// 8. Throw a TypeError exception.
|
||||
a->Bind(&throw_error);
|
||||
{
|
||||
Node* const message_id =
|
||||
a->SmiConstant(MessageTemplate::kSpeciesNotConstructor);
|
||||
a->CallRuntime(Runtime::kThrowTypeError, context, message_id);
|
||||
a->Goto(&out);
|
||||
}
|
||||
|
||||
a->Bind(&out);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void AppendPromiseCallback(CodeStubAssembler* a, int offset,
|
||||
compiler::Node* promise, compiler::Node* value) {
|
||||
typedef compiler::Node Node;
|
||||
|
||||
Node* elements = a->LoadObjectField(promise, offset);
|
||||
Node* length = a->LoadFixedArrayBaseLength(elements);
|
||||
CodeStubAssembler::ParameterMode mode = a->OptimalParameterMode();
|
||||
length = a->UntagParameter(length, mode);
|
||||
|
||||
Node* delta = a->IntPtrOrSmiConstant(1, mode);
|
||||
Node* new_capacity = a->IntPtrAdd(length, delta);
|
||||
ElementsKind kind = FAST_ELEMENTS;
|
||||
|
||||
Node* new_elements = a->AllocateFixedArray(kind, new_capacity, mode);
|
||||
|
||||
a->CopyFixedArrayElements(kind, elements, new_elements, length,
|
||||
UPDATE_WRITE_BARRIER, mode);
|
||||
a->StoreFixedArrayElement(new_elements, length, value, UPDATE_WRITE_BARRIER,
|
||||
0, mode);
|
||||
|
||||
a->StoreObjectField(promise, offset, new_elements);
|
||||
}
|
||||
|
||||
void InternalPerformPromiseThen(CodeStubAssembler* a, compiler::Node* context,
|
||||
compiler::Node* promise,
|
||||
compiler::Node* on_resolve,
|
||||
compiler::Node* on_reject,
|
||||
compiler::Node* deferred) {
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef compiler::Node Node;
|
||||
|
||||
Isolate* isolate = a->isolate();
|
||||
Node* const native_context = a->LoadNativeContext(context);
|
||||
|
||||
Variable var_on_resolve(a, MachineRepresentation::kTagged),
|
||||
var_on_reject(a, MachineRepresentation::kTagged);
|
||||
|
||||
var_on_resolve.Bind(on_resolve);
|
||||
var_on_reject.Bind(on_reject);
|
||||
|
||||
Label out(a), if_onresolvenotcallable(a), onrejectcheck(a),
|
||||
append_callbacks(a);
|
||||
a->GotoIf(a->TaggedIsSmi(on_resolve), &if_onresolvenotcallable);
|
||||
|
||||
Node* const on_resolve_map = a->LoadMap(on_resolve);
|
||||
a->Branch(a->IsCallableMap(on_resolve_map), &onrejectcheck,
|
||||
&if_onresolvenotcallable);
|
||||
|
||||
a->Bind(&if_onresolvenotcallable);
|
||||
{
|
||||
var_on_resolve.Bind(a->LoadContextElement(
|
||||
native_context, Context::PROMISE_ID_RESOLVE_HANDLER_INDEX));
|
||||
a->Goto(&onrejectcheck);
|
||||
}
|
||||
|
||||
a->Bind(&onrejectcheck);
|
||||
{
|
||||
Label if_onrejectnotcallable(a);
|
||||
a->GotoIf(a->TaggedIsSmi(on_reject), &if_onrejectnotcallable);
|
||||
|
||||
Node* const on_reject_map = a->LoadMap(on_reject);
|
||||
a->Branch(a->IsCallableMap(on_reject_map), &append_callbacks,
|
||||
&if_onrejectnotcallable);
|
||||
|
||||
a->Bind(&if_onrejectnotcallable);
|
||||
{
|
||||
var_on_reject.Bind(a->LoadContextElement(
|
||||
native_context, Context::PROMISE_ID_REJECT_HANDLER_INDEX));
|
||||
a->Goto(&append_callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
a->Bind(&append_callbacks);
|
||||
{
|
||||
Label fulfilled_check(a);
|
||||
Node* const status = a->LoadObjectField(promise, JSPromise::kStatusOffset);
|
||||
a->GotoUnless(a->SmiEqual(status, a->SmiConstant(kPromisePending)),
|
||||
&fulfilled_check);
|
||||
|
||||
Node* const existing_deferred =
|
||||
a->LoadObjectField(promise, JSPromise::kDeferredOffset);
|
||||
|
||||
Label if_noexistingcallbacks(a), if_existingcallbacks(a);
|
||||
a->Branch(a->IsUndefined(existing_deferred), &if_noexistingcallbacks,
|
||||
&if_existingcallbacks);
|
||||
|
||||
a->Bind(&if_noexistingcallbacks);
|
||||
{
|
||||
// Store callbacks directly in the slots.
|
||||
a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferred);
|
||||
a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset,
|
||||
var_on_resolve.value());
|
||||
a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset,
|
||||
var_on_reject.value());
|
||||
a->Goto(&out);
|
||||
}
|
||||
|
||||
a->Bind(&if_existingcallbacks);
|
||||
{
|
||||
Label if_singlecallback(a), if_multiplecallbacks(a);
|
||||
a->BranchIfJSObject(existing_deferred, &if_singlecallback,
|
||||
&if_multiplecallbacks);
|
||||
|
||||
a->Bind(&if_singlecallback);
|
||||
{
|
||||
// Create new FixedArrays to store callbacks.
|
||||
Node* const deferreds =
|
||||
a->AllocateFixedArray(FAST_ELEMENTS, a->Int32Constant(2));
|
||||
Node* const fulfill_reactions =
|
||||
a->AllocateFixedArray(FAST_ELEMENTS, a->Int32Constant(2));
|
||||
Node* const reject_reactions =
|
||||
a->AllocateFixedArray(FAST_ELEMENTS, a->Int32Constant(2));
|
||||
|
||||
// Store existing callbacks in FixedArrays.
|
||||
a->StoreFixedArrayElement(deferreds, 0, existing_deferred);
|
||||
a->StoreFixedArrayElement(
|
||||
fulfill_reactions, 0,
|
||||
a->LoadObjectField(promise, JSPromise::kFulfillReactionsOffset));
|
||||
a->StoreFixedArrayElement(
|
||||
reject_reactions, 0,
|
||||
a->LoadObjectField(promise, JSPromise::kRejectReactionsOffset));
|
||||
|
||||
// Store new callbacks in FixedArrays.
|
||||
a->StoreFixedArrayElement(deferreds, 1, deferred);
|
||||
a->StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value());
|
||||
a->StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value());
|
||||
|
||||
// Store new FixedArrays in promise.
|
||||
a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferreds);
|
||||
a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset,
|
||||
fulfill_reactions);
|
||||
a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset,
|
||||
reject_reactions);
|
||||
a->Goto(&out);
|
||||
}
|
||||
|
||||
a->Bind(&if_multiplecallbacks);
|
||||
{
|
||||
AppendPromiseCallback(a, JSPromise::kDeferredOffset, promise, deferred);
|
||||
AppendPromiseCallback(a, JSPromise::kFulfillReactionsOffset, promise,
|
||||
var_on_resolve.value());
|
||||
AppendPromiseCallback(a, JSPromise::kRejectReactionsOffset, promise,
|
||||
var_on_reject.value());
|
||||
a->Goto(&out);
|
||||
}
|
||||
}
|
||||
|
||||
a->Bind(&fulfilled_check);
|
||||
{
|
||||
Label reject(a);
|
||||
Node* const result =
|
||||
a->LoadObjectField(promise, JSPromise::kResultOffset);
|
||||
a->GotoUnless(a->WordEqual(status, a->SmiConstant(kPromiseFulfilled)),
|
||||
&reject);
|
||||
|
||||
// TODO(gsathya): Move this to TF.
|
||||
a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result,
|
||||
var_on_resolve.value(), deferred,
|
||||
a->SmiConstant(kPromiseFulfilled));
|
||||
a->Goto(&out);
|
||||
|
||||
a->Bind(&reject);
|
||||
{
|
||||
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
|
||||
Node* const key =
|
||||
a->HeapConstant(isolate->factory()->promise_has_handler_symbol());
|
||||
Node* const has_handler =
|
||||
a->CallStub(getproperty_callable, context, promise, key);
|
||||
|
||||
Label enqueue(a);
|
||||
|
||||
// TODO(gsathya): Fold these runtime calls and move to TF.
|
||||
a->GotoIf(a->WordEqual(has_handler, a->TrueConstant()), &enqueue);
|
||||
a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
|
||||
a->Goto(&enqueue);
|
||||
|
||||
a->Bind(&enqueue);
|
||||
{
|
||||
a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result,
|
||||
var_on_reject.value(), deferred,
|
||||
a->SmiConstant(kPromiseRejected));
|
||||
|
||||
a->Goto(&out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a->Bind(&out);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_PerformPromiseThen(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
CodeStubAssembler a(state);
|
||||
typedef compiler::Node Node;
|
||||
|
||||
Node* const promise = a.Parameter(1);
|
||||
Node* const on_resolve = a.Parameter(2);
|
||||
Node* const on_reject = a.Parameter(3);
|
||||
Node* const deferred = a.Parameter(4);
|
||||
Node* const context = a.Parameter(7);
|
||||
|
||||
InternalPerformPromiseThen(&a, context, promise, on_resolve, on_reject,
|
||||
deferred);
|
||||
|
||||
// TODO(gsathya): This is unused, but value is returned according to spec.
|
||||
a.Return(promise);
|
||||
}
|
||||
|
||||
void Builtins::Generate_PromiseThen(compiler::CodeAssemblerState* state) {
|
||||
CodeStubAssembler a(state);
|
||||
typedef compiler::Node Node;
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
|
||||
// 1. Let promise be the this value.
|
||||
Node* const promise = a.Parameter(0);
|
||||
Node* const on_resolve = a.Parameter(1);
|
||||
Node* const on_reject = a.Parameter(2);
|
||||
Node* const context = a.Parameter(5);
|
||||
Isolate* isolate = a.isolate();
|
||||
|
||||
// 2. If IsPromise(promise) is false, throw a TypeError exception.
|
||||
a.ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
|
||||
"Promise.prototype.then");
|
||||
|
||||
Node* const native_context = a.LoadNativeContext(context);
|
||||
Node* const promise_fun =
|
||||
a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
|
||||
|
||||
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
|
||||
Node* constructor =
|
||||
SpeciesConstructor(&a, isolate, context, promise, promise_fun);
|
||||
|
||||
// 4. Let resultCapability be ? NewPromiseCapability(C).
|
||||
Callable call_callable = CodeFactory::Call(isolate);
|
||||
Label fast_promise_capability(&a), promise_capability(&a),
|
||||
perform_promise_then(&a);
|
||||
Variable var_deferred(&a, MachineRepresentation::kTagged);
|
||||
|
||||
a.Branch(a.WordEqual(promise_fun, constructor), &fast_promise_capability,
|
||||
&promise_capability);
|
||||
|
||||
// TODO(gsathya): Remove deferred object and move
|
||||
// NewPromiseCapbability functions to TF.
|
||||
a.Bind(&fast_promise_capability);
|
||||
{
|
||||
// TODO(gsathya): Move this to TF.
|
||||
Node* const promise_internal_capability = a.LoadContextElement(
|
||||
native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX);
|
||||
Node* const capability =
|
||||
a.CallJS(call_callable, context, promise_internal_capability,
|
||||
a.UndefinedConstant());
|
||||
var_deferred.Bind(capability);
|
||||
a.Goto(&perform_promise_then);
|
||||
}
|
||||
|
||||
a.Bind(&promise_capability);
|
||||
{
|
||||
// TODO(gsathya): Move this to TF.
|
||||
Node* const new_promise_capability = a.LoadContextElement(
|
||||
native_context, Context::NEW_PROMISE_CAPABILITY_INDEX);
|
||||
Node* const capability =
|
||||
a.CallJS(call_callable, context, new_promise_capability,
|
||||
a.UndefinedConstant(), constructor);
|
||||
var_deferred.Bind(capability);
|
||||
a.Goto(&perform_promise_then);
|
||||
}
|
||||
|
||||
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
|
||||
// resultCapability).
|
||||
a.Bind(&perform_promise_then);
|
||||
InternalPerformPromiseThen(&a, context, promise, on_resolve, on_reject,
|
||||
var_deferred.value());
|
||||
|
||||
// TODO(gsathya): Protect with debug check.
|
||||
a.CallRuntime(
|
||||
Runtime::kSetProperty, context, promise,
|
||||
a.HeapConstant(isolate->factory()->promise_has_handler_symbol()),
|
||||
a.TrueConstant(), a.SmiConstant(STRICT));
|
||||
|
||||
// TODO(gsathya): This call will be removed once we don't have to
|
||||
// deal with deferred objects.
|
||||
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
|
||||
Node* const key =
|
||||
a.HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise"));
|
||||
Node* const result =
|
||||
a.CallStub(getproperty_callable, context, var_deferred.value(), key);
|
||||
|
||||
a.Return(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -567,6 +567,9 @@ namespace internal {
|
||||
CPP(CreateResolvingFunctions) \
|
||||
CPP(PromiseResolveClosure) \
|
||||
CPP(PromiseRejectClosure) \
|
||||
TFJ(PromiseThen, 2) \
|
||||
TFJ(PromiseCreateAndSet, 2) \
|
||||
TFJ(PerformPromiseThen, 4) \
|
||||
\
|
||||
/* Proxy */ \
|
||||
CPP(ProxyConstructor) \
|
||||
|
113
src/contexts.h
113
src/contexts.h
@ -63,60 +63,67 @@ enum ContextLookupFlags {
|
||||
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)
|
||||
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
|
||||
V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \
|
||||
V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set)
|
||||
|
||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
|
||||
V(ARRAY_POP_INDEX, JSFunction, array_pop) \
|
||||
V(ARRAY_PUSH_INDEX, JSFunction, array_push) \
|
||||
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
|
||||
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
|
||||
V(ARRAY_SLICE_INDEX, JSFunction, array_slice) \
|
||||
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
|
||||
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
|
||||
V(ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX, JSFunction, \
|
||||
async_function_await_caught) \
|
||||
V(ASYNC_FUNCTION_AWAIT_UNCAUGHT_INDEX, JSFunction, \
|
||||
async_function_await_uncaught) \
|
||||
V(ASYNC_FUNCTION_PROMISE_CREATE_INDEX, JSFunction, \
|
||||
async_function_promise_create) \
|
||||
V(ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, JSFunction, \
|
||||
async_function_promise_release) \
|
||||
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
|
||||
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
|
||||
V(ERROR_TO_STRING, JSFunction, error_to_string) \
|
||||
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
|
||||
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
|
||||
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
||||
V(MAP_DELETE_METHOD_INDEX, JSFunction, map_delete) \
|
||||
V(MAP_GET_METHOD_INDEX, JSFunction, map_get) \
|
||||
V(MAP_HAS_METHOD_INDEX, JSFunction, map_has) \
|
||||
V(MAP_SET_METHOD_INDEX, JSFunction, map_set) \
|
||||
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
|
||||
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
||||
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
|
||||
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
|
||||
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
||||
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
|
||||
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
|
||||
promise_has_user_defined_reject_handler) \
|
||||
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
|
||||
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
|
||||
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
|
||||
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
|
||||
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
|
||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
|
||||
V(REJECT_PROMISE_NO_DEBUG_EVENT_INDEX, JSFunction, \
|
||||
reject_promise_no_debug_event) \
|
||||
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
|
||||
V(SET_ADD_METHOD_INDEX, JSFunction, set_add) \
|
||||
V(SET_DELETE_METHOD_INDEX, JSFunction, set_delete) \
|
||||
V(SET_HAS_METHOD_INDEX, JSFunction, set_has) \
|
||||
V(SYNTAX_ERROR_FUNCTION_INDEX, JSFunction, syntax_error_function) \
|
||||
V(TYPE_ERROR_FUNCTION_INDEX, JSFunction, type_error_function) \
|
||||
V(URI_ERROR_FUNCTION_INDEX, JSFunction, uri_error_function) \
|
||||
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
|
||||
wasm_compile_error_function) \
|
||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
|
||||
V(ARRAY_POP_INDEX, JSFunction, array_pop) \
|
||||
V(ARRAY_PUSH_INDEX, JSFunction, array_push) \
|
||||
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
|
||||
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
|
||||
V(ARRAY_SLICE_INDEX, JSFunction, array_slice) \
|
||||
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
|
||||
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
|
||||
V(ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX, JSFunction, \
|
||||
async_function_await_caught) \
|
||||
V(ASYNC_FUNCTION_AWAIT_UNCAUGHT_INDEX, JSFunction, \
|
||||
async_function_await_uncaught) \
|
||||
V(ASYNC_FUNCTION_PROMISE_CREATE_INDEX, JSFunction, \
|
||||
async_function_promise_create) \
|
||||
V(ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, JSFunction, \
|
||||
async_function_promise_release) \
|
||||
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
|
||||
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
|
||||
V(ERROR_TO_STRING, JSFunction, error_to_string) \
|
||||
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
|
||||
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
||||
V(MAP_DELETE_METHOD_INDEX, JSFunction, map_delete) \
|
||||
V(MAP_GET_METHOD_INDEX, JSFunction, map_get) \
|
||||
V(MAP_HAS_METHOD_INDEX, JSFunction, map_has) \
|
||||
V(MAP_SET_METHOD_INDEX, JSFunction, map_set) \
|
||||
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
|
||||
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
|
||||
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
||||
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
|
||||
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
|
||||
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
||||
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
|
||||
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
|
||||
promise_has_user_defined_reject_handler) \
|
||||
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
|
||||
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
|
||||
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
|
||||
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
|
||||
V(PROMISE_ID_RESOLVE_HANDLER_INDEX, JSFunction, promise_id_resolve_handler) \
|
||||
V(PROMISE_ID_REJECT_HANDLER_INDEX, JSFunction, promise_id_reject_handler) \
|
||||
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
|
||||
V(NEW_PROMISE_CAPABILITY_INDEX, JSFunction, new_promise_capability) \
|
||||
V(INTERNAL_PROMISE_CAPABILITY_INDEX, JSFunction, \
|
||||
internal_promise_capability) \
|
||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
|
||||
V(REJECT_PROMISE_NO_DEBUG_EVENT_INDEX, JSFunction, \
|
||||
reject_promise_no_debug_event) \
|
||||
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
|
||||
V(SET_ADD_METHOD_INDEX, JSFunction, set_add) \
|
||||
V(SET_DELETE_METHOD_INDEX, JSFunction, set_delete) \
|
||||
V(SET_HAS_METHOD_INDEX, JSFunction, set_has) \
|
||||
V(SYNTAX_ERROR_FUNCTION_INDEX, JSFunction, syntax_error_function) \
|
||||
V(TYPE_ERROR_FUNCTION_INDEX, JSFunction, type_error_function) \
|
||||
V(URI_ERROR_FUNCTION_INDEX, JSFunction, uri_error_function) \
|
||||
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
|
||||
wasm_compile_error_function) \
|
||||
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, wasm_runtime_error_function)
|
||||
|
||||
#define NATIVE_CONTEXT_JS_ARRAY_ITERATOR_MAPS(V) \
|
||||
|
@ -13,8 +13,6 @@ var IsNaN = global.isNaN;
|
||||
var JSONStringify = global.JSON.stringify;
|
||||
var MapEntries;
|
||||
var MapIteratorNext;
|
||||
var promiseStateSymbol = utils.ImportNow("promise_state_symbol");
|
||||
var promiseResultSymbol = utils.ImportNow("promise_result_symbol");
|
||||
var SetIteratorNext;
|
||||
var SetValues;
|
||||
|
||||
@ -1267,7 +1265,7 @@ inherits(PromiseMirror, ObjectMirror);
|
||||
|
||||
|
||||
function PromiseGetStatus_(value) {
|
||||
var status = %DebugGetProperty(value, promiseStateSymbol);
|
||||
var status = %PromiseStatus(value);
|
||||
if (status == 0) return "pending";
|
||||
if (status == 1) return "resolved";
|
||||
return "rejected";
|
||||
@ -1275,7 +1273,7 @@ function PromiseGetStatus_(value) {
|
||||
|
||||
|
||||
function PromiseGetValue_(value) {
|
||||
return %DebugGetProperty(value, promiseResultSymbol);
|
||||
return %PromiseResult(value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -213,16 +213,10 @@
|
||||
V(premonomorphic_symbol) \
|
||||
V(promise_async_stack_id_symbol) \
|
||||
V(promise_debug_marker_symbol) \
|
||||
V(promise_deferred_reaction_symbol) \
|
||||
V(promise_forwarding_handler_symbol) \
|
||||
V(promise_fulfill_reactions_symbol) \
|
||||
V(promise_handled_by_symbol) \
|
||||
V(promise_handled_hint_symbol) \
|
||||
V(promise_has_handler_symbol) \
|
||||
V(promise_raw_symbol) \
|
||||
V(promise_reject_reactions_symbol) \
|
||||
V(promise_result_symbol) \
|
||||
V(promise_state_symbol) \
|
||||
V(sealed_symbol) \
|
||||
V(stack_trace_symbol) \
|
||||
V(strict_function_transition_symbol) \
|
||||
|
@ -3176,23 +3176,15 @@ void Isolate::PromiseReactionJob(Handle<PromiseReactionJobInfo> info,
|
||||
Handle<Object> tasks(info->tasks(), this);
|
||||
Handle<JSFunction> promise_handle_fn = promise_handle();
|
||||
Handle<Object> undefined = factory()->undefined_value();
|
||||
Handle<Object> deferred(info->deferred(), this);
|
||||
|
||||
// If tasks is an array we have multiple onFulfilled/onRejected callbacks
|
||||
// associated with the promise. The deferred object for each callback
|
||||
// is attached to this array as well.
|
||||
// Otherwise, there is a single callback and the deferred object is attached
|
||||
// directly to PromiseReactionJobInfo.
|
||||
if (tasks->IsJSArray()) {
|
||||
Handle<JSArray> array = Handle<JSArray>::cast(tasks);
|
||||
DCHECK(array->length()->IsSmi());
|
||||
int length = Smi::cast(array->length())->value();
|
||||
ElementsAccessor* accessor = array->GetElementsAccessor();
|
||||
DCHECK(length % 2 == 0);
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
DCHECK(accessor->HasElement(array, i));
|
||||
DCHECK(accessor->HasElement(array, i + 1));
|
||||
Handle<Object> argv[] = {value, accessor->Get(array, i),
|
||||
accessor->Get(array, i + 1)};
|
||||
if (deferred->IsFixedArray()) {
|
||||
DCHECK(tasks->IsFixedArray());
|
||||
Handle<FixedArray> deferred_arr = Handle<FixedArray>::cast(deferred);
|
||||
Handle<FixedArray> tasks_arr = Handle<FixedArray>::cast(tasks);
|
||||
for (int i = 0; i < deferred_arr->length(); i++) {
|
||||
Handle<Object> argv[] = {value, handle(tasks_arr->get(i), this),
|
||||
handle(deferred_arr->get(i), this)};
|
||||
*result = Execution::TryCall(this, promise_handle_fn, undefined,
|
||||
arraysize(argv), argv, maybe_exception);
|
||||
// If execution is terminating, just bail out.
|
||||
@ -3201,7 +3193,6 @@ void Isolate::PromiseReactionJob(Handle<PromiseReactionJobInfo> info,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Handle<Object> deferred(info->deferred(), this);
|
||||
Handle<Object> argv[] = {value, tasks, deferred};
|
||||
*result = Execution::TryCall(this, promise_handle_fn, undefined,
|
||||
arraysize(argv), argv, maybe_exception);
|
||||
|
@ -14,7 +14,6 @@
|
||||
var AsyncFunctionNext;
|
||||
var AsyncFunctionThrow;
|
||||
var CreateInternalPromiseCapability;
|
||||
var PerformPromiseThen;
|
||||
var PromiseCreate;
|
||||
var PromiseNextMicrotaskID;
|
||||
var RejectPromise;
|
||||
@ -24,7 +23,6 @@ utils.Import(function(from) {
|
||||
AsyncFunctionNext = from.AsyncFunctionNext;
|
||||
AsyncFunctionThrow = from.AsyncFunctionThrow;
|
||||
CreateInternalPromiseCapability = from.CreateInternalPromiseCapability;
|
||||
PerformPromiseThen = from.PerformPromiseThen;
|
||||
PromiseCreate = from.PromiseCreate;
|
||||
RejectPromise = from.RejectPromise;
|
||||
ResolvePromise = from.ResolvePromise;
|
||||
@ -44,6 +42,7 @@ var promiseHasHandlerSymbol =
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
function PromiseCastResolved(value) {
|
||||
// TODO(caitp): This is non spec compliant. See v8:5694.
|
||||
if (%is_promise(value)) {
|
||||
return value;
|
||||
} else {
|
||||
@ -106,7 +105,7 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) {
|
||||
outerPromise);
|
||||
}
|
||||
|
||||
PerformPromiseThen(promise, onFulfilled, onRejected, throwawayCapability);
|
||||
%perform_promise_then(promise, onFulfilled, onRejected, throwawayCapability);
|
||||
}
|
||||
|
||||
// Called by the parser from the desugaring of 'await' when catch
|
||||
|
@ -20,12 +20,6 @@ var promiseForwardingHandlerSymbol =
|
||||
utils.ImportNow("promise_forwarding_handler_symbol");
|
||||
var promiseHasHandlerSymbol =
|
||||
utils.ImportNow("promise_has_handler_symbol");
|
||||
var promiseRejectReactionsSymbol =
|
||||
utils.ImportNow("promise_reject_reactions_symbol");
|
||||
var promiseFulfillReactionsSymbol =
|
||||
utils.ImportNow("promise_fulfill_reactions_symbol");
|
||||
var promiseDeferredReactionSymbol =
|
||||
utils.ImportNow("promise_deferred_reaction_symbol");
|
||||
var promiseHandledHintSymbol =
|
||||
utils.ImportNow("promise_handled_hint_symbol");
|
||||
var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
|
||||
@ -36,6 +30,7 @@ var speciesSymbol = utils.ImportNow("species_symbol");
|
||||
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
|
||||
var ObjectHasOwnProperty;
|
||||
var GlobalPromise = global.Promise;
|
||||
var PromiseThen = GlobalPromise.prototype.then;
|
||||
|
||||
utils.Import(function(from) {
|
||||
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
|
||||
@ -46,42 +41,6 @@ utils.Import(function(from) {
|
||||
|
||||
// Core functionality.
|
||||
|
||||
function PromiseSet(promise, status, value) {
|
||||
SET_PRIVATE(promise, promiseStateSymbol, status);
|
||||
SET_PRIVATE(promise, promiseResultSymbol, value);
|
||||
|
||||
// There are 3 possible states for the resolve, reject symbols when we add
|
||||
// a new callback --
|
||||
// 1) UNDEFINED -- This is the zero state where there is no callback
|
||||
// registered. When we see this state, we directly attach the callbacks to
|
||||
// the symbol.
|
||||
// 2) !IS_ARRAY -- There is a single callback directly attached to the
|
||||
// symbols. We need to create a new array to store additional callbacks.
|
||||
// 3) IS_ARRAY -- There are multiple callbacks already registered,
|
||||
// therefore we can just push the new callback to the existing array.
|
||||
SET_PRIVATE(promise, promiseFulfillReactionsSymbol, UNDEFINED);
|
||||
SET_PRIVATE(promise, promiseRejectReactionsSymbol, UNDEFINED);
|
||||
|
||||
// This symbol is used only when one deferred needs to be attached. When more
|
||||
// than one deferred need to be attached the promise, we attach them directly
|
||||
// to the promiseFulfillReactionsSymbol and promiseRejectReactionsSymbol and
|
||||
// reset this back to UNDEFINED.
|
||||
SET_PRIVATE(promise, promiseDeferredReactionSymbol, UNDEFINED);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function PromiseCreateAndSet(status, value) {
|
||||
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);
|
||||
}
|
||||
|
||||
function PromiseInit(promise) {
|
||||
return PromiseSet(promise, kPending, UNDEFINED);
|
||||
}
|
||||
|
||||
function PromiseHandle(value, handler, deferred) {
|
||||
var debug_is_active = DEBUG_IS_ACTIVE;
|
||||
try {
|
||||
@ -98,7 +57,6 @@ function PromiseHandle(value, handler, deferred) {
|
||||
// Pass false for debugEvent so .then chaining or throwaway promises
|
||||
// in async functions do not trigger redundant ExceptionEvents.
|
||||
%PromiseReject(deferred.promise, exception, false);
|
||||
PromiseSet(deferred.promise, kRejected, exception);
|
||||
} else {
|
||||
%_Call(deferred.reject, UNDEFINED, exception);
|
||||
}
|
||||
@ -134,34 +92,6 @@ function PromiseDebugGetInfo(deferreds, status) {
|
||||
return [id, name];
|
||||
}
|
||||
|
||||
function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) {
|
||||
var maybeResolveCallbacks =
|
||||
GET_PRIVATE(promise, promiseFulfillReactionsSymbol);
|
||||
if (IS_UNDEFINED(maybeResolveCallbacks)) {
|
||||
SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve);
|
||||
SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject);
|
||||
SET_PRIVATE(promise, promiseDeferredReactionSymbol, deferred);
|
||||
} else if (!IS_ARRAY(maybeResolveCallbacks)) {
|
||||
var resolveCallbacks = new InternalArray();
|
||||
var rejectCallbacks = new InternalArray();
|
||||
var existingDeferred = GET_PRIVATE(promise, promiseDeferredReactionSymbol);
|
||||
|
||||
resolveCallbacks.push(
|
||||
maybeResolveCallbacks, existingDeferred, onResolve, deferred);
|
||||
rejectCallbacks.push(GET_PRIVATE(promise, promiseRejectReactionsSymbol),
|
||||
existingDeferred,
|
||||
onReject,
|
||||
deferred);
|
||||
|
||||
SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks);
|
||||
SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks);
|
||||
SET_PRIVATE(promise, promiseDeferredReactionSymbol, UNDEFINED);
|
||||
} else {
|
||||
maybeResolveCallbacks.push(onResolve, deferred);
|
||||
GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject, deferred);
|
||||
}
|
||||
}
|
||||
|
||||
function PromiseIdResolveHandler(x) { return x; }
|
||||
function PromiseIdRejectHandler(r) { %_ReThrow(r); }
|
||||
SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
|
||||
@ -171,8 +101,9 @@ SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
|
||||
|
||||
// For bootstrapper.
|
||||
|
||||
// This is used by utils and v8-extras.
|
||||
function PromiseCreate() {
|
||||
return PromiseInit(%promise_internal_constructor());
|
||||
return %promise_internal_constructor();
|
||||
}
|
||||
|
||||
// ES#sec-promise-resolve-functions
|
||||
@ -181,7 +112,6 @@ function ResolvePromise(promise, resolution) {
|
||||
if (resolution === promise) {
|
||||
var exception = %make_type_error(kPromiseCyclic, resolution);
|
||||
%PromiseReject(promise, exception, true);
|
||||
PromiseSet(promise, kRejected, exception);
|
||||
return;
|
||||
}
|
||||
if (IS_RECEIVER(resolution)) {
|
||||
@ -190,7 +120,6 @@ function ResolvePromise(promise, resolution) {
|
||||
var then = resolution.then;
|
||||
} catch (e) {
|
||||
%PromiseReject(promise, e, true);
|
||||
PromiseSet(promise, kRejected, e);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -198,18 +127,16 @@ function ResolvePromise(promise, resolution) {
|
||||
// rejected, shortcircuit the resolution procedure by directly
|
||||
// reusing the value from the promise.
|
||||
if (%is_promise(resolution) && then === PromiseThen) {
|
||||
var thenableState = GET_PRIVATE(resolution, promiseStateSymbol);
|
||||
var thenableState = %PromiseStatus(resolution);
|
||||
if (thenableState === kFulfilled) {
|
||||
// This goes inside the if-else to save one symbol lookup in
|
||||
// the slow path.
|
||||
var thenableValue = GET_PRIVATE(resolution, promiseResultSymbol);
|
||||
%PromiseFulfill(promise, kFulfilled, thenableValue,
|
||||
promiseFulfillReactionsSymbol);
|
||||
PromiseSet(promise, kFulfilled, thenableValue);
|
||||
var thenableValue = %PromiseResult(resolution);
|
||||
%PromiseFulfill(promise, kFulfilled, thenableValue);
|
||||
SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
|
||||
return;
|
||||
} else if (thenableState === kRejected) {
|
||||
var thenableValue = GET_PRIVATE(resolution, promiseResultSymbol);
|
||||
var thenableValue = %PromiseResult(resolution);
|
||||
if (!HAS_DEFINED_PRIVATE(resolution, promiseHasHandlerSymbol)) {
|
||||
// Promise has already been rejected, but had no handler.
|
||||
// Revoke previously triggered reject event.
|
||||
@ -217,7 +144,6 @@ function ResolvePromise(promise, resolution) {
|
||||
}
|
||||
// Don't cause a debug event as this case is forwarding a rejection
|
||||
%PromiseReject(promise, thenableValue, false);
|
||||
PromiseSet(promise, kRejected, thenableValue);
|
||||
SET_PRIVATE(resolution, promiseHasHandlerSymbol, true);
|
||||
return;
|
||||
}
|
||||
@ -232,21 +158,17 @@ function ResolvePromise(promise, resolution) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
%PromiseFulfill(promise, kFulfilled, resolution,
|
||||
promiseFulfillReactionsSymbol);
|
||||
PromiseSet(promise, kFulfilled, resolution);
|
||||
%PromiseFulfill(promise, kFulfilled, resolution);
|
||||
}
|
||||
|
||||
// Only used by async-await.js
|
||||
function RejectPromise(promise, reason, debugEvent) {
|
||||
%PromiseReject(promise, reason, debugEvent);
|
||||
PromiseSet(promise, kRejected, reason);
|
||||
}
|
||||
|
||||
// Export to bindings
|
||||
function DoRejectPromise(promise, reason) {
|
||||
%PromiseReject(promise, reason, true);
|
||||
PromiseSet(promise, kRejected, reason);
|
||||
}
|
||||
|
||||
// The resultCapability.promise is only ever fulfilled internally,
|
||||
@ -254,7 +176,7 @@ function DoRejectPromise(promise, reason) {
|
||||
// calling them multiple times.
|
||||
function CreateInternalPromiseCapability() {
|
||||
return {
|
||||
promise: PromiseCreate(),
|
||||
promise: %promise_internal_constructor(),
|
||||
resolve: UNDEFINED,
|
||||
reject: UNDEFINED
|
||||
};
|
||||
@ -265,7 +187,7 @@ function CreateInternalPromiseCapability() {
|
||||
function NewPromiseCapability(C, debugEvent) {
|
||||
if (C === GlobalPromise) {
|
||||
// Optimized case, avoid extra closure.
|
||||
var promise = PromiseCreate();
|
||||
var promise = %promise_internal_constructor();
|
||||
// TODO(gsathya): Remove container for callbacks when this is
|
||||
// moved to CPP/TF.
|
||||
var callbacks = %create_resolving_functions(promise, debugEvent);
|
||||
@ -298,7 +220,7 @@ function PromiseReject(r) {
|
||||
}
|
||||
if (this === GlobalPromise) {
|
||||
// Optimized case, avoid extra closure.
|
||||
var promise = PromiseCreateAndSet(kRejected, r);
|
||||
var promise = %promise_create_and_set(kRejected, r);
|
||||
// Trigger debug events if the debugger is on, as Promise.reject is
|
||||
// equivalent to throwing an exception directly.
|
||||
%PromiseRejectEventFromStack(promise, r);
|
||||
@ -310,55 +232,6 @@ function PromiseReject(r) {
|
||||
}
|
||||
}
|
||||
|
||||
function PerformPromiseThen(promise, onResolve, onReject, resultCapability) {
|
||||
if (!IS_CALLABLE(onResolve)) onResolve = PromiseIdResolveHandler;
|
||||
if (!IS_CALLABLE(onReject)) onReject = PromiseIdRejectHandler;
|
||||
|
||||
var status = GET_PRIVATE(promise, promiseStateSymbol);
|
||||
switch (status) {
|
||||
case kPending:
|
||||
PromiseAttachCallbacks(promise, resultCapability, onResolve, onReject);
|
||||
break;
|
||||
case kFulfilled:
|
||||
%EnqueuePromiseReactionJob(GET_PRIVATE(promise, promiseResultSymbol),
|
||||
onResolve, resultCapability, kFulfilled);
|
||||
break;
|
||||
case kRejected:
|
||||
if (!HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) {
|
||||
// Promise has already been rejected, but had no handler.
|
||||
// Revoke previously triggered reject event.
|
||||
%PromiseRevokeReject(promise);
|
||||
}
|
||||
%EnqueuePromiseReactionJob(GET_PRIVATE(promise, promiseResultSymbol),
|
||||
onReject, resultCapability, kRejected);
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark this promise as having handler.
|
||||
SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
|
||||
return resultCapability.promise;
|
||||
}
|
||||
|
||||
// ES#sec-promise.prototype.then
|
||||
// Promise.prototype.then ( onFulfilled, onRejected )
|
||||
// Multi-unwrapped chaining with thenable coercion.
|
||||
function PromiseThen(onResolve, onReject) {
|
||||
if (!%is_promise(this)) {
|
||||
throw %make_type_error(kNotAPromise, this);
|
||||
}
|
||||
|
||||
var constructor = SpeciesConstructor(this, GlobalPromise);
|
||||
var resultCapability;
|
||||
if (constructor === GlobalPromise) {
|
||||
resultCapability = CreateInternalPromiseCapability();
|
||||
} else {
|
||||
// Pass false for debugEvent so .then chaining does not trigger
|
||||
// redundant ExceptionEvents.
|
||||
resultCapability = NewPromiseCapability(constructor, false);
|
||||
}
|
||||
return PerformPromiseThen(this, onResolve, onReject, resultCapability);
|
||||
}
|
||||
|
||||
// ES#sec-promise.prototype.catch
|
||||
// Promise.prototype.catch ( onRejected )
|
||||
function PromiseCatch(onReject) {
|
||||
@ -377,7 +250,7 @@ function PromiseResolve(x) {
|
||||
|
||||
// Avoid creating resolving functions.
|
||||
if (this === GlobalPromise) {
|
||||
var promise = PromiseCreate();
|
||||
var promise = %promise_internal_constructor();
|
||||
ResolvePromise(promise, x);
|
||||
return promise;
|
||||
}
|
||||
@ -520,8 +393,10 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol);
|
||||
var deferred = GET_PRIVATE(promise, promiseDeferredReactionSymbol);
|
||||
if (!%is_promise(promise)) return false;
|
||||
|
||||
var queue = %PromiseRejectReactions(promise);
|
||||
var deferred = %PromiseDeferred(promise);
|
||||
|
||||
if (IS_UNDEFINED(queue)) return false;
|
||||
|
||||
@ -529,8 +404,8 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
|
||||
return PromiseHasUserDefinedRejectHandlerCheck(queue, deferred);
|
||||
}
|
||||
|
||||
for (var i = 0; i < queue.length; i += 2) {
|
||||
if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) {
|
||||
for (var i = 0; i < queue.length; i++) {
|
||||
if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], deferred[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -568,7 +443,6 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
|
||||
utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
|
||||
|
||||
utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
|
||||
"then", PromiseThen,
|
||||
"catch", PromiseCatch
|
||||
]);
|
||||
|
||||
@ -582,7 +456,11 @@ utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
|
||||
"promise_resolve", ResolvePromise,
|
||||
"promise_then", PromiseThen,
|
||||
"promise_handle", PromiseHandle,
|
||||
"promise_debug_get_info", PromiseDebugGetInfo
|
||||
"promise_debug_get_info", PromiseDebugGetInfo,
|
||||
"new_promise_capability", NewPromiseCapability,
|
||||
"internal_promise_capability", CreateInternalPromiseCapability,
|
||||
"promise_id_resolve_handler", PromiseIdResolveHandler,
|
||||
"promise_id_reject_handler", PromiseIdRejectHandler
|
||||
]);
|
||||
|
||||
// This allows extras to create promises quickly without building extra
|
||||
@ -600,7 +478,6 @@ utils.Export(function(to) {
|
||||
to.PromiseThen = PromiseThen;
|
||||
|
||||
to.CreateInternalPromiseCapability = CreateInternalPromiseCapability;
|
||||
to.PerformPromiseThen = PerformPromiseThen;
|
||||
to.ResolvePromise = ResolvePromise;
|
||||
to.RejectPromise = RejectPromise;
|
||||
});
|
||||
|
@ -104,7 +104,6 @@ void HeapObject::HeapObjectVerify() {
|
||||
case JS_API_OBJECT_TYPE:
|
||||
case JS_SPECIAL_API_OBJECT_TYPE:
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
case JS_PROMISE_TYPE:
|
||||
JSObject::cast(this)->JSObjectVerify();
|
||||
break;
|
||||
case JS_GENERATOR_OBJECT_TYPE:
|
||||
@ -205,6 +204,9 @@ void HeapObject::HeapObjectVerify() {
|
||||
case JS_WEAK_SET_TYPE:
|
||||
JSWeakSet::cast(this)->JSWeakSetVerify();
|
||||
break;
|
||||
case JS_PROMISE_TYPE:
|
||||
JSPromise::cast(this)->JSPromiseVerify();
|
||||
break;
|
||||
case JS_REGEXP_TYPE:
|
||||
JSRegExp::cast(this)->JSRegExpVerify();
|
||||
break;
|
||||
@ -884,6 +886,19 @@ void JSWeakSet::JSWeakSetVerify() {
|
||||
CHECK(table()->IsHashTable() || table()->IsUndefined(GetIsolate()));
|
||||
}
|
||||
|
||||
void JSPromise::JSPromiseVerify() {
|
||||
CHECK(IsJSPromise());
|
||||
JSObjectVerify();
|
||||
Isolate* isolate = GetIsolate();
|
||||
CHECK(result()->IsUndefined(isolate) || result()->IsObject());
|
||||
CHECK(deferred()->IsUndefined(isolate) || deferred()->IsJSObject() ||
|
||||
deferred()->IsFixedArray());
|
||||
CHECK(fulfill_reactions()->IsUndefined(isolate) ||
|
||||
fulfill_reactions()->IsCallable() ||
|
||||
fulfill_reactions()->IsFixedArray());
|
||||
CHECK(reject_reactions()->IsUndefined(isolate) ||
|
||||
reject_reactions()->IsCallable() || reject_reactions()->IsFixedArray());
|
||||
}
|
||||
|
||||
void JSRegExp::JSRegExpVerify() {
|
||||
JSObjectVerify();
|
||||
@ -1011,8 +1026,8 @@ void PromiseReactionJobInfo::PromiseReactionJobInfoVerify() {
|
||||
Isolate* isolate = GetIsolate();
|
||||
CHECK(IsPromiseReactionJobInfo());
|
||||
CHECK(value()->IsObject());
|
||||
CHECK(tasks()->IsJSArray() || tasks()->IsCallable());
|
||||
CHECK(deferred()->IsJSObject() || deferred()->IsUndefined(isolate));
|
||||
CHECK(tasks()->IsFixedArray() || tasks()->IsCallable());
|
||||
CHECK(deferred()->IsFixedArray() || deferred()->IsJSObject());
|
||||
CHECK(debug_id()->IsNumber() || debug_id()->IsUndefined(isolate));
|
||||
CHECK(debug_name()->IsString() || debug_name()->IsUndefined(isolate));
|
||||
CHECK(context()->IsContext());
|
||||
|
@ -858,7 +858,6 @@ bool HeapObject::IsJSArrayBufferView() const {
|
||||
|
||||
TYPE_CHECKER(JSRegExp, JS_REGEXP_TYPE)
|
||||
|
||||
|
||||
template <> inline bool Is<JSArray>(Object* obj) {
|
||||
return obj->IsJSArray();
|
||||
}
|
||||
@ -2094,7 +2093,7 @@ int JSObject::GetHeaderSize(InstanceType type) {
|
||||
case JS_WEAK_SET_TYPE:
|
||||
return JSWeakSet::kSize;
|
||||
case JS_PROMISE_TYPE:
|
||||
return JSObject::kHeaderSize;
|
||||
return JSPromise::kSize;
|
||||
case JS_REGEXP_TYPE:
|
||||
return JSRegExp::kSize;
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
@ -3323,6 +3322,7 @@ CAST_ACCESSOR(JSObject)
|
||||
CAST_ACCESSOR(JSProxy)
|
||||
CAST_ACCESSOR(JSReceiver)
|
||||
CAST_ACCESSOR(JSRegExp)
|
||||
CAST_ACCESSOR(JSPromise)
|
||||
CAST_ACCESSOR(JSSet)
|
||||
CAST_ACCESSOR(JSSetIterator)
|
||||
CAST_ACCESSOR(JSStringIterator)
|
||||
@ -7095,6 +7095,11 @@ void JSTypedArray::set_length(Object* value, WriteBarrierMode mode) {
|
||||
ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset)
|
||||
#endif
|
||||
|
||||
SMI_ACCESSORS(JSPromise, status, kStatusOffset)
|
||||
ACCESSORS(JSPromise, result, Object, kResultOffset)
|
||||
ACCESSORS(JSPromise, deferred, Object, kDeferredOffset)
|
||||
ACCESSORS(JSPromise, fulfill_reactions, Object, kFulfillReactionsOffset)
|
||||
ACCESSORS(JSPromise, reject_reactions, Object, kRejectReactionsOffset)
|
||||
|
||||
ACCESSORS(JSRegExp, data, Object, kDataOffset)
|
||||
ACCESSORS(JSRegExp, flags, Object, kFlagsOffset)
|
||||
|
@ -150,11 +150,13 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
|
||||
case JS_SPECIAL_API_OBJECT_TYPE:
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
case JS_GENERATOR_OBJECT_TYPE:
|
||||
case JS_PROMISE_TYPE:
|
||||
case JS_ARGUMENTS_TYPE:
|
||||
case JS_ERROR_TYPE:
|
||||
JSObject::cast(this)->JSObjectPrint(os);
|
||||
break;
|
||||
case JS_PROMISE_TYPE:
|
||||
JSPromise::cast(this)->JSPromisePrint(os);
|
||||
break;
|
||||
case JS_ARRAY_TYPE:
|
||||
JSArray::cast(this)->JSArrayPrint(os);
|
||||
break;
|
||||
@ -542,6 +544,14 @@ void JSArray::JSArrayPrint(std::ostream& os) { // NOLINT
|
||||
JSObjectPrintBody(os, this);
|
||||
}
|
||||
|
||||
void JSPromise::JSPromisePrint(std::ostream& os) { // NOLINT
|
||||
JSObjectPrintHeader(os, this, "JSPromise");
|
||||
os << "\n - status = " << JSPromise::Status(status());
|
||||
os << "\n - result = " << Brief(result());
|
||||
os << "\n - deferreds = " << Brief(deferred());
|
||||
os << "\n - fulfill_reactions = " << Brief(fulfill_reactions());
|
||||
os << "\n - reject_reactions = " << Brief(reject_reactions());
|
||||
}
|
||||
|
||||
void JSRegExp::JSRegExpPrint(std::ostream& os) { // NOLINT
|
||||
JSObjectPrintHeader(os, this, "JSRegExp");
|
||||
|
@ -16447,6 +16447,19 @@ class StringSharedKey : public HashTableKey {
|
||||
int scope_position_;
|
||||
};
|
||||
|
||||
// static
|
||||
const char* JSPromise::Status(int status) {
|
||||
switch (status) {
|
||||
case kPromiseFulfilled:
|
||||
return "resolved";
|
||||
case kPromisePending:
|
||||
return "pending";
|
||||
case kPromiseRejected:
|
||||
return "rejected";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -8895,6 +8895,41 @@ class JSMessageObject: public JSObject {
|
||||
kSize> BodyDescriptor;
|
||||
};
|
||||
|
||||
class JSPromise : public JSObject {
|
||||
public:
|
||||
DECL_INT_ACCESSORS(status)
|
||||
DECL_ACCESSORS(result, Object)
|
||||
|
||||
// There are 3 possible states for these fields --
|
||||
// 1) Undefined -- This is the zero state when there is no callback
|
||||
// or deferred registered.
|
||||
// 2) Object -- There is a single Callable directly attached to the
|
||||
// fulfill_reactions, reject_reactions and the deferred JSObject is
|
||||
// directly attached to the deferred field.
|
||||
// 3) FixedArray -- There is more than one callback and deferred
|
||||
// attached to a FixedArray.
|
||||
DECL_ACCESSORS(deferred, Object)
|
||||
DECL_ACCESSORS(fulfill_reactions, Object)
|
||||
DECL_ACCESSORS(reject_reactions, Object)
|
||||
|
||||
static const char* Status(int status);
|
||||
|
||||
DECLARE_CAST(JSPromise)
|
||||
|
||||
// Dispatched behavior.
|
||||
DECLARE_PRINTER(JSPromise)
|
||||
DECLARE_VERIFIER(JSPromise)
|
||||
|
||||
// Layout description.
|
||||
static const int kStatusOffset = JSObject::kHeaderSize;
|
||||
static const int kResultOffset = kStatusOffset + kPointerSize;
|
||||
static const int kDeferredOffset = kResultOffset + kPointerSize;
|
||||
static const int kFulfillReactionsOffset = kDeferredOffset + kPointerSize;
|
||||
static const int kRejectReactionsOffset =
|
||||
kFulfillReactionsOffset + kPointerSize;
|
||||
static const int kSize = kRejectReactionsOffset + kPointerSize;
|
||||
};
|
||||
|
||||
// Regular expressions
|
||||
// The regular expression holds a single reference to a FixedArray in
|
||||
// the kDataOffset field.
|
||||
|
@ -136,14 +136,6 @@ static Handle<Object> DebugGetProperty(LookupIterator* it,
|
||||
return it->isolate()->factory()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
static Handle<Object> DebugGetProperty(Handle<Object> object,
|
||||
Handle<Name> name) {
|
||||
LookupIterator it(object, name);
|
||||
return DebugGetProperty(&it);
|
||||
}
|
||||
|
||||
|
||||
template <class IteratorType>
|
||||
static MaybeHandle<JSArray> GetIteratorInternalProperties(
|
||||
Isolate* isolate, Handle<IteratorType> object) {
|
||||
@ -248,24 +240,8 @@ MaybeHandle<JSArray> Runtime::GetInternalProperties(Isolate* isolate,
|
||||
result->set(5, generator->receiver());
|
||||
return factory->NewJSArrayWithElements(result);
|
||||
} else if (object->IsJSPromise()) {
|
||||
Handle<JSObject> promise = Handle<JSObject>::cast(object);
|
||||
|
||||
Handle<Object> status_obj =
|
||||
DebugGetProperty(promise, isolate->factory()->promise_state_symbol());
|
||||
CHECK(status_obj->IsSmi());
|
||||
const char* status = "rejected";
|
||||
int status_val = Handle<Smi>::cast(status_obj)->value();
|
||||
switch (status_val) {
|
||||
case kPromiseFulfilled:
|
||||
status = "resolved";
|
||||
break;
|
||||
case kPromisePending:
|
||||
status = "pending";
|
||||
break;
|
||||
default:
|
||||
DCHECK_EQ(kPromiseRejected, status_val);
|
||||
}
|
||||
|
||||
Handle<JSPromise> promise = Handle<JSPromise>::cast(object);
|
||||
const char* status = JSPromise::Status(promise->status());
|
||||
Handle<FixedArray> result = factory->NewFixedArray(2 * 2);
|
||||
Handle<String> promise_status =
|
||||
factory->NewStringFromAsciiChecked("[[PromiseStatus]]");
|
||||
@ -273,8 +249,7 @@ MaybeHandle<JSArray> Runtime::GetInternalProperties(Isolate* isolate,
|
||||
Handle<String> status_str = factory->NewStringFromAsciiChecked(status);
|
||||
result->set(1, *status_str);
|
||||
|
||||
Handle<Object> value_obj =
|
||||
DebugGetProperty(promise, isolate->factory()->promise_result_symbol());
|
||||
Handle<Object> value_obj(promise->result(), isolate);
|
||||
Handle<String> promise_value =
|
||||
factory->NewStringFromAsciiChecked("[[PromiseValue]]");
|
||||
result->set(2, *promise_value);
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright 2016 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/runtime/runtime-utils.h"
|
||||
|
||||
#include "src/debug/debug.h"
|
||||
@ -66,10 +65,17 @@ void EnqueuePromiseReactionJob(Isolate* isolate, Handle<Object> value,
|
||||
Handle<Object> debug_name = isolate->factory()->undefined_value();
|
||||
if (isolate->debug()->is_active()) {
|
||||
MaybeHandle<Object> maybe_result;
|
||||
Handle<Object> argv[] = {deferred, status};
|
||||
Handle<Object> deferred_obj(deferred);
|
||||
|
||||
if (deferred->IsFixedArray()) {
|
||||
deferred_obj = isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
Handle<Object> argv[] = {deferred_obj, status};
|
||||
maybe_result = Execution::TryCall(
|
||||
isolate, isolate->promise_debug_get_info(),
|
||||
isolate->factory()->undefined_value(), arraysize(argv), argv);
|
||||
|
||||
Handle<Object> result;
|
||||
if ((maybe_result).ToHandle(&result)) {
|
||||
CHECK(result->IsJSArray());
|
||||
@ -88,42 +94,50 @@ void EnqueuePromiseReactionJob(Isolate* isolate, Handle<Object> value,
|
||||
isolate->EnqueueMicrotask(info);
|
||||
}
|
||||
|
||||
void PromiseFulfill(Isolate* isolate, Handle<JSReceiver> promise,
|
||||
Handle<Smi> status, Handle<Object> value,
|
||||
Handle<Symbol> reaction) {
|
||||
Handle<Object> tasks = JSReceiver::GetDataProperty(promise, reaction);
|
||||
if (!tasks->IsUndefined(isolate)) {
|
||||
Handle<Object> deferred = JSReceiver::GetDataProperty(
|
||||
promise, isolate->factory()->promise_deferred_reaction_symbol());
|
||||
void PromiseSet(Handle<JSPromise> promise, int status, Handle<Object> result) {
|
||||
promise->set_status(status);
|
||||
promise->set_result(*result);
|
||||
// TODO(gsathya): reset reactions?
|
||||
}
|
||||
|
||||
void PromiseFulfill(Isolate* isolate, Handle<JSPromise> promise,
|
||||
Handle<Smi> status, Handle<Object> value) {
|
||||
// Check if there are any callbacks.
|
||||
if (!promise->deferred()->IsUndefined(isolate)) {
|
||||
Handle<Object> tasks((status->value() == kPromiseFulfilled)
|
||||
? promise->fulfill_reactions()
|
||||
: promise->reject_reactions(),
|
||||
isolate);
|
||||
Handle<Object> deferred(promise->deferred(), isolate);
|
||||
EnqueuePromiseReactionJob(isolate, value, tasks, deferred, status);
|
||||
}
|
||||
|
||||
PromiseSet(promise, status->value(), value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseReject) {
|
||||
DCHECK(args.length() == 3);
|
||||
HandleScope scope(isolate);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, promise, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(debug_event, 2);
|
||||
|
||||
PromiseRejectEvent(isolate, promise, promise, reason, debug_event);
|
||||
|
||||
Handle<Smi> status = handle(Smi::FromInt(kPromiseRejected), isolate);
|
||||
Handle<Symbol> reaction =
|
||||
isolate->factory()->promise_reject_reactions_symbol();
|
||||
PromiseFulfill(isolate, promise, status, reason, reaction);
|
||||
Handle<Smi> status(Smi::FromInt(kPromiseRejected), isolate);
|
||||
PromiseFulfill(isolate, promise, status, reason);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseFulfill) {
|
||||
DCHECK(args.length() == 4);
|
||||
DCHECK(args.length() == 3);
|
||||
HandleScope scope(isolate);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, promise, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Smi, status, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Symbol, reaction, 3);
|
||||
PromiseFulfill(isolate, promise, status, value, reaction);
|
||||
PromiseFulfill(isolate, promise, status, value);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
@ -192,6 +206,7 @@ RUNTIME_FUNCTION(Runtime_RunMicrotasks) {
|
||||
RUNTIME_FUNCTION(Runtime_CreateResolvingFunctions) {
|
||||
HandleScope scope(isolate);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
|
||||
DCHECK(args.length() == 1);
|
||||
Handle<JSFunction> resolve, reject;
|
||||
|
||||
PromiseUtils::CreateResolvingFunctions(
|
||||
@ -204,5 +219,58 @@ RUNTIME_FUNCTION(Runtime_CreateResolvingFunctions) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseStatus) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
|
||||
return Smi::FromInt(promise->status());
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseResult) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
return promise->result();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseDeferred) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
|
||||
Handle<Object> deferred(promise->deferred(), isolate);
|
||||
if (deferred->IsUndefined(isolate)) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
if (deferred->IsJSObject()) {
|
||||
return *deferred;
|
||||
}
|
||||
|
||||
DCHECK(deferred->IsFixedArray());
|
||||
return *isolate->factory()->NewJSArrayWithElements(
|
||||
Handle<FixedArray>::cast(deferred));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseRejectReactions) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
|
||||
Handle<Object> reject_reactions(promise->reject_reactions(), isolate);
|
||||
if (reject_reactions->IsUndefined(isolate)) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
if (reject_reactions->IsJSObject()) {
|
||||
return *reject_reactions;
|
||||
}
|
||||
|
||||
DCHECK(reject_reactions->IsFixedArray());
|
||||
return *isolate->factory()->NewJSArrayWithElements(
|
||||
Handle<FixedArray>::cast(reject_reactions));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -304,10 +304,14 @@ namespace internal {
|
||||
F(NewSyntaxError, 2, 1) \
|
||||
F(NewTypeError, 2, 1) \
|
||||
F(OrdinaryHasInstance, 2, 1) \
|
||||
F(PromiseDeferred, 1, 1) \
|
||||
F(PromiseReject, 3, 1) \
|
||||
F(PromiseFulfill, 4, 1) \
|
||||
F(PromiseFulfill, 3, 1) \
|
||||
F(PromiseRejectEventFromStack, 2, 1) \
|
||||
F(PromiseRejectReactions, 1, 1) \
|
||||
F(PromiseRevokeReject, 1, 1) \
|
||||
F(PromiseResult, 1, 1) \
|
||||
F(PromiseStatus, 1, 1) \
|
||||
F(PromoteScheduledException, 0, 1) \
|
||||
F(ReThrow, 1, 1) \
|
||||
F(RunMicrotasks, 0, 1) \
|
||||
|
@ -1099,10 +1099,8 @@ TEST(SubclassPromiseBuiltin) {
|
||||
CcTest::InitializeVM();
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
|
||||
const int first_field = 5;
|
||||
TestSubclassBuiltin("A1", JS_PROMISE_TYPE, "Promise",
|
||||
"function(resolve, reject) { resolve('ok'); }",
|
||||
first_field);
|
||||
"function(resolve, reject) { resolve('ok'); }");
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user