Add API support for passing a C++ function as a microtask callback
authoradamk@chromium.org <adamk@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Jun 2014 20:12:19 +0000 (20:12 +0000)
committeradamk@chromium.org <adamk@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Jun 2014 20:12:19 +0000 (20:12 +0000)
This allows embedders to enqueue microtasks without having any v8::Context
handy, as happens in Blink in some cases (such as DOM mutations due to editing
triggering MutationObservers).

LOG=Y
R=dcarney@chromium.org

Review URL: https://codereview.chromium.org/306053003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21658 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8.h
src/api.cc
src/isolate.cc
src/isolate.h
test/cctest/test-api.cc

index 0fd196b..b1da55e 100644 (file)
@@ -3966,6 +3966,9 @@ typedef void (*MemoryAllocationCallback)(ObjectSpace space,
 // --- Leave Script Callback ---
 typedef void (*CallCompletedCallback)();
 
+// --- Microtask Callback ---
+typedef void (*MicrotaskCallback)(void* data);
+
 // --- Failed Access Check Callback ---
 typedef void (*FailedAccessCheckCallback)(Local<Object> target,
                                           AccessType type,
@@ -4385,6 +4388,11 @@ class V8_EXPORT Isolate {
    */
   void EnqueueMicrotask(Handle<Function> microtask);
 
+  /**
+   * Experimental: Enqueues the callback to the Microtask Work Queue
+   */
+  void EnqueueMicrotask(MicrotaskCallback microtask, void* data = NULL);
+
    /**
    * Experimental: Controls whether the Microtask Work Queue is automatically
    * run when the script call depth decrements to zero.
index cfd952e..9f8103c 100644 (file)
@@ -6679,6 +6679,18 @@ void Isolate::EnqueueMicrotask(Handle<Function> microtask) {
 }
 
 
+void Isolate::EnqueueMicrotask(MicrotaskCallback microtask, void* data) {
+  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+  i::HandleScope scope(isolate);
+  i::Handle<i::CallHandlerInfo> callback_info =
+      i::Handle<i::CallHandlerInfo>::cast(
+          isolate->factory()->NewStruct(i::CALL_HANDLER_INFO_TYPE));
+  SET_FIELD_WRAPPED(callback_info, set_callback, microtask);
+  SET_FIELD_WRAPPED(callback_info, set_data, data);
+  isolate->EnqueueMicrotask(callback_info);
+}
+
+
 void Isolate::SetAutorunMicrotasks(bool autorun) {
   reinterpret_cast<i::Isolate*>(this)->set_autorun_microtasks(autorun);
 }
index 54210c5..7e60566 100644 (file)
@@ -2262,7 +2262,8 @@ void Isolate::FireCallCompletedCallback() {
 }
 
 
-void Isolate::EnqueueMicrotask(Handle<JSFunction> microtask) {
+void Isolate::EnqueueMicrotask(Handle<Object> microtask) {
+  ASSERT(microtask->IsJSFunction() || microtask->IsCallHandlerInfo());
   Handle<FixedArray> queue(heap()->microtask_queue(), this);
   int num_tasks = pending_microtask_count();
   ASSERT(num_tasks <= queue->length());
@@ -2301,18 +2302,30 @@ void Isolate::RunMicrotasks() {
 
     for (int i = 0; i < num_tasks; i++) {
       HandleScope scope(this);
-      Handle<JSFunction> microtask(JSFunction::cast(queue->get(i)), this);
-      Handle<Object> exception;
-      MaybeHandle<Object> result = Execution::TryCall(
-          microtask, factory()->undefined_value(), 0, NULL, &exception);
-      // If execution is terminating, just bail out.
-      if (result.is_null() &&
-          !exception.is_null() &&
-          *exception == heap()->termination_exception()) {
-        // Clear out any remaining callbacks in the queue.
-        heap()->set_microtask_queue(heap()->empty_fixed_array());
-        set_pending_microtask_count(0);
-        return;
+      Handle<Object> microtask(queue->get(i), this);
+      if (microtask->IsJSFunction()) {
+        Handle<JSFunction> microtask_function =
+            Handle<JSFunction>::cast(microtask);
+        Handle<Object> exception;
+        MaybeHandle<Object> result = Execution::TryCall(
+            microtask_function, factory()->undefined_value(),
+            0, NULL, &exception);
+        // If execution is terminating, just bail out.
+        if (result.is_null() &&
+            !exception.is_null() &&
+            *exception == heap()->termination_exception()) {
+          // Clear out any remaining callbacks in the queue.
+          heap()->set_microtask_queue(heap()->empty_fixed_array());
+          set_pending_microtask_count(0);
+          return;
+        }
+      } else {
+        Handle<CallHandlerInfo> callback_info =
+            Handle<CallHandlerInfo>::cast(microtask);
+        v8::MicrotaskCallback callback =
+            v8::ToCData<v8::MicrotaskCallback>(callback_info->callback());
+        void* data = v8::ToCData<void*>(callback_info->data());
+        callback(data);
       }
     }
   }
index 8b1be2d..56d9a77 100644 (file)
@@ -1077,7 +1077,7 @@ class Isolate {
   void RemoveCallCompletedCallback(CallCompletedCallback callback);
   void FireCallCompletedCallback();
 
-  void EnqueueMicrotask(Handle<JSFunction> microtask);
+  void EnqueueMicrotask(Handle<Object> microtask);
   void RunMicrotasks();
 
  private:
index 3ce54cf..539a1f7 100644 (file)
@@ -20689,6 +20689,14 @@ static void MicrotaskTwo(const v8::FunctionCallbackInfo<Value>& info) {
 }
 
 
+void* g_passed_to_three = NULL;
+
+
+static void MicrotaskThree(void* data) {
+  g_passed_to_three = data;
+}
+
+
 TEST(EnqueueMicrotask) {
   LocalContext env;
   v8::HandleScope scope(env->GetIsolate());
@@ -20722,6 +20730,25 @@ TEST(EnqueueMicrotask) {
   CompileRun("1+1;");
   CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value());
   CHECK_EQ(2, CompileRun("ext2Calls")->Int32Value());
+
+  g_passed_to_three = NULL;
+  env->GetIsolate()->EnqueueMicrotask(MicrotaskThree);
+  CompileRun("1+1;");
+  CHECK_EQ(NULL, g_passed_to_three);
+  CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value());
+  CHECK_EQ(2, CompileRun("ext2Calls")->Int32Value());
+
+  int dummy;
+  env->GetIsolate()->EnqueueMicrotask(
+      Function::New(env->GetIsolate(), MicrotaskOne));
+  env->GetIsolate()->EnqueueMicrotask(MicrotaskThree, &dummy);
+  env->GetIsolate()->EnqueueMicrotask(
+      Function::New(env->GetIsolate(), MicrotaskTwo));
+  CompileRun("1+1;");
+  CHECK_EQ(&dummy, g_passed_to_three);
+  CHECK_EQ(3, CompileRun("ext1Calls")->Int32Value());
+  CHECK_EQ(3, CompileRun("ext2Calls")->Int32Value());
+  g_passed_to_three = NULL;
 }