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