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:
parent
c17dafd3a3
commit
d9f7cf819c
20
include/v8.h
20
include/v8.h
@ -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.
|
||||
|
40
src/api.cc
40
src/api.cc
@ -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();
|
||||
|
39
src/v8.cc
39
src/v8.cc
@ -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;
|
||||
|
6
src/v8.h
6
src/v8.h
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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';");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user