Implement callback when script finishes running in V8 API.

TEST=test-api/LeaveScriptCallback

Review URL: http://codereview.chromium.org/8937003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10323 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2012-01-04 08:48:35 +00:00
parent c17dafd3a3
commit d9f7cf819c
5 changed files with 190 additions and 9 deletions

View File

@ -2635,6 +2635,9 @@ typedef void (*MemoryAllocationCallback)(ObjectSpace space,
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 @@ class V8EXPORT V8 {
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.

View File

@ -78,7 +78,7 @@ namespace v8 {
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 @@ namespace v8 {
} \
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 @@ Local<Value> Script::Run() {
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 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv,
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 @@ Local<v8::Value> Object::CallAsConstructor(int argc,
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 @@ Local<v8::Value> Object::CallAsConstructor(int argc,
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 @@ Local<v8::Object> Function::NewInstance(int argc,
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 @@ Local<v8::Value> Function::Call(v8::Handle<v8::Object> recv, int argc,
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);
@ -5045,6 +5056,21 @@ void V8::RemoveMemoryAllocationCallback(MemoryAllocationCallback 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() {
i::Isolate* isolate = i::Isolate::Current();
isolate->logger()->PauseProfiler();

View File

@ -51,6 +51,7 @@ bool V8::has_been_setup_ = false;
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 @@ void V8::TearDown() {
is_running_ = false;
has_been_disposed_ = true;
delete call_completed_callbacks_;
call_completed_callbacks_ = NULL;
}
@ -169,6 +173,41 @@ bool V8::IdleNotification(int 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.
typedef union {
double double_value;

View File

@ -108,6 +108,10 @@ class V8 : public AllStatic {
// 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 @@ class V8 : public AllStatic {
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_;
};

View File

@ -15726,3 +15726,97 @@ THREADED_TEST(ForeignFunctionReceiver) {
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';");
}