Commit 4f95a1eb authored by caitp's avatar caitp Committed by Commit bot

[promises] port NewPromiseCapability to TF

- Adds CodeAssembler::ConstructJS() to simplify calling JS functions as
constructors, used by NewPromiseCapability()
- Defines PromiseCapability as a special JSObject subclass, with a
non-exensible Map, and read-only non-configurable DataDescriptors which
point to its in-object fields. This allows its fields to be used by JS
builtins until there is no longer any need.

Currently, the performance benefit comes from
https://codereview.chromium.org/2567033003/, but does not appear to
regress performance in any significant way.

BUG=v8:5343
TBR=ulan@chromium.org

Review-Url: https://codereview.chromium.org/2567333002
Cr-Commit-Position: refs/heads/master@{#42014}
parent 587fda09
......@@ -258,6 +258,7 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
case JS_WEAK_MAP_TYPE:
case JS_WEAK_SET_TYPE:
case JS_PROMISE_CAPABILITY_TYPE:
case JS_PROMISE_TYPE:
case JS_BOUND_FUNCTION_TYPE:
DCHECK(!map->is_undetectable());
......
......@@ -1843,6 +1843,24 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
to_primitive->shared()->set_length(1);
}
{
Handle<Code> code = isolate->builtins()->PromiseGetCapabilitiesExecutor();
Handle<SharedFunctionInfo> info =
factory->NewSharedFunctionInfo(factory->empty_string(), code, true);
info->SetConstructStub(*isolate->builtins()->JSBuiltinsConstructStub());
info->set_instance_class_name(isolate->heap()->Object_string());
info->set_internal_formal_parameter_count(2);
info->set_length(2);
native_context()->set_promise_get_capabilities_executor_shared_fun(*info);
// %new_promise_capability(C, debugEvent)
Handle<JSFunction> new_promise_capability =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kNewPromiseCapability, 2, false);
InstallWithIntrinsicDefaultProto(isolate, new_promise_capability,
Context::NEW_PROMISE_CAPABILITY_INDEX);
}
{ // -- P r o m i s e
// Set catch prediction
Handle<Code> promise_code = isolate->builtins()->PromiseConstructor();
......@@ -1972,12 +1990,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
info->set_length(1);
native_context()->set_promise_reject_shared_fun(*info);
}
Handle<JSFunction> create_resolving_functions =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kCreateResolvingFunctions, 2, false);
native_context()->set_create_resolving_functions(
*create_resolving_functions);
}
{ // -- R e g E x p
......
This diff is collapsed.
......@@ -45,11 +45,18 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified,
Label* if_ismodified);
Node* CreatePromiseContext(Node* native_context, int slots);
Node* CreatePromiseResolvingFunctionsContext(Node* promise, Node* debug_event,
Node* native_context);
std::pair<Node*, Node*> CreatePromiseResolvingFunctions(
Node* promise, Node* native_context, Node* promise_context);
Node* CreatePromiseGetCapabilitiesExecutorContext(Node* native_context,
Node* promise_capability);
Node* NewPromiseCapability(Node* context, Node* constructor,
Node* debug_event = nullptr);
};
} // namespace internal
......
......@@ -618,10 +618,11 @@ namespace internal {
TFS(ForInFilter, BUILTIN, kNoExtraICState, ForInFilter) \
\
/* Promise */ \
TFJ(PromiseGetCapabilitiesExecutor, 2) \
TFJ(NewPromiseCapability, 2) \
TFJ(PromiseConstructor, 1) \
TFJ(PromiseInternalConstructor, 1) \
TFJ(IsPromise, 1) \
TFJ(CreateResolvingFunctions, 2) \
TFJ(PromiseResolveClosure, 1) \
CPP(PromiseRejectClosure) \
TFJ(PromiseThen, 2) \
......
......@@ -28,6 +28,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(CodeMap, CodeMap) \
V(empty_string, EmptyString) \
V(EmptyFixedArray, EmptyFixedArray) \
V(EmptyLiteralsArray, EmptyLiteralsArray) \
V(FalseValue, False) \
V(FixedArrayMap, FixedArrayMap) \
V(FixedCOWArrayMap, FixedCOWArrayMap) \
......@@ -1232,8 +1233,23 @@ class CodeStubArguments {
#ifdef DEBUG
#define CSA_ASSERT(csa, x) \
(csa)->Assert([&] { return (x); }, #x, __FILE__, __LINE__)
#define CSA_ASSERT_JS_ARGC_OP(csa, Op, op, expected) \
(csa)->Assert( \
[&] { \
const CodeAssemblerState* state = (csa)->state(); \
/* See Linkage::GetJSCallDescriptor(). */ \
int argc_index = state->parameter_count() - 2; \
compiler::Node* const argc = (csa)->Parameter(argc_index); \
return (csa)->Op(argc, (csa)->Int32Constant(expected)); \
}, \
"argc " #op " " #expected, __FILE__, __LINE__)
#define CSA_ASSERT_JS_ARGC_EQ(csa, expected) \
CSA_ASSERT_JS_ARGC_OP(csa, Word32Equal, ==, expected)
#else
#define CSA_ASSERT(csa, x) ((void)0)
#define CSA_ASSERT_JS_ARGC_EQ(csa, expected) ((void)0)
#endif
#ifdef ENABLE_SLOW_DCHECKS
......
......@@ -72,6 +72,10 @@ CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone,
CodeAssemblerState::~CodeAssemblerState() {}
int CodeAssemblerState::parameter_count() const {
return static_cast<int>(raw_assembler_->call_descriptor()->ParameterCount());
}
CodeAssembler::~CodeAssembler() {}
class BreakOnNodeDecorator final : public GraphDecorator {
......
......@@ -352,6 +352,18 @@ class V8_EXPORT_PRIVATE CodeAssembler {
return CallStub(callable, context, function, arity, receiver, args...);
}
template <class... TArgs>
Node* ConstructJS(Callable const& callable, Node* context, Node* new_target,
TArgs... args) {
int argc = static_cast<int>(sizeof...(args));
Node* arity = Int32Constant(argc);
Node* receiver = LoadRoot(Heap::kUndefinedValueRootIndex);
// Construct(target, new_target, arity, receiver, arguments...)
return CallStub(callable, context, new_target, new_target, arity, receiver,
args...);
}
// Call to a C function with two arguments.
Node* CallCFunction2(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, Node* function, Node* arg0,
......@@ -469,6 +481,7 @@ class V8_EXPORT_PRIVATE CodeAssemblerState {
~CodeAssemblerState();
const char* name() const { return name_; }
int parameter_count() const;
private:
friend class CodeAssembler;
......
......@@ -263,6 +263,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case JS_WEAK_MAP_TYPE:
case JS_WEAK_SET_TYPE:
case JS_PROMISE_CAPABILITY_TYPE:
case JS_PROMISE_TYPE:
DCHECK(!map->is_callable());
DCHECK(!map->is_undetectable());
......
......@@ -34,42 +34,42 @@ enum ContextLookupFlags {
// must always be allocated via Heap::AllocateContext() or
// Factory::NewContext.
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
V(IS_ARRAYLIKE, JSFunction, is_arraylike) \
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
V(GET_TEMPLATE_CALL_SITE_INDEX, JSFunction, get_template_call_site) \
V(MAKE_ERROR_INDEX, JSFunction, make_error) \
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
V(OBJECT_CREATE, JSFunction, object_create) \
V(OBJECT_DEFINE_PROPERTIES, JSFunction, object_define_properties) \
V(OBJECT_DEFINE_PROPERTY, JSFunction, object_define_property) \
V(OBJECT_FREEZE, JSFunction, object_freeze) \
V(OBJECT_GET_PROTOTYPE_OF, JSFunction, object_get_prototype_of) \
V(OBJECT_IS_EXTENSIBLE, JSFunction, object_is_extensible) \
V(OBJECT_IS_FROZEN, JSFunction, object_is_frozen) \
V(OBJECT_IS_SEALED, JSFunction, object_is_sealed) \
V(OBJECT_KEYS, JSFunction, object_keys) \
V(REGEXP_INTERNAL_MATCH, JSFunction, regexp_internal_match) \
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
V(REFLECT_DEFINE_PROPERTY_INDEX, JSFunction, reflect_define_property) \
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
V(MATH_POW_INDEX, JSFunction, math_pow) \
V(CREATE_RESOLVING_FUNCTION_INDEX, JSFunction, create_resolving_functions) \
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
promise_internal_constructor) \
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \
V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set) \
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
V(IS_ARRAYLIKE, JSFunction, is_arraylike) \
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
V(GET_TEMPLATE_CALL_SITE_INDEX, JSFunction, get_template_call_site) \
V(MAKE_ERROR_INDEX, JSFunction, make_error) \
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
V(OBJECT_CREATE, JSFunction, object_create) \
V(OBJECT_DEFINE_PROPERTIES, JSFunction, object_define_properties) \
V(OBJECT_DEFINE_PROPERTY, JSFunction, object_define_property) \
V(OBJECT_FREEZE, JSFunction, object_freeze) \
V(OBJECT_GET_PROTOTYPE_OF, JSFunction, object_get_prototype_of) \
V(OBJECT_IS_EXTENSIBLE, JSFunction, object_is_extensible) \
V(OBJECT_IS_FROZEN, JSFunction, object_is_frozen) \
V(OBJECT_IS_SEALED, JSFunction, object_is_sealed) \
V(OBJECT_KEYS, JSFunction, object_keys) \
V(REGEXP_INTERNAL_MATCH, JSFunction, regexp_internal_match) \
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
V(REFLECT_DEFINE_PROPERTY_INDEX, JSFunction, reflect_define_property) \
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
V(MATH_POW_INDEX, JSFunction, math_pow) \
V(NEW_PROMISE_CAPABILITY_INDEX, JSFunction, new_promise_capability) \
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
promise_internal_constructor) \
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) \
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
V(PROMISE_HANDLE_REJECT_INDEX, JSFunction, promise_handle_reject)
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
......@@ -110,9 +110,6 @@ enum ContextLookupFlags {
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
V(PROMISE_ID_RESOLVE_HANDLER_INDEX, JSFunction, promise_id_resolve_handler) \
V(PROMISE_ID_REJECT_HANDLER_INDEX, JSFunction, promise_id_reject_handler) \
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) \
......@@ -289,6 +286,8 @@ enum ContextLookupFlags {
V(PROXY_FUNCTION_INDEX, JSFunction, proxy_function) \
V(PROXY_FUNCTION_MAP_INDEX, Map, proxy_function_map) \
V(PROXY_MAP_INDEX, Map, proxy_map) \
V(PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN, SharedFunctionInfo, \
promise_get_capabilities_executor_shared_fun) \
V(PROMISE_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
promise_resolve_shared_fun) \
V(PROMISE_REJECT_SHARED_FUN, SharedFunctionInfo, promise_reject_shared_fun) \
......
......@@ -2270,6 +2270,9 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, optimized_out);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, stale_register);
ALLOCATE_MAP(JS_PROMISE_CAPABILITY_TYPE, JSPromiseCapability::kSize,
js_promise_capability);
for (unsigned i = 0; i < arraysize(string_type_table); i++) {
const StringTypeTable& entry = string_type_table[i];
{
......@@ -2876,6 +2879,42 @@ void Heap::CreateInitialObjects() {
// Initialize compilation cache.
isolate_->compilation_cache()->Clear();
// Finish creating JSPromiseCapabilityMap
{
// TODO(caitp): This initialization can be removed once PromiseCapability
// object is no longer used by builtins implemented in javascript.
Handle<Map> map = factory->js_promise_capability_map();
map->set_inobject_properties_or_constructor_function_index(3);
Map::EnsureDescriptorSlack(map, 3);
PropertyAttributes attrs =
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
{ // promise
Descriptor d = Descriptor::DataField(factory->promise_string(),
JSPromiseCapability::kPromiseIndex,
attrs, Representation::Tagged());
map->AppendDescriptor(&d);
}
{ // resolve
Descriptor d = Descriptor::DataField(factory->resolve_string(),
JSPromiseCapability::kResolveIndex,
attrs, Representation::Tagged());
map->AppendDescriptor(&d);
}
{ // reject
Descriptor d = Descriptor::DataField(factory->reject_string(),
JSPromiseCapability::kRejectIndex,
attrs, Representation::Tagged());
map->AppendDescriptor(&d);
}
map->set_is_extensible(false);
set_js_promise_capability_map(*map);
}
}
bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) {
......
......@@ -227,7 +227,10 @@ using v8::MemoryPressureLevel;
V(Map, exception_map, ExceptionMap) \
V(Map, termination_exception_map, TerminationExceptionMap) \
V(Map, optimized_out_map, OptimizedOutMap) \
V(Map, stale_register_map, StaleRegisterMap)
V(Map, stale_register_map, StaleRegisterMap) \
/* per-Isolate map for JSPromiseCapability. */ \
/* TODO(caitp): Make this a Struct */ \
V(Map, js_promise_capability_map, JSPromiseCapabilityMap)
// Entries in this list are limited to Smis and are not visited during GC.
#define SMI_ROOT_LIST(V) \
......
......@@ -158,6 +158,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
case JS_GENERIC_ARRAY_VALUE_ITERATOR_TYPE:
case JS_PROMISE_CAPABILITY_TYPE:
case JS_PROMISE_TYPE:
case JS_BOUND_FUNCTION_TYPE:
return GetVisitorIdForSize(kVisitJSObject, kVisitJSObjectGeneric,
......
......@@ -79,36 +79,6 @@ function DoRejectPromise(promise, reason) {
%PromiseReject(promise, reason, true);
}
// ES#sec-newpromisecapability
// NewPromiseCapability ( C )
function NewPromiseCapability(C, debugEvent) {
if (C === GlobalPromise) {
// Optimized case, avoid extra closure.
var promise = %promise_internal_constructor(UNDEFINED);
// TODO(gsathya): Remove container for callbacks when this is
// moved to CPP/TF.
var callbacks = %create_resolving_functions(promise, debugEvent);
return {
promise: promise,
resolve: callbacks[kResolveCallback],
reject: callbacks[kRejectCallback]
};
}
var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED };
result.promise = new C((resolve, reject) => {
if (!IS_UNDEFINED(result.resolve) || !IS_UNDEFINED(result.reject))
throw %make_type_error(kPromiseExecutorAlreadyInvoked);
result.resolve = resolve;
result.reject = reject;
});
if (!IS_CALLABLE(result.resolve) || !IS_CALLABLE(result.reject))
throw %make_type_error(kPromiseNonCallable);
return result;
}
// ES#sec-promise.reject
// Promise.reject ( x )
function PromiseReject(r) {
......@@ -123,7 +93,7 @@ function PromiseReject(r) {
%PromiseRejectEventFromStack(promise, r);
return promise;
} else {
var promiseCapability = NewPromiseCapability(this, true);
var promiseCapability = %new_promise_capability(this, true);
%_Call(promiseCapability.reject, UNDEFINED, r);
return promiseCapability.promise;
}
......@@ -147,7 +117,7 @@ function PromiseResolve(x) {
}
// debugEvent is not so meaningful here as it will be resolved
var promiseCapability = NewPromiseCapability(this, true);
var promiseCapability = %new_promise_capability(this, true);
%_Call(promiseCapability.resolve, UNDEFINED, x);
return promiseCapability.promise;
}
......@@ -161,7 +131,7 @@ function PromiseAll(iterable) {
// false debugEvent so that forwarding the rejection through all does not
// trigger redundant ExceptionEvents
var deferred = NewPromiseCapability(this, false);
var deferred = %new_promise_capability(this, false);
var resolutions = new InternalArray();
var count;
......@@ -225,7 +195,7 @@ function PromiseRace(iterable) {
// false debugEvent so that forwarding the rejection through race does not
// trigger redundant ExceptionEvents
var deferred = NewPromiseCapability(this, false);
var deferred = %new_promise_capability(this, false);
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
......@@ -270,7 +240,6 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
// TODO(gsathya): Remove this once we update the promise builtin.
"promise_internal_reject", RejectPromise,
"promise_debug_get_info", PromiseDebugGetInfo,
"new_promise_capability", NewPromiseCapability,
"promise_id_resolve_handler", PromiseIdResolveHandler,
"promise_id_reject_handler", PromiseIdRejectHandler
]);
......@@ -286,7 +255,6 @@ utils.InstallFunctions(extrasUtils, 0, [
utils.Export(function(to) {
to.PromiseCreate = PromiseCreate;
to.RejectPromise = RejectPromise;
});
......
......@@ -465,6 +465,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3) {
case JS_OBJECT_TYPE:
case JS_ERROR_TYPE:
case JS_ARGUMENTS_TYPE:
case JS_PROMISE_CAPABILITY_TYPE:
case JS_PROMISE_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
......
......@@ -201,6 +201,9 @@ void HeapObject::HeapObjectVerify() {
case JS_WEAK_SET_TYPE:
JSWeakSet::cast(this)->JSWeakSetVerify();
break;
case JS_PROMISE_CAPABILITY_TYPE:
JSPromiseCapability::cast(this)->JSPromiseCapabilityVerify();
break;
case JS_PROMISE_TYPE:
JSPromise::cast(this)->JSPromiseVerify();
break;
......@@ -883,6 +886,14 @@ void JSWeakSet::JSWeakSetVerify() {
CHECK(table()->IsHashTable() || table()->IsUndefined(GetIsolate()));
}
void JSPromiseCapability::JSPromiseCapabilityVerify() {
CHECK(IsJSPromiseCapability());
JSObjectVerify();
VerifyPointer(promise());
VerifyPointer(resolve());
VerifyPointer(reject());
}
void JSPromise::JSPromiseVerify() {
CHECK(IsJSPromise());
JSObjectVerify();
......
......@@ -153,6 +153,7 @@ TYPE_CHECKER(JSMap, JS_MAP_TYPE)
TYPE_CHECKER(JSMapIterator, JS_MAP_ITERATOR_TYPE)
TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE)
TYPE_CHECKER(JSModuleNamespace, JS_MODULE_NAMESPACE_TYPE)
TYPE_CHECKER(JSPromiseCapability, JS_PROMISE_CAPABILITY_TYPE)
TYPE_CHECKER(JSPromise, JS_PROMISE_TYPE)
TYPE_CHECKER(JSRegExp, JS_REGEXP_TYPE)
TYPE_CHECKER(JSSet, JS_SET_TYPE)
......@@ -640,6 +641,7 @@ CAST_ACCESSOR(JSObject)
CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSReceiver)
CAST_ACCESSOR(JSRegExp)
CAST_ACCESSOR(JSPromiseCapability)
CAST_ACCESSOR(JSPromise)
CAST_ACCESSOR(JSSet)
CAST_ACCESSOR(JSSetIterator)
......@@ -2173,6 +2175,8 @@ int JSObject::GetHeaderSize(InstanceType type) {
return JSWeakMap::kSize;
case JS_WEAK_SET_TYPE:
return JSWeakSet::kSize;
case JS_PROMISE_CAPABILITY_TYPE:
return JSPromiseCapability::kSize;
case JS_PROMISE_TYPE:
return JSPromise::kSize;
case JS_REGEXP_TYPE:
......@@ -7094,6 +7098,10 @@ void JSTypedArray::set_length(Object* value, WriteBarrierMode mode) {
ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset)
#endif
ACCESSORS(JSPromiseCapability, promise, Object, kPromiseOffset)
ACCESSORS(JSPromiseCapability, resolve, Object, kResolveOffset)
ACCESSORS(JSPromiseCapability, reject, Object, kRejectOffset)
SMI_ACCESSORS(JSPromise, status, kStatusOffset)
ACCESSORS(JSPromise, result, Object, kResultOffset)
ACCESSORS(JSPromise, deferred_promise, Object, kDeferredPromiseOffset)
......
......@@ -152,6 +152,7 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
case JS_GENERATOR_OBJECT_TYPE:
case JS_ARGUMENTS_TYPE:
case JS_ERROR_TYPE:
case JS_PROMISE_CAPABILITY_TYPE:
JSObject::cast(this)->JSObjectPrint(os);
break;
case JS_PROMISE_TYPE:
......
......@@ -422,6 +422,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(JS_MAP_ITERATOR_TYPE) \
V(JS_WEAK_MAP_TYPE) \
V(JS_WEAK_SET_TYPE) \
V(JS_PROMISE_CAPABILITY_TYPE) \
V(JS_PROMISE_TYPE) \
V(JS_REGEXP_TYPE) \
V(JS_ERROR_TYPE) \
......@@ -767,6 +768,7 @@ enum InstanceType {
JS_MAP_ITERATOR_TYPE,
JS_WEAK_MAP_TYPE,
JS_WEAK_SET_TYPE,
JS_PROMISE_CAPABILITY_TYPE,
JS_PROMISE_TYPE,
JS_REGEXP_TYPE,
JS_ERROR_TYPE,
......@@ -1089,6 +1091,7 @@ template <class C> inline bool Is(Object* obj);
V(JSDataView) \
V(JSProxy) \
V(JSError) \
V(JSPromiseCapability) \
V(JSPromise) \
V(JSStringIterator) \
V(JSSet) \
......@@ -8638,6 +8641,36 @@ class JSMessageObject: public JSObject {
kSize> BodyDescriptor;
};
class JSPromise;
// TODO(caitp): Make this a Struct once properties are no longer accessed from
// JS
class JSPromiseCapability : public JSObject {
public:
DECLARE_CAST(JSPromiseCapability)
DECLARE_VERIFIER(JSPromiseCapability)
DECL_ACCESSORS(promise, Object)
DECL_ACCESSORS(resolve, Object)
DECL_ACCESSORS(reject, Object)
static const int kPromiseOffset = JSObject::kHeaderSize;
static const int kResolveOffset = kPromiseOffset + kPointerSize;
static const int kRejectOffset = kResolveOffset + kPointerSize;
static const int kSize = kRejectOffset + kPointerSize;
enum InObjectPropertyIndex {
kPromiseIndex,
kResolveIndex,
kRejectIndex,
kInObjectPropertyCount // Dummy.
};
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSPromiseCapability);
};
class JSPromise : public JSObject {
public:
DECL_INT_ACCESSORS(status)
......
......@@ -40,6 +40,16 @@ class PromiseUtils : public AllStatic {
Handle<JSFunction>* resolve,
Handle<JSFunction>* reject);
};
class GetPromiseCapabilityExecutor : public AllStatic {
public:
enum FunctionContextSlot {
kCapabilitySlot = Context::MIN_CONTEXT_SLOTS,
kContextLength,
};
};
} // namespace internal
} // namespace v8
......
......@@ -2070,5 +2070,154 @@ TEST(AllocateFunctionWithMapAndContext) {
CHECK_EQ(isolate->heap()->undefined_value(), fun->next_function_link());
}
TEST(CreatePromiseGetCapabilitiesExecutorContext) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler m(data.state());
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const map = m.LoadRoot(Heap::kJSPromiseCapabilityMapRootIndex);
Node* const capability = m.AllocateJSObjectFromMap(map);
m.StoreObjectFieldNoWriteBarrier(
capability, JSPromiseCapability::kPromiseOffset, m.UndefinedConstant());
m.StoreObjectFieldNoWriteBarrier(
capability, JSPromiseCapability::kResolveOffset, m.UndefinedConstant());
m.StoreObjectFieldNoWriteBarrier(
capability, JSPromiseCapability::kRejectOffset, m.UndefinedConstant());
Node* const executor_context =
m.CreatePromiseGetCapabilitiesExecutorContext(capability, native_context);
m.Return(executor_context);
Handle<Code> code = data.GenerateCode();
CHECK(!code.is_null());
FunctionTester ft(code, kNumParams);
Handle<Object> result_obj =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result_obj->IsContext());
Handle<Context> context_js = Handle<Context>::cast(result_obj);
CHECK_EQ(GetPromiseCapabilityExecutor::kContextLength, context_js->length());
CHECK_EQ(isolate->native_context()->closure(), context_js->closure());
CHECK_EQ(isolate->heap()->the_hole_value(), context_js->extension());
CHECK_EQ(*isolate->native_context(), context_js->native_context());
CHECK(context_js->get(GetPromiseCapabilityExecutor::kCapabilitySlot)
->IsJSPromiseCapability());
}
TEST(NewPromiseCapability) {
Isolate* isolate(CcTest::InitIsolateOnce());
{ // Builtin Promise
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler m(data.state());
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise_constructor =
m.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const capability =
m.NewPromiseCapability(context, promise_constructor);
m.Return(capability);
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
Handle<Object> result_obj =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result_obj->IsJSPromiseCapability());
Handle<JSPromiseCapability> result =
Handle<JSPromiseCapability>::cast(result_obj);
CHECK(result->promise()->IsJSPromise());
CHECK(result->resolve()->IsJSFunction());
CHECK(result->reject()->IsJSFunction());
CHECK_EQ(isolate->native_context()->promise_resolve_shared_fun(),
JSFunction::cast(result->resolve())->shared());
CHECK_EQ(isolate->native_context()->promise_reject_shared_fun(),
JSFunction::cast(result->reject())->shared());
Handle<JSFunction> callbacks[] = {
handle(JSFunction::cast(result->resolve())),
handle(JSFunction::cast(result->reject()))};
for (auto&& callback : callbacks) {
Handle<Context> context(Context::cast(callback->context()));
CHECK_EQ(isolate->native_context()->closure(), context->closure());
CHECK_EQ(isolate->heap()->the_hole_value(), context->extension());
CHECK_EQ(*isolate->native_context(), context->native_context());
CHECK_EQ(PromiseUtils::kPromiseContextLength, context->length());
CHECK_EQ(context->get(PromiseUtils::kPromiseSlot), result->promise());
}
}
{ // Custom Promise
const int kNumParams = 2;
CodeAssemblerTester data(isolate, kNumParams);
PromiseBuiltinsAssembler m(data.state());
Node* const context = m.Parameter(kNumParams + 2);
Node* const constructor = m.Parameter(1);
Node* const capability = m.NewPromiseCapability(context, constructor);
m.Return(capability);
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
Handle<JSFunction> constructor_fn =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*CompileRun(
"(function FakePromise(executor) {"
" var self = this;"
" function resolve(value) { self.resolvedValue = value; }"
" function reject(reason) { self.rejectedReason = reason; }"
" executor(resolve, reject);"
"})")));
Handle<Object> result_obj =
ft.Call(isolate->factory()->undefined_value(), constructor_fn)
.ToHandleChecked();
CHECK(result_obj->IsJSPromiseCapability());
Handle<JSPromiseCapability> result =
Handle<JSPromiseCapability>::cast(result_obj);
CHECK(result->promise()->IsJSObject());
Handle<JSObject> promise(JSObject::cast(result->promise()));
CHECK_EQ(constructor_fn->prototype_or_initial_map(), promise->map());
CHECK(result->resolve()->IsJSFunction());
CHECK(result->reject()->IsJSFunction());
Handle<String> resolved_str =
isolate->factory()->NewStringFromAsciiChecked("resolvedStr");
Handle<String> rejected_str =
isolate->factory()->NewStringFromAsciiChecked("rejectedStr");
Handle<Object> argv1[] = {resolved_str};
Handle<Object> ret =
Execution::Call(isolate, handle(result->resolve(), isolate),
isolate->factory()->undefined_value(), 1, argv1)
.ToHandleChecked();
Handle<Object> prop1 =
JSReceiver::GetProperty(isolate, promise, "resolvedValue")
.ToHandleChecked();
CHECK_EQ(*resolved_str, *prop1);
Handle<Object> argv2[] = {rejected_str};
ret = Execution::Call(isolate, handle(result->reject(), isolate),
isolate->factory()->undefined_value(), 1, argv2)
.ToHandleChecked();
Handle<Object> prop2 =
JSReceiver::GetProperty(isolate, promise, "rejectedReason")
.ToHandleChecked();
CHECK_EQ(*rejected_str, *prop2);
}
}
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment