Skip to content

Commit

Permalink
[api] add API for Promise status and result.
Browse files Browse the repository at this point in the history
Currently, to find out a Promise's status and result, one has to use the
debug context. This is for example done in Node.js. This new API is a
better replacement, also in the context of the debug context being
deprecated eventually.

R=franzih@chromium.org, gsathya@chromium.org, jochen@chromium.org
BUG=v8:5764

Review-Url: https://codereview.chromium.org/2589113002
Cr-Commit-Position: refs/heads/master@{#41855}
  • Loading branch information
hashseed authored and Commit bot committed Dec 20, 2016
1 parent 55c88e5 commit 2843258
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 29 deletions.
17 changes: 17 additions & 0 deletions include/v8.h
Original file line number Diff line number Diff line change
Expand Up @@ -3699,6 +3699,12 @@ class V8_EXPORT Function : public Object {
*/
class V8_EXPORT Promise : public Object {
public:
/**
* State of the promise. Each value corresponds to one of the possible values
* of the [[PromiseState]] field.
*/
enum PromiseState { kPending, kFulfilled, kRejected };

class V8_EXPORT Resolver : public Object {
public:
/**
Expand Down Expand Up @@ -3755,6 +3761,17 @@ class V8_EXPORT Promise : public Object {
*/
bool HasHandler();

/**
* Returns the content of the [[PromiseResult]] field. The Promise must not
* be pending.
*/
Local<Value> Result();

/**
* Returns the value of the [[PromiseState]] field.
*/
PromiseState State();

V8_INLINE static Promise* Cast(Value* obj);

private:
Expand Down
18 changes: 18 additions & 0 deletions src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7289,6 +7289,24 @@ bool Promise::HasHandler() {
return false;
}

Local<Value> Promise::Result() {
i::Handle<i::JSReceiver> promise = Utils::OpenHandle(this);
i::Isolate* isolate = promise->GetIsolate();
LOG_API(isolate, Promise, Result);
i::Handle<i::JSPromise> js_promise = i::Handle<i::JSPromise>::cast(promise);
Utils::ApiCheck(js_promise->status() != kPending, "v8_Promise_Result",
"Promise is still pending");
i::Handle<i::Object> result(js_promise->result(), isolate);
return Utils::ToLocal(result);
}

Promise::PromiseState Promise::State() {
i::Handle<i::JSReceiver> promise = Utils::OpenHandle(this);
i::Isolate* isolate = promise->GetIsolate();
LOG_API(isolate, Promise, Status);
i::Handle<i::JSPromise> js_promise = i::Handle<i::JSPromise>::cast(promise);
return static_cast<PromiseState>(js_promise->status());
}

Local<Object> Proxy::GetTarget() {
i::Handle<i::JSProxy> self = Utils::OpenHandle(this);
Expand Down
17 changes: 9 additions & 8 deletions src/builtins/builtins-promise.cc
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context,
{
Label fulfilled_check(this);
Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset);
GotoUnless(SmiEqual(status, SmiConstant(kPromisePending)),
GotoUnless(SmiEqual(status, SmiConstant(v8::Promise::kPending)),
&fulfilled_check);

Node* const existing_deferred =
Expand Down Expand Up @@ -304,12 +304,13 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context,
{
Label reject(this);
Node* const result = LoadObjectField(promise, JSPromise::kResultOffset);
GotoUnless(WordEqual(status, SmiConstant(kPromiseFulfilled)), &reject);
GotoUnless(WordEqual(status, SmiConstant(v8::Promise::kFulfilled)),
&reject);

// TODO(gsathya): Move this to TF.
CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, result,
var_on_resolve.value(), deferred,
SmiConstant(kPromiseFulfilled));
SmiConstant(v8::Promise::kFulfilled));
Goto(&out);

Bind(&reject);
Expand All @@ -326,7 +327,7 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context,
{
CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise,
result, var_on_reject.value(), deferred,
SmiConstant(kPromiseRejected));
SmiConstant(v8::Promise::kRejected));

Goto(&out);
}
Expand Down Expand Up @@ -415,7 +416,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
LoadObjectField(result, JSPromise::kResultOffset);

Label if_isnotpending(this);
GotoUnless(SmiEqual(SmiConstant(kPromisePending), thenable_status),
GotoUnless(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status),
&if_isnotpending);

// TODO(gsathya): Use a marker here instead of the actual then
Expand All @@ -430,13 +431,13 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Bind(&if_isnotpending);
{
Label if_fulfilled(this), if_rejected(this);
Branch(SmiEqual(SmiConstant(kPromiseFulfilled), thenable_status),
Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status),
&if_fulfilled, &if_rejected);

Bind(&if_fulfilled);
{
CallRuntime(Runtime::kPromiseFulfill, context, promise,
SmiConstant(kPromiseFulfilled), thenable_value);
SmiConstant(v8::Promise::kFulfilled), thenable_value);
PromiseSetHasHandler(promise);
Goto(out);
}
Expand Down Expand Up @@ -507,7 +508,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Bind(&fulfill);
{
CallRuntime(Runtime::kPromiseFulfill, context, promise,
SmiConstant(kPromiseFulfilled), result);
SmiConstant(v8::Promise::kFulfilled), result);
Goto(out);
}

Expand Down
2 changes: 1 addition & 1 deletion src/code-stub-assembler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8340,7 +8340,7 @@ Node* CodeStubAssembler::AllocateJSPromise(Node* context) {

void CodeStubAssembler::PromiseInit(Node* promise) {
StoreObjectField(promise, JSPromise::kStatusOffset,
SmiConstant(kPromisePending));
SmiConstant(v8::Promise::kPending));
StoreObjectField(promise, JSPromise::kFlagsOffset, SmiConstant(0));
}

Expand Down
2 changes: 2 additions & 0 deletions src/counters.h
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,8 @@ class RuntimeCallTimer final {
V(Promise_HasRejectHandler) \
V(Promise_Resolver_New) \
V(Promise_Resolver_Resolve) \
V(Promise_Result) \
V(Promise_Status) \
V(Promise_Then) \
V(Proxy_New) \
V(RangeError_New) \
Expand Down
8 changes: 0 additions & 8 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,6 @@ enum ParseRestriction {
ONLY_SINGLE_FUNCTION_LITERAL // Only a single FunctionLiteral expression.
};

// TODO(gsathya): Move this to JSPromise once we create it.
// This should be in sync with the constants in promise.js
enum PromiseStatus {
kPromisePending,
kPromiseFulfilled,
kPromiseRejected,
};

// A CodeDesc describes a buffer holding instructions and relocation
// information. The instructions start at the beginning of the buffer
// and grow forward, the relocation information starts at the end of
Expand Down
3 changes: 2 additions & 1 deletion src/js/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@
define kRegExpPrototypeOldFlagGetter = 31;

# [[PromiseState]] values:
# These values should be kept in sync with PromiseStatus in globals.h
# These values must be kept in sync with v8::Promise::PromiseState in
# include/v8.h
define kPending = 0;
define kFulfilled = 1;
define kRejected = 2;
Expand Down
6 changes: 3 additions & 3 deletions src/objects.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16484,11 +16484,11 @@ class StringSharedKey : public HashTableKey {
// static
const char* JSPromise::Status(int status) {
switch (status) {
case kPromiseFulfilled:
case v8::Promise::kFulfilled:
return "resolved";
case kPromisePending:
case v8::Promise::kPending:
return "pending";
case kPromiseRejected:
case v8::Promise::kRejected:
return "rejected";
}
UNREACHABLE();
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/runtime-promise.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ 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)
Handle<Object> tasks((status->value() == v8::Promise::kFulfilled)
? promise->fulfill_reactions()
: promise->reject_reactions(),
isolate);
Expand All @@ -131,7 +131,7 @@ RUNTIME_FUNCTION(Runtime_PromiseReject) {

PromiseRejectEvent(isolate, promise, promise, reason, debug_event);

Handle<Smi> status(Smi::FromInt(kPromiseRejected), isolate);
Handle<Smi> status(Smi::FromInt(v8::Promise::kRejected), isolate);
PromiseFulfill(isolate, promise, status, reason);
return isolate->heap()->undefined_value();
}
Expand Down
19 changes: 19 additions & 0 deletions test/cctest/test-api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24352,6 +24352,25 @@ TEST(PromiseThen) {
.FromJust());
}

TEST(PromiseStateAndValue) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"var resolver;"
"new Promise((res, rej) => { resolver = res; })");
v8::Local<v8::Promise> promise = v8::Local<v8::Promise>::Cast(result);
CHECK(promise->State() == v8::Promise::PromiseState::kPending);

CompileRun("resolver('fulfilled')");
CHECK(promise->State() == v8::Promise::PromiseState::kFulfilled);
CHECK(v8_str("fulfilled")->SameValue(promise->Result()));

result = CompileRun("Promise.reject('rejected')");
promise = v8::Local<v8::Promise>::Cast(result);
CHECK(promise->State() == v8::Promise::PromiseState::kRejected);
CHECK(v8_str("rejected")->SameValue(promise->Result()));
}

TEST(DisallowJavascriptExecutionScope) {
LocalContext context;
Expand Down
12 changes: 6 additions & 6 deletions test/cctest/test-code-stub-assembler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1863,7 +1863,7 @@ TEST(PromiseInit) {
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result->IsJSPromise());
Handle<JSPromise> js_promise = Handle<JSPromise>::cast(result);
CHECK_EQ(kPromisePending, js_promise->status());
CHECK_EQ(v8::Promise::kPending, js_promise->status());
CHECK_EQ(isolate->heap()->undefined_value(), js_promise->result());
CHECK(!js_promise->has_handler());
}
Expand All @@ -1877,7 +1877,7 @@ TEST(PromiseSet) {

Node* const context = m.Parameter(kNumParams + 2);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
m.PromiseSet(promise, m.SmiConstant(v8::Promise::kPending), m.SmiConstant(1));
m.Return(promise);

Handle<Code> code = data.GenerateCode();
Expand All @@ -1888,7 +1888,7 @@ TEST(PromiseSet) {
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK(result->IsJSPromise());
Handle<JSPromise> js_promise = Handle<JSPromise>::cast(result);
CHECK_EQ(kPromisePending, js_promise->status());
CHECK_EQ(v8::Promise::kPending, js_promise->status());
CHECK_EQ(Smi::FromInt(1), js_promise->result());
CHECK(!js_promise->has_handler());
}
Expand Down Expand Up @@ -2007,7 +2007,7 @@ TEST(CreatePromiseResolvingFunctionsContext) {
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
m.PromiseSet(promise, m.SmiConstant(v8::Promise::kPending), m.SmiConstant(1));
Node* const promise_context = m.CreatePromiseResolvingFunctionsContext(
promise, m.BooleanConstant(false), native_context);
m.Return(promise_context);
Expand Down Expand Up @@ -2039,7 +2039,7 @@ TEST(CreatePromiseResolvingFunctions) {
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
m.PromiseSet(promise, m.SmiConstant(v8::Promise::kPending), m.SmiConstant(1));
Node *resolve, *reject;
std::tie(resolve, reject) = m.CreatePromiseResolvingFunctions(
promise, m.BooleanConstant(false), native_context);
Expand Down Expand Up @@ -2071,7 +2071,7 @@ TEST(AllocateFunctionWithMapAndContext) {
Node* const context = m.Parameter(kNumParams + 2);
Node* const native_context = m.LoadNativeContext(context);
Node* const promise = m.AllocateJSPromise(context);
m.PromiseSet(promise, m.SmiConstant(kPromisePending), m.SmiConstant(1));
m.PromiseSet(promise, m.SmiConstant(v8::Promise::kPending), m.SmiConstant(1));
Node* promise_context = m.CreatePromiseResolvingFunctionsContext(
promise, m.BooleanConstant(false), native_context);
Node* resolve_info =
Expand Down

0 comments on commit 2843258

Please sign in to comment.