Revision: 10323
Author:   [email protected]
Date:     Wed Jan  4 00:48:35 2012
Log:      Implement callback when script finishes running in V8 API.

TEST=test-api/LeaveScriptCallback

Review URL: http://codereview.chromium.org/8937003
http://code.google.com/p/v8/source/detail?r=10323

Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/v8.cc
 /branches/bleeding_edge/src/v8.h
 /branches/bleeding_edge/test/cctest/test-api.cc

=======================================
--- /branches/bleeding_edge/include/v8.h        Wed Nov 30 03:13:36 2011
+++ /branches/bleeding_edge/include/v8.h        Wed Jan  4 00:48:35 2012
@@ -2635,6 +2635,9 @@
                                          AllocationAction action,
                                          int size);

+// --- Leave Script Callback ---
+typedef void (*CallCompletedCallback)();
+
 // --- Failed Access Check Callback ---
 typedef void (*FailedAccessCheckCallback)(Local<Object> target,
                                           AccessType type,
@@ -3033,11 +3036,24 @@
                                           AllocationAction action);

   /**
-   * This function removes callback which was installed by
-   * AddMemoryAllocationCallback function.
+   * Removes callback that was installed by AddMemoryAllocationCallback.
    */
static void RemoveMemoryAllocationCallback(MemoryAllocationCallback callback);

