*/
bool IsRegExp() const;
+ /**
+ * Returns true if this value is a Promise.
+ * This is an experimental feature.
+ */
+ bool IsPromise() const;
/**
* Returns true if this value is an ArrayBuffer.
static void CheckCast(Value* obj);
};
+
+/**
+ * An instance of the built-in Promise constructor (ES6 draft).
+ * This API is experimental. Only works with --harmony flag.
+ */
+class V8_EXPORT Promise : public Object {
+ public:
+ /**
+ * Create a new Promise in pending state.
+ */
+ static Local<Promise> New(Isolate* isolate);
+
+ /**
+ * Resolve/reject a promise with a given value.
+ * Ignored if the promise is not unresolved.
+ */
+ void Resolve(Handle<Value> value);
+ void Reject(Handle<Value> value);
+
+ /**
+ * Register a resolution/rejection handler with a promise.
+ * The handler is given the respective resolution/rejection value as
+ * an argument. If the promise is already resolved/rejected, the handler is
+ * invoked at the end of turn.
+ */
+ Local<Promise> Chain(Handle<Function> handler);
+ Local<Promise> Catch(Handle<Function> handler);
+
+ V8_INLINE static Promise* Cast(Value* obj);
+
+ private:
+ Promise();
+ static void CheckCast(Value* obj);
+};
+
+
#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
// The number of required internal fields can be defined by embedder.
#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
}
+Promise* Promise::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Promise*>(value);
+}
+
+
ArrayBuffer* ArrayBuffer::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
}
+void v8::Promise::CheckCast(Value* that) {
+ Utils::ApiCheck(that->IsPromise(),
+ "v8::Promise::Cast()",
+ "Could not convert to promise");
+}
+
+
void v8::ArrayBuffer::CheckCast(Value* that) {
i::Handle<i::Object> obj = Utils::OpenHandle(that);
Utils::ApiCheck(obj->IsJSArrayBuffer(),
}
+bool Value::IsPromise() const {
+ i::Handle<i::Object> val = Utils::OpenHandle(this);
+ if (!i::FLAG_harmony_promises || !val->IsJSObject()) return false;
+ i::Handle<i::JSObject> obj = i::Handle<i::JSObject>::cast(val);
+ i::Isolate* isolate = obj->GetIsolate();
+ LOG_API(isolate, "IsPromise");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> argv[] = { obj };
+ i::Handle<i::Object> b = i::Execution::Call(
+ isolate,
+ handle(
+ isolate->context()->global_object()->native_context()->is_promise()),
+ isolate->factory()->undefined_value(),
+ ARRAY_SIZE(argv), argv,
+ &has_pending_exception,
+ false);
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return b->BooleanValue();
+}
+
+
+Local<Promise> Promise::New(Isolate* v8_isolate) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
+ LOG_API(isolate, "Promise::New");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result = i::Execution::Call(
+ isolate,
+ handle(isolate->context()->global_object()->native_context()->
+ promise_create()),
+ isolate->factory()->undefined_value(),
+ 0, NULL,
+ &has_pending_exception,
+ false);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Promise>());
+ return Local<Promise>::Cast(Utils::ToLocal(result));
+}
+
+
+void Promise::Resolve(Handle<Value> value) {
+ i::Handle<i::JSObject> promise = Utils::OpenHandle(this);
+ i::Isolate* isolate = promise->GetIsolate();
+ LOG_API(isolate, "Promise::Resolve");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> argv[] = { promise, Utils::OpenHandle(*value) };
+ i::Execution::Call(
+ isolate,
+ handle(isolate->context()->global_object()->native_context()->
+ promise_resolve()),
+ isolate->factory()->undefined_value(),
+ ARRAY_SIZE(argv), argv,
+ &has_pending_exception,
+ false);
+ EXCEPTION_BAILOUT_CHECK(isolate, /* void */ ;);
+}
+
+
+void Promise::Reject(Handle<Value> value) {
+ i::Handle<i::JSObject> promise = Utils::OpenHandle(this);
+ i::Isolate* isolate = promise->GetIsolate();
+ LOG_API(isolate, "Promise::Reject");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> argv[] = { promise, Utils::OpenHandle(*value) };
+ i::Execution::Call(
+ isolate,
+ handle(isolate->context()->global_object()->native_context()->
+ promise_reject()),
+ isolate->factory()->undefined_value(),
+ ARRAY_SIZE(argv), argv,
+ &has_pending_exception,
+ false);
+ EXCEPTION_BAILOUT_CHECK(isolate, /* void */ ;);
+}
+
+
+Local<Promise> Promise::Chain(Handle<Function> handler) {
+ i::Handle<i::JSObject> promise = Utils::OpenHandle(this);
+ i::Isolate* isolate = promise->GetIsolate();
+ LOG_API(isolate, "Promise::Chain");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> argv[] = { Utils::OpenHandle(*handler) };
+ i::Handle<i::Object> result = i::Execution::Call(
+ isolate,
+ handle(isolate->context()->global_object()->native_context()->
+ promise_chain()),
+ promise,
+ ARRAY_SIZE(argv), argv,
+ &has_pending_exception,
+ false);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Promise>());
+ return Local<Promise>::Cast(Utils::ToLocal(result));
+}
+
+
+Local<Promise> Promise::Catch(Handle<Function> handler) {
+ i::Handle<i::JSObject> promise = Utils::OpenHandle(this);
+ i::Isolate* isolate = promise->GetIsolate();
+ LOG_API(isolate, "Promise::Catch");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> argv[] = { Utils::OpenHandle(*handler) };
+ i::Handle<i::Object> result = i::Execution::Call(
+ isolate,
+ handle(isolate->context()->global_object()->native_context()->
+ promise_catch()),
+ promise,
+ ARRAY_SIZE(argv), argv,
+ &has_pending_exception,
+ false);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Promise>());
+ return Local<Promise>::Cast(Utils::ToLocal(result));
+}
+
+
bool v8::ArrayBuffer::IsExternal() const {
return Utils::OpenHandle(this)->is_external();
}
INSTALL_NATIVE(JSFunction, "EnqueueExternalMicrotask",
enqueue_external_microtask);
+ if (FLAG_harmony_promises) {
+ INSTALL_NATIVE(JSFunction, "IsPromise", is_promise);
+ INSTALL_NATIVE(JSFunction, "PromiseCreate", promise_create);
+ INSTALL_NATIVE(JSFunction, "PromiseResolve", promise_resolve);
+ INSTALL_NATIVE(JSFunction, "PromiseReject", promise_reject);
+ INSTALL_NATIVE(JSFunction, "PromiseChain", promise_chain);
+ INSTALL_NATIVE(JSFunction, "PromiseCatch", promise_catch);
+ }
+
if (FLAG_harmony_proxies) {
INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap);
INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap);
error_message_for_code_gen_from_strings) \
V(RUN_MICROTASKS_INDEX, JSFunction, run_microtasks) \
V(ENQUEUE_EXTERNAL_MICROTASK_INDEX, JSFunction, enqueue_external_microtask) \
+ V(IS_PROMISE_INDEX, JSFunction, is_promise) \
+ V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
+ V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
+ V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
+ V(PROMISE_CHAIN_INDEX, JSFunction, promise_chain) \
+ V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
V(TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX, JSFunction, \
to_complete_property_descriptor) \
V(DERIVED_HAS_TRAP_INDEX, JSFunction, derived_has_trap) \
ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX,
RUN_MICROTASKS_INDEX,
ENQUEUE_EXTERNAL_MICROTASK_INDEX,
+ IS_PROMISE_INDEX,
+ PROMISE_CREATE_INDEX,
+ PROMISE_RESOLVE_INDEX,
+ PROMISE_REJECT_INDEX,
+ PROMISE_CHAIN_INDEX,
+ PROMISE_CATCH_INDEX,
TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX,
DERIVED_HAS_TRAP_INDEX,
DERIVED_GET_TRAP_INDEX,
}
+// For API.
+
+function PromiseNopResolver() {}
+
+function PromiseCreate() {
+ return new Promise(PromiseNopResolver)
+}
+
+
// Convenience.
function PromiseDeferred() {
/* ES5 */ \
F(ObjectFreeze, 1, 1) \
\
- /* Harmony Microtasks */ \
+ /* Harmony microtasks */ \
F(GetMicrotaskState, 0, 1) \
\
/* Harmony modules */ \
CHECK_EQ("V8.Test", last_event_message);
CHECK_EQ(1, last_event_status);
}
+
+
+TEST(Promises) {
+ i::FLAG_harmony_promises = true;
+
+ LocalContext context;
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::HandleScope scope(isolate);
+ Handle<Object> global = context->Global();
+
+ // Creation.
+ Handle<v8::Promise> p = v8::Promise::New(isolate);
+ Handle<v8::Promise> r = v8::Promise::New(isolate);
+
+ // IsPromise predicate.
+ CHECK(p->IsPromise());
+ CHECK(r->IsPromise());
+ Handle<Value> o = v8::Object::New(isolate);
+ CHECK(!o->IsPromise());
+
+ // Resolution and rejection.
+ p->Resolve(v8::Integer::New(isolate, 1));
+ CHECK(p->IsPromise());
+ r->Reject(v8::Integer::New(isolate, 2));
+ CHECK(r->IsPromise());
+
+ // Chaining non-pending promises.
+ CompileRun(
+ "var x1 = 0;\n"
+ "var x2 = 0;\n"
+ "function f1(x) { x1 = x; return x+1 };\n"
+ "function f2(x) { x2 = x; return x+1 };\n");
+ Handle<Function> f1 = Handle<Function>::Cast(global->Get(v8_str("f1")));
+ Handle<Function> f2 = Handle<Function>::Cast(global->Get(v8_str("f2")));
+
+ p->Chain(f1);
+ CHECK_EQ(0, global->Get(v8_str("x1"))->Int32Value());
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(1, global->Get(v8_str("x1"))->Int32Value());
+
+ p->Catch(f2);
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(0, global->Get(v8_str("x2"))->Int32Value());
+
+ r->Catch(f2);
+ CHECK_EQ(0, global->Get(v8_str("x2"))->Int32Value());
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(2, global->Get(v8_str("x2"))->Int32Value());
+
+ r->Chain(f1);
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(1, global->Get(v8_str("x1"))->Int32Value());
+
+ // Chaining pending promises.
+ CompileRun("x1 = x2 = 0;");
+ p = v8::Promise::New(isolate);
+ r = v8::Promise::New(isolate);
+
+ p->Chain(f1);
+ r->Catch(f2);
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(0, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(0, global->Get(v8_str("x2"))->Int32Value());
+
+ p->Resolve(v8::Integer::New(isolate, 1));
+ r->Reject(v8::Integer::New(isolate, 2));
+ CHECK_EQ(0, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(0, global->Get(v8_str("x2"))->Int32Value());
+
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(1, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(2, global->Get(v8_str("x2"))->Int32Value());
+
+ // Multi-chaining.
+ CompileRun("x1 = x2 = 0;");
+ p = v8::Promise::New(isolate);
+ p->Chain(f1)->Chain(f2);
+ p->Resolve(v8::Integer::New(isolate, 3));
+ CHECK_EQ(0, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(0, global->Get(v8_str("x2"))->Int32Value());
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(3, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(4, global->Get(v8_str("x2"))->Int32Value());
+
+ CompileRun("x1 = x2 = 0;");
+ r = v8::Promise::New(isolate);
+ r->Catch(f1)->Chain(f2);
+ r->Reject(v8::Integer::New(isolate, 3));
+ CHECK_EQ(0, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(0, global->Get(v8_str("x2"))->Int32Value());
+ V8::RunMicrotasks(isolate);
+ CHECK_EQ(3, global->Get(v8_str("x1"))->Int32Value());
+ CHECK_EQ(4, global->Get(v8_str("x2"))->Int32Value());
+}