-- 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:
gsathya 2016-12-05 13:08:16 -08:00 committed by Commit bot
parent 7036eec6f7
commit 30b564c76f
18 changed files with 740 additions and 305 deletions

View File

@ -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),

View File

@ -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

View File

@ -567,6 +567,9 @@ namespace internal {
CPP(CreateResolvingFunctions) \
CPP(PromiseResolveClosure) \
CPP(PromiseRejectClosure) \
TFJ(PromiseThen, 2) \
TFJ(PromiseCreateAndSet, 2) \
TFJ(PerformPromiseThen, 4) \
\
/* Proxy */ \
CPP(ProxyConstructor) \

View File

@ -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) \

View File

@ -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);
}

View File

@ -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) \

View File

@ -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);

View File

@ -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

View File

@ -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;
});

View File

@ -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());

View File

@ -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)

View File

@ -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");

View File

@ -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 {

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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) \

View File

@ -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'); }");
}