+  /**
+   * Adds a callback to notify the host application when a script finished
+   * running.  If a script re-enters the runtime during executing, the
+   * CallCompletedCallback is only invoked when the outer-most script
+   * execution ends.  Executing scripts inside the callback do not trigger
+   * further callbacks.
+   */
+  static void AddCallCompletedCallback(CallCompletedCallback callback);
+
+  /**
+   * Removes callback that was installed by AddCallCompletedCallback.
+   */
+  static void RemoveCallCompletedCallback(CallCompletedCallback callback);
+
   /**
    * Allows the host application to group objects together. If one
    * object in the group is alive, all objects in the group are alive.
=======================================
--- /branches/bleeding_edge/src/api.cc  Thu Dec  8 08:07:07 2011
+++ /branches/bleeding_edge/src/api.cc  Wed Jan  4 00:48:35 2012
@@ -78,7 +78,7 @@
   bool has_pending_exception = false


-#define EXCEPTION_BAILOUT_CHECK(isolate, value) \ +#define EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, do_callback) \ do { \ i::HandleScopeImplementer* handle_scope_implementer = \ (isolate)->handle_scope_implementer(); \
@@ -91,11 +91,22 @@
} \ bool call_depth_is_zero = handle_scope_implementer->CallDepthIsZero(); \ (isolate)->OptionalRescheduleException(call_depth_is_zero); \ + do_callback \ return value; \ } \ + do_callback \
   } while (false)


+#define EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, value) \ + EXCEPTION_BAILOUT_CHECK_GENERIC( \
+      isolate, value, i::V8::FireCallCompletedCallback(isolate);)
+
+
+#define EXCEPTION_BAILOUT_CHECK(isolate, value) \
+  EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, ;)
+
+
#define API_ENTRY_CHECK(isolate, msg) \ do { \ if (v8::Locker::IsActive()) { \
@@ -1568,7 +1579,7 @@
         isolate->context()->global_proxy(), isolate);
     i::Handle<i::Object> result =
         i::Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
-    EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+    EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Value>());
     raw_result = *result;
   }
   i::Handle<i::Object> result(raw_result, isolate);
@@ -3494,7 +3505,7 @@
   EXCEPTION_PREAMBLE(isolate);
   i::Handle<i::Object> returned =
i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception);
-  EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+  EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Value>());
   return Utils::ToLocal(scope.CloseAndEscape(returned));
 }

@@ -3515,7 +3526,7 @@
     EXCEPTION_PREAMBLE(isolate);
     i::Handle<i::Object> returned =
         i::Execution::New(fun, argc, args, &has_pending_exception);
-    EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+    EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>());
     return Utils::ToLocal(scope.CloseAndEscape(
         i::Handle<i::JSObject>::cast(returned)));
   }
@@ -3528,7 +3539,7 @@
     EXCEPTION_PREAMBLE(isolate);
     i::Handle<i::Object> returned =
         i::Execution::Call(fun, obj, argc, args, &has_pending_exception);
-    EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+    EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>());
     ASSERT(!delegate->IsUndefined());
     return Utils::ToLocal(scope.CloseAndEscape(returned));
   }
@@ -3555,7 +3566,7 @@
   EXCEPTION_PREAMBLE(isolate);
   i::Handle<i::Object> returned =
       i::Execution::New(function, argc, args, &has_pending_exception);
-  EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+  EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>());
return scope.Close(Utils::ToLocal(i::Handle<i::JSObject>::cast(returned)));
 }

@@ -3576,7 +3587,7 @@
     EXCEPTION_PREAMBLE(isolate);
     i::Handle<i::Object> returned =
i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception);
-    EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>());
+    EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Object>());
     raw_result = *returned;
   }
   i::Handle<i::Object> result(raw_result);
@@ -5043,6 +5054,21 @@
   isolate->memory_allocator()->RemoveMemoryAllocationCallback(
       callback);
 }
+
+
+void V8::AddCallCompletedCallback(CallCompletedCallback callback) {
+  if (callback == NULL) return;
+  i::Isolate* isolate = i::Isolate::Current();
+  if (IsDeadCheck(isolate, "v8::V8::AddLeaveScriptCallback()")) return;
+  i::V8::AddCallCompletedCallback(callback);
+}
+
+
+void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) {
+  i::Isolate* isolate = i::Isolate::Current();
+  if (IsDeadCheck(isolate, "v8::V8::RemoveLeaveScriptCallback()")) return;
+  i::V8::RemoveCallCompletedCallback(callback);
+}


 void V8::PauseProfiler() {
=======================================
--- /branches/bleeding_edge/src/v8.cc   Thu Dec 15 09:00:27 2011
+++ /branches/bleeding_edge/src/v8.cc   Wed Jan  4 00:48:35 2012
@@ -51,6 +51,7 @@
 bool V8::has_been_disposed_ = false;
 bool V8::has_fatal_error_ = false;
 bool V8::use_crankshaft_ = true;
+List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL;

 static Mutex* entropy_mutex = OS::CreateMutex();
 static EntropySource entropy_source;
@@ -104,6 +105,9 @@

   is_running_ = false;
   has_been_disposed_ = true;
+
+  delete call_completed_callbacks_;
+  call_completed_callbacks_ = NULL;
 }


@@ -167,6 +171,41 @@
   // Tell the heap that it may want to adjust.
   return HEAP->IdleNotification(hint);
 }
+
+
+void V8::AddCallCompletedCallback(CallCompletedCallback callback) {
+  if (call_completed_callbacks_ == NULL) {  // Lazy init.
+    call_completed_callbacks_ = new List<CallCompletedCallback>();
+  }
+  for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+    if (callback == call_completed_callbacks_->at(i)) return;
+  }
+  call_completed_callbacks_->Add(callback);
+}
+
+
+void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) {
+  if (call_completed_callbacks_ == NULL) return;
+  for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+    if (callback == call_completed_callbacks_->at(i)) {
+      call_completed_callbacks_->Remove(i);
+    }
+  }
+}
+
+
+void V8::FireCallCompletedCallback(Isolate* isolate) {
+  if (call_completed_callbacks_ == NULL) return;
+  HandleScopeImplementer* handle_scope_implementer =
+      isolate->handle_scope_implementer();
+  if (!handle_scope_implementer->CallDepthIsZero()) return;
+  // Fire callbacks.  Increase call depth to prevent recursive callbacks.
+  handle_scope_implementer->IncrementCallDepth();
+  for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+    call_completed_callbacks_->at(i)();
+  }
+  handle_scope_implementer->DecrementCallDepth();
+}


 // Use a union type to avoid type-aliasing optimizations in GCC.
=======================================
--- /branches/bleeding_edge/src/v8.h    Wed Nov 30 03:13:36 2011
+++ /branches/bleeding_edge/src/v8.h    Wed Jan  4 00:48:35 2012
@@ -108,6 +108,10 @@
   // Idle notification directly from the API.
   static bool IdleNotification(int hint);

+  static void AddCallCompletedCallback(CallCompletedCallback callback);
+  static void RemoveCallCompletedCallback(CallCompletedCallback callback);
+  static void FireCallCompletedCallback(Isolate* isolate);
+
  private:
   static void InitializeOncePerProcess();

@@ -123,6 +127,8 @@
   static bool has_been_disposed_;
   // True if we are using the crankshaft optimizing compiler.
   static bool use_crankshaft_;
+  // List of callbacks when a Call completes.
+  static List<CallCompletedCallback>* call_completed_callbacks_;
 };


=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc     Wed Dec 21 08:14:38 2011
+++ /branches/bleeding_edge/test/cctest/test-api.cc     Wed Jan  4 00:48:35 2012
@@ -15726,3 +15726,97 @@

   foreign_context.Dispose();
 }
+
+
+uint8_t callback_fired = 0;
+
+
+void CallCompletedCallback1() {
+  printf("Firing callback 1.\n");
+  callback_fired ^= 1;  // Toggle first bit.
+}
+
+
+void CallCompletedCallback2() {
+  printf("Firing callback 2.\n");
+  callback_fired ^= 2;  // Toggle second bit.
+}
+
+
+Handle<Value> RecursiveCall(const Arguments& args) {
+  uint32_t level = args[0]->Uint32Value();
+  if (level < 3) {
+    level++;
+    printf("Entering recursion level %d.\n", level);
+    char script[64];
+    snprintf(script, sizeof(script), "recursion(%d)", level);
+    CompileRun(script);
+    printf("Leaving recursion level %d.\n", level);
+    CHECK_EQ(0, callback_fired);
+  } else {
+    printf("Recursion ends.\n");
+    CHECK_EQ(0, callback_fired);
+  }
+  return Undefined();
+}
+
+
+TEST(CallCompletedCallback) {
+  v8::HandleScope scope;
+  LocalContext env;
+  v8::Handle<v8::FunctionTemplate> recursive_runtime =
+      v8::FunctionTemplate::New(RecursiveCall);
+  env->Global()->Set(v8_str("recursion"),
+                     recursive_runtime->GetFunction());
+  // Adding the same callback a second time has no effect.
+  v8::V8::AddCallCompletedCallback(CallCompletedCallback1);
+  v8::V8::AddCallCompletedCallback(CallCompletedCallback1);
+  v8::V8::AddCallCompletedCallback(CallCompletedCallback2);
+  printf("--- Script (1) ---\n");
+  Local<Script> script =
+      v8::Script::Compile(v8::String::New("recursion(0)"));
+  script->Run();
+  CHECK_EQ(3, callback_fired);
+
+  printf("\n--- Script (2) ---\n");
+  callback_fired = 0;
+  v8::V8::RemoveCallCompletedCallback(CallCompletedCallback1);
+  script->Run();
+  CHECK_EQ(2, callback_fired);
+
+  printf("\n--- Function ---\n");
+  callback_fired = 0;
+  Local<Function> recursive_function =
+      Local<Function>::Cast(env->Global()->Get(v8_str("recursion")));
+  v8::Handle<Value> args[] = { v8_num(0) };
+  recursive_function->Call(env->Global(), 1, args);
+  CHECK_EQ(2, callback_fired);
+}
+
+
+void CallCompletedCallbackNoException() {
+  v8::HandleScope scope;
+  CompileRun("1+1;");
+}
+
+
+void CallCompletedCallbackException() {
+  v8::HandleScope scope;
+  CompileRun("throw 'second exception';");
+}
+
+
+TEST(CallCompletedCallbackOneException) {
+  v8::HandleScope scope;
+  LocalContext env;
+  v8::V8::AddCallCompletedCallback(CallCompletedCallbackNoException);
+  CompileRun("throw 'exception';");
+}
+
+
+TEST(CallCompletedCallbackTwoExceptions) {
+  v8::HandleScope scope;
+  LocalContext env;
+  v8::V8::AddCallCompletedCallback(CallCompletedCallbackException);
+  CompileRun("throw 'first exception';");
+}

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to