Revision: 19811
Author:   [email protected]
Date:     Tue Mar 11 16:17:20 2014 UTC
Log:      API support for promises

[email protected]
BUG=

Review URL: https://codereview.chromium.org/194663003
http://code.google.com/p/v8/source/detail?r=19811

Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/contexts.h
 /branches/bleeding_edge/src/promise.js
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/cctest/test-api.cc

=======================================
--- /branches/bleeding_edge/include/v8.h        Mon Mar 10 08:56:48 2014 UTC
+++ /branches/bleeding_edge/include/v8.h        Tue Mar 11 16:17:20 2014 UTC
@@ -1412,6 +1412,11 @@
    */
   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.
@@ -2536,6 +2541,42 @@
   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
@@ -6187,6 +6228,14 @@
 #endif
   return static_cast<Array*>(value);
 }
+
+
+Promise* Promise::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Promise*>(value);
+}


 ArrayBuffer* ArrayBuffer::Cast(v8::Value* value) {
=======================================
--- /branches/bleeding_edge/src/api.cc  Tue Mar 11 14:41:22 2014 UTC
+++ /branches/bleeding_edge/src/api.cc  Tue Mar 11 16:17:20 2014 UTC
@@ -2649,6 +2649,13 @@
                   "v8::Array::Cast()",
                   "Could not convert to array");
 }
+
+
+void v8::Promise::CheckCast(Value* that) {
+  Utils::ApiCheck(that->IsPromise(),
+                  "v8::Promise::Cast()",
+                  "Could not convert to promise");
+}


 void v8::ArrayBuffer::CheckCast(Value* that) {
@@ -5729,6 +5736,124 @@
   EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>());
   return Utils::ToLocal(result);
 }
+
+
+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 {
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Tue Mar 11 14:41:22 2014 UTC
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue Mar 11 16:17:20 2014 UTC
@@ -1592,6 +1592,15 @@
   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);
=======================================
--- /branches/bleeding_edge/src/contexts.h      Tue Mar 11 14:41:22 2014 UTC
+++ /branches/bleeding_edge/src/contexts.h      Tue Mar 11 16:17:20 2014 UTC
@@ -170,6 +170,12 @@
     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) \
@@ -324,6 +330,12 @@
     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,
=======================================
--- /branches/bleeding_edge/src/promise.js      Tue Mar 11 10:41:18 2014 UTC
+++ /branches/bleeding_edge/src/promise.js      Tue Mar 11 16:17:20 2014 UTC
@@ -100,6 +100,15 @@
 function PromiseReject(promise, r) {
   PromiseDone(promise, -1, r, promiseOnReject)
 }
+
+
+// For API.
+
+function PromiseNopResolver() {}
+
+function PromiseCreate() {
+  return new Promise(PromiseNopResolver)
+}


 // Convenience.
=======================================
--- /branches/bleeding_edge/src/runtime.h       Tue Mar 11 14:41:22 2014 UTC
+++ /branches/bleeding_edge/src/runtime.h       Tue Mar 11 16:17:20 2014 UTC
@@ -303,7 +303,7 @@
   /* ES5 */ \
   F(ObjectFreeze, 1, 1) \
   \
-  /* Harmony Microtasks */ \
+  /* Harmony microtasks */ \
   F(GetMicrotaskState, 0, 1) \
   \
   /* Harmony modules */ \
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Tue Mar 11 14:41:22 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-api.cc Tue Mar 11 16:17:20 2014 UTC
@@ -22110,3 +22110,97 @@
   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());
+}

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to