As long as all debugger messages are handled by a single static method we need a way to identify request sender to route the response to the right handler. To accomplish this clients can send some additional data along with command text and debugger will later pass this data to the message handler along with the response text.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1755 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yurys@chromium.org 2009-04-21 14:06:48 +00:00
parent 0efd83c916
commit 7fc551ecc3
7 changed files with 368 additions and 145 deletions

View File

@ -79,48 +79,60 @@ enum DebugEvent {
};
/**
* Debug event callback function.
*
* \param event the type of the debug event that triggered the callback
* (enum DebugEvent)
* \param exec_state execution state (JavaScript object)
* \param event_data event specific data (JavaScript object)
* \param data value passed by the user to SetDebugEventListener
*/
typedef void (*DebugEventCallback)(DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
Handle<Value> data);
/**
* Debug message callback function.
*
* \param message the debug message
* \param length length of the message
* \param data the data value passed when registering the message handler
* A DebugMessageHandler does not take posession of the message string,
* and must not rely on the data persisting after the handler returns.
*/
typedef void (*DebugMessageHandler)(const uint16_t* message, int length,
void* data);
/**
* Debug host dispatch callback function.
*
* \param dispatch the dispatch value
* \param data the data value passed when registering the dispatch handler
*/
typedef void (*DebugHostDispatchHandler)(void* dispatch,
void* data);
class EXPORT Debug {
public:
/**
* A client object passed to the v8 debugger whose ownership will be taken by
* it. v8 is always responsible for deleting the object.
*/
class ClientData {
public:
virtual ~ClientData() {}
};
/**
* Debug event callback function.
*
* \param event the type of the debug event that triggered the callback
* (enum DebugEvent)
* \param exec_state execution state (JavaScript object)
* \param event_data event specific data (JavaScript object)
* \param data value passed by the user to SetDebugEventListener
*/
typedef void (*EventCallback)(DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
Handle<Value> data);
/**
* Debug message callback function.
*
* \param message the debug message
* \param length length of the message
* \param data the data value passed when registering the message handler
* \param client_data the data value passed into Debug::SendCommand along
* with the request that led to the message or NULL if the message is an
* asynchronous event. The debugger takes ownership of the data and will
* delete it before dying even if there is no message handler.
* A MessageHandler does not take posession of the message string,
* and must not rely on the data persisting after the handler returns.
*/
typedef void (*MessageHandler)(const uint16_t* message, int length,
ClientData* client_data);
/**
* Debug host dispatch callback function.
*
* \param dispatch the dispatch value
* \param data the data value passed when registering the dispatch handler
*/
typedef void (*HostDispatchHandler)(ClientData* dispatch);
// Set a C debug event listener.
static bool SetDebugEventListener(DebugEventCallback that,
static bool SetDebugEventListener(EventCallback that,
Handle<Value> data = Handle<Value>());
// Set a JavaScript debug event listener.
@ -131,14 +143,14 @@ class EXPORT Debug {
static void DebugBreak();
// Message based interface. The message protocol is JSON.
static void SetMessageHandler(DebugMessageHandler handler, void* data = NULL,
static void SetMessageHandler(MessageHandler handler,
bool message_handler_thread = true);
static void SendCommand(const uint16_t* command, int length);
static void SendCommand(const uint16_t* command, int length,
ClientData* client_data = NULL);
// Dispatch interface.
static void SetHostDispatchHandler(DebugHostDispatchHandler handler,
void* data = NULL);
static void SendHostDispatch(void* dispatch);
static void SetHostDispatchHandler(HostDispatchHandler handler);
static void SendHostDispatch(ClientData* dispatch);
/**
* Run a JavaScript function in the debugger.

View File

@ -3230,7 +3230,7 @@ Local<Value> Exception::Error(v8::Handle<v8::String> raw_message) {
// --- D e b u g S u p p o r t ---
#ifdef ENABLE_DEBUGGER_SUPPORT
bool Debug::SetDebugEventListener(DebugEventCallback that, Handle<Value> data) {
bool Debug::SetDebugEventListener(EventCallback that, Handle<Value> data) {
EnsureInitialized("v8::Debug::SetDebugEventListener()");
ON_BAILOUT("v8::Debug::SetDebugEventListener()", return false);
ENTER_V8;
@ -3260,29 +3260,30 @@ void Debug::DebugBreak() {
}
void Debug::SetMessageHandler(v8::DebugMessageHandler handler, void* data,
void Debug::SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread) {
EnsureInitialized("v8::Debug::SetMessageHandler");
ENTER_V8;
i::Debugger::SetMessageHandler(handler, data, message_handler_thread);
i::Debugger::SetMessageHandler(handler, message_handler_thread);
}
void Debug::SendCommand(const uint16_t* command, int length) {
void Debug::SendCommand(const uint16_t* command, int length,
ClientData* client_data) {
if (!i::V8::HasBeenSetup()) return;
i::Debugger::ProcessCommand(i::Vector<const uint16_t>(command, length));
i::Debugger::ProcessCommand(i::Vector<const uint16_t>(command, length),
client_data);
}
void Debug::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data) {
void Debug::SetHostDispatchHandler(HostDispatchHandler handler) {
EnsureInitialized("v8::Debug::SetHostDispatchHandler");
ENTER_V8;
i::Debugger::SetHostDispatchHandler(handler, data);
i::Debugger::SetHostDispatchHandler(handler);
}
void Debug::SendHostDispatch(void* dispatch) {
void Debug::SendHostDispatch(ClientData* dispatch) {
if (!i::V8::HasBeenSetup()) return;
i::Debugger::ProcessHostDispatch(dispatch);
}

View File

@ -35,10 +35,12 @@ namespace v8 { namespace internal {
// Public V8 debugger API message handler function. This function just delegates
// to the debugger agent through it's data parameter.
void DebuggerAgentMessageHandler(const uint16_t* message, int length,
void *data) {
reinterpret_cast<DebuggerAgent*>(data)->DebuggerMessage(message, length);
v8::Debug::ClientData* client_data) {
DebuggerAgent::instance_->DebuggerMessage(message, length);
}
// static
DebuggerAgent* DebuggerAgent::instance_ = NULL;
// Debugger agent main thread.
void DebuggerAgent::Run() {

View File

@ -46,8 +46,14 @@ class DebuggerAgent: public Thread {
: name_(StrDup(name)), port_(port),
server_(OS::CreateSocket()), terminate_(false),
session_access_(OS::CreateMutex()), session_(NULL),
terminate_now_(OS::CreateSemaphore(0)) {}
~DebuggerAgent() { delete server_; }
terminate_now_(OS::CreateSemaphore(0)) {
ASSERT(instance_ == NULL);
instance_ = this;
}
~DebuggerAgent() {
instance_ = NULL;
delete server_;
}
void Shutdown();
@ -66,9 +72,11 @@ class DebuggerAgent: public Thread {
DebuggerAgentSession* session_; // Current active session if any.
Semaphore* terminate_now_; // Semaphore to signal termination.
static DebuggerAgent* instance_;
friend class DebuggerAgentSession;
friend void DebuggerAgentMessageHandler(const uint16_t* message, int length,
void *data);
v8::Debug::ClientData* client_data);
DISALLOW_COPY_AND_ASSIGN(DebuggerAgent);
};

View File

@ -1398,11 +1398,9 @@ bool Debugger::compiling_natives_ = false;
bool Debugger::is_loading_debugger_ = false;
bool Debugger::never_unload_debugger_ = false;
DebugMessageThread* Debugger::message_thread_ = NULL;
v8::DebugMessageHandler Debugger::message_handler_ = NULL;
v8::Debug::MessageHandler Debugger::message_handler_ = NULL;
bool Debugger::message_handler_cleared_ = false;
void* Debugger::message_handler_data_ = NULL;
v8::DebugHostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
void* Debugger::host_dispatch_handler_data_ = NULL;
v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
DebuggerAgent* Debugger::agent_ = NULL;
LockingMessageQueue Debugger::command_queue_(kQueueInitialSize);
LockingMessageQueue Debugger::message_queue_(kQueueInitialSize);
@ -1704,8 +1702,8 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event,
if (event_listener_->IsProxy()) {
// C debug event listener.
Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
v8::DebugEventCallback callback =
FUNCTION_CAST<v8::DebugEventCallback>(callback_obj->proxy());
v8::Debug::EventCallback callback =
FUNCTION_CAST<v8::Debug::EventCallback>(callback_obj->proxy());
callback(event,
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(Handle<JSObject>::cast(event_data)),
@ -1813,18 +1811,20 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
StackGuard::Continue(DEBUGCOMMAND);
// Get the command from the queue.
Vector<uint16_t> command = command_queue_.Get();
Message command = command_queue_.Get();
Logger::DebugTag("Got request from command queue, in interactive loop.");
if (!Debugger::IsDebuggerActive()) {
// Delete command text and user data.
command.Dispose();
return;
}
// Check if the command is a host dispatch.
if (command[0] == 0) {
if (command.IsHostDispatch()) {
if (Debugger::host_dispatch_handler_) {
int32_t dispatch = (command[1] << 16) | command[2];
Debugger::host_dispatch_handler_(reinterpret_cast<void*>(dispatch),
Debugger::host_dispatch_handler_data_);
Debugger::host_dispatch_handler_(command.client_data());
// Delete the dispatch.
command.Dispose();
}
if (auto_continue && !HasCommands()) {
return;
@ -1839,8 +1839,10 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
v8::TryCatch try_catch;
fun_name = v8::String::New("processDebugRequest");
fun = v8::Function::Cast(*cmd_processor->Get(fun_name));
request = v8::String::New(reinterpret_cast<uint16_t*>(command.start()),
command.length());
request = v8::String::New(command.text().start(),
command.text().length());
command.text().Dispose();
static const int kArgc = 1;
v8::Handle<Value> argv[kArgc] = { request };
v8::Local<v8::Value> response_val = fun->Call(cmd_processor, kArgc, argv);
@ -1876,13 +1878,8 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
response = try_catch.Exception()->ToString();
}
// Convert text result to C string.
v8::String::Value val(response);
Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val),
response->Length());
// Return the result.
SendMessage(str);
SendMessage(Message::NewOutput(response, command.client_data()));
// Return from debug event processing if either the VM is put into the
// runnning state (through a continue command) or auto continue is active
@ -1928,12 +1925,11 @@ void Debugger::SetEventListener(Handle<Object> callback,
}
void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data,
void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread) {
ScopedLock with(debugger_access_);
message_handler_ = handler;
message_handler_data_ = data;
if (handler != NULL) {
if (!message_thread_ && message_handler_thread) {
message_thread_ = new DebugMessageThread();
@ -1952,10 +1948,8 @@ void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data,
}
void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data) {
void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler) {
host_dispatch_handler_ = handler;
host_dispatch_handler_data_ = data;
}
@ -1963,26 +1957,28 @@ void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
// public API. Messages are kept internally as Vector<uint16_t> strings, which
// are allocated in various places and deallocated by the calling function
// sometime after this call.
void Debugger::InvokeMessageHandler(Vector<uint16_t> message) {
void Debugger::InvokeMessageHandler(Message message) {
ScopedLock with(debugger_access_);
if (message_handler_ != NULL) {
message_handler_(message.start(), message.length(), message_handler_data_);
message_handler_(message.text().start(),
message.text().length(),
message.client_data());
}
message.Dispose();
}
void Debugger::SendMessage(Vector<uint16_t> message) {
void Debugger::SendMessage(Message message) {
if (message_thread_ == NULL) {
// If there is no message thread just invoke the message handler from the
// V8 thread.
InvokeMessageHandler(message);
} else {
// Put a copy of the message coming from V8 on the queue. The new copy of
// the event string is destroyed by the message thread.
Vector<uint16_t> message_copy = message.Clone();
// Put the message coming from V8 on the queue. The text and user data will
// be destroyed by the message thread.
Logger::DebugTag("Put message on event message_queue.");
message_queue_.Put(message_copy);
message_queue_.Put(message);
message_received_->Signal();
}
}
@ -2005,12 +2001,11 @@ bool Debugger::SendEventMessage(Handle<Object> event_data) {
if (FLAG_trace_debug_json) {
PrintLn(json_event_string);
}
v8::String::Value val(json_event_string);
Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val),
json_event_string->Length());
SendMessage(str);
SendMessage(Message::NewOutput(
json_event_string,
NULL /* no user data since there was no request */));
} else {
SendMessage(Vector<uint16_t>::empty());
SendMessage(Message::NewEmptyMessage());
}
} else {
PrintLn(try_catch.Exception());
@ -2026,13 +2021,15 @@ bool Debugger::SendEventMessage(Handle<Object> event_data) {
// by the API client thread. This is where the API client hands off
// processing of the command to the DebugMessageThread thread.
// The new copy of the command is destroyed in HandleCommand().
void Debugger::ProcessCommand(Vector<const uint16_t> command) {
// Make a copy of the command. Need to cast away const for Clone to work.
Vector<uint16_t> command_copy =
void Debugger::ProcessCommand(Vector<const uint16_t> command,
v8::Debug::ClientData* client_data) {
// Need to cast away const.
Message message = Message::NewCommand(
Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
command.length()).Clone();
command.length()),
client_data);
Logger::DebugTag("Put command on command_queue.");
command_queue_.Put(command_copy);
command_queue_.Put(message);
command_received_->Signal();
// Set the debug command break flag to have the command processed.
@ -2047,14 +2044,10 @@ bool Debugger::HasCommands() {
}
void Debugger::ProcessHostDispatch(void* dispatch) {
void Debugger::ProcessHostDispatch(v8::Debug::ClientData* dispatch) {
// Puts a host dispatch comming from the public API on the queue.
uint16_t hack[3];
hack[0] = 0;
hack[1] = reinterpret_cast<uint32_t>(dispatch) >> 16;
hack[2] = reinterpret_cast<uint32_t>(dispatch) & 0xFFFF;
Logger::DebugTag("Put dispatch on command_queue.");
command_queue_.Put(Vector<uint16_t>(hack, 3).Clone());
command_queue_.Put(Message::NewHostDispatch(dispatch));
command_received_->Signal();
// Set the debug command break flag to have the host dispatch processed.
@ -2134,9 +2127,11 @@ void DebugMessageThread::Run() {
// Wait and Get are paired so that semaphore count equals queue length.
Debugger::message_received_->Wait();
Logger::DebugTag("Get message from event message_queue.");
Vector<uint16_t> message = Debugger::message_queue_.Get();
if (message.length() > 0) {
Message message = Debugger::message_queue_.Get();
if (message.text().length() > 0) {
Debugger::InvokeMessageHandler(message);
} else {
message.Dispose();
}
}
}
@ -2144,22 +2139,84 @@ void DebugMessageThread::Run() {
void DebugMessageThread::Stop() {
keep_running_ = false;
Debugger::SendMessage(Vector<uint16_t>(NULL, 0));
Debugger::SendMessage(Message::NewEmptyMessage());
Join();
}
Message::Message() : text_(Vector<uint16_t>::empty()),
client_data_(NULL),
is_host_dispatch_(false) {
}
Message::Message(const Vector<uint16_t>& text,
v8::Debug::ClientData* data,
bool is_host_dispatch)
: text_(text),
client_data_(data),
is_host_dispatch_(is_host_dispatch) {
}
Message::~Message() {
}
void Message::Dispose() {
text_.Dispose();
delete client_data_;
client_data_ = NULL;
}
bool Message::IsHostDispatch() const {
return is_host_dispatch_;
}
Message Message::NewCommand(const Vector<uint16_t>& command,
v8::Debug::ClientData* data) {
return Message(command.Clone(), data, false);
}
Message Message::NewHostDispatch(v8::Debug::ClientData* dispatch) {
return Message(Vector<uint16_t>::empty(), dispatch, true);
}
Message Message::NewOutput(v8::Handle<v8::String> output,
v8::Debug::ClientData* data) {
Vector<uint16_t> text;
if (!output.IsEmpty()) {
text = Vector<uint16_t>::New(output->Length() + 1);
output->Write(text.start());
}
return Message(text, data, false);
}
Message Message::NewEmptyMessage() {
return Message();
}
MessageQueue::MessageQueue(int size) : start_(0), end_(0), size_(size) {
messages_ = NewArray<Vector<uint16_t> >(size);
messages_ = NewArray<Message>(size);
}
MessageQueue::~MessageQueue() {
while(!IsEmpty()) {
Message m = Get();
m.Dispose();
}
DeleteArray(messages_);
}
Vector<uint16_t> MessageQueue::Get() {
Message MessageQueue::Get() {
ASSERT(!IsEmpty());
int result = start_;
start_ = (start_ + 1) % size_;
@ -2167,7 +2224,7 @@ Vector<uint16_t> MessageQueue::Get() {
}
void MessageQueue::Put(const Vector<uint16_t>& message) {
void MessageQueue::Put(const Message& message) {
if ((end_ + 1) % size_ == start_) {
Expand();
}
@ -2181,9 +2238,11 @@ void MessageQueue::Expand() {
while (!IsEmpty()) {
new_queue.Put(Get());
}
Vector<uint16_t>* array_to_free = messages_;
Message* array_to_free = messages_;
*this = new_queue;
new_queue.messages_ = array_to_free;
// Make the new_queue empty so that it doesn't call Dispose on any messages.
new_queue.start_ = new_queue.end_;
// Automatic destructor called on new_queue, freeing array_to_free.
}
@ -2204,18 +2263,18 @@ bool LockingMessageQueue::IsEmpty() const {
}
Vector<uint16_t> LockingMessageQueue::Get() {
Message LockingMessageQueue::Get() {
ScopedLock sl(lock_);
Vector<uint16_t> result = queue_.Get();
Logger::DebugEvent("Get", result);
Message result = queue_.Get();
Logger::DebugEvent("Get", result.text());
return result;
}
void LockingMessageQueue::Put(const Vector<uint16_t>& message) {
void LockingMessageQueue::Put(const Message& message) {
ScopedLock sl(lock_);
queue_.Put(message);
Logger::DebugEvent("Put", message);
Logger::DebugEvent("Put", message.text());
}

View File

@ -397,6 +397,36 @@ class Debug {
};
// Message send by user to v8 debugger or debugger output message.
// In addition to command text it may contain a pointer to some user data
// which are expected to be passed along with the command reponse to message
// handler.
class Message {
public:
static Message NewCommand(const Vector<uint16_t>& command,
v8::Debug::ClientData* data);
static Message NewHostDispatch(v8::Debug::ClientData* dispatch);
static Message NewOutput(v8::Handle<v8::String> output,
v8::Debug::ClientData* data);
static Message NewEmptyMessage();
Message();
~Message();
// Deletes user data and disposes of the text.
void Dispose();
bool IsHostDispatch() const;
Vector<uint16_t> text() const { return text_; }
v8::Debug::ClientData* client_data() const { return client_data_; }
private:
Message(const Vector<uint16_t>& text,
v8::Debug::ClientData* data,
bool is_host_dispatch);
Vector<uint16_t> text_;
v8::Debug::ClientData* client_data_;
bool is_host_dispatch_;
};
// A Queue of Vector<uint16_t> objects. A thread-safe version is
// LockingMessageQueue, based on this class.
class MessageQueue BASE_EMBEDDED {
@ -404,14 +434,14 @@ class MessageQueue BASE_EMBEDDED {
explicit MessageQueue(int size);
~MessageQueue();
bool IsEmpty() const { return start_ == end_; }
Vector<uint16_t> Get();
void Put(const Vector<uint16_t>& message);
Message Get();
void Put(const Message& message);
void Clear() { start_ = end_ = 0; } // Queue is empty after Clear().
private:
// Doubles the size of the message queue, and copies the messages.
void Expand();
Vector<uint16_t>* messages_;
Message* messages_;
int start_;
int end_;
int size_; // The size of the queue buffer. Queue can hold size-1 messages.
@ -427,8 +457,8 @@ class LockingMessageQueue BASE_EMBEDDED {
explicit LockingMessageQueue(int size);
~LockingMessageQueue();
bool IsEmpty() const;
Vector<uint16_t> Get();
void Put(const Vector<uint16_t>& message);
Message Get();
void Put(const Message& message);
void Clear();
private:
MessageQueue queue_;
@ -473,29 +503,29 @@ class Debugger {
Handle<Object> event_data,
bool auto_continue);
static void SetEventListener(Handle<Object> callback, Handle<Object> data);
static void SetMessageHandler(v8::DebugMessageHandler handler, void* data,
static void SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread);
static void TearDown();
static void SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data);
static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler);
// Invoke the message handler function.
static void InvokeMessageHandler(Vector< uint16_t> message);
static void InvokeMessageHandler(Message message);
// Send a message to the message handler eiher through the message thread or
// directly.
static void SendMessage(Vector<uint16_t> message);
static void SendMessage(Message message);
// Send the JSON message for a debug event.
static bool SendEventMessage(Handle<Object> event_data);
// Add a debugger command to the command queue.
static void ProcessCommand(Vector<const uint16_t> command);
static void ProcessCommand(Vector<const uint16_t> command,
v8::Debug::ClientData* client_data = NULL);
// Check whether there are commands in the command queue.
static bool HasCommands();
static void ProcessHostDispatch(void* dispatch);
static void ProcessHostDispatch(v8::Debug::ClientData* dispatch);
static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data,
bool* pending_exception);
@ -539,11 +569,9 @@ class Debugger {
static bool is_loading_debugger_; // Are we loading the debugger?
static bool never_unload_debugger_; // Can we unload the debugger?
static DebugMessageThread* message_thread_;
static v8::DebugMessageHandler message_handler_;
static v8::Debug::MessageHandler message_handler_;
static bool message_handler_cleared_; // Was message handler cleared?
static void* message_handler_data_;
static v8::DebugHostDispatchHandler host_dispatch_handler_;
static void* host_dispatch_handler_data_;
static v8::Debug::HostDispatchHandler host_dispatch_handler_;
static DebuggerAgent* agent_;

View File

@ -45,10 +45,13 @@ using ::v8::internal::JSGlobalProxy;
using ::v8::internal::Code;
using ::v8::internal::Debug;
using ::v8::internal::Debugger;
using ::v8::internal::Message;
using ::v8::internal::MessageQueue;
using ::v8::internal::StepAction;
using ::v8::internal::StepIn; // From StepAction enum
using ::v8::internal::StepNext; // From StepAction enum
using ::v8::internal::StepOut; // From StepAction enum
using ::v8::internal::Vector;
// Size of temp buffer for formatting small strings.
@ -3375,7 +3378,8 @@ class MessageQueueDebuggerThread : public v8::internal::Thread {
void Run();
};
static void MessageHandler(const uint16_t* message, int length, void *data) {
static void MessageHandler(const uint16_t* message, int length,
v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
if (IsBreakEventMessage(print_buffer)) {
@ -3390,7 +3394,6 @@ static void MessageHandler(const uint16_t* message, int length, void *data) {
fflush(stdout);
}
void MessageQueueDebuggerThread::Run() {
const int kBufferSize = 1000;
uint16_t buffer_1[kBufferSize];
@ -3481,6 +3484,114 @@ TEST(MessageQueues) {
fflush(stdout);
}
class TestClientData : public v8::Debug::ClientData {
public:
TestClientData() {
constructor_call_counter++;
}
virtual ~TestClientData() {
destructor_call_counter++;
}
static void ResetCounters() {
constructor_call_counter = 0;
destructor_call_counter = 0;
}
static int constructor_call_counter;
static int destructor_call_counter;
};
int TestClientData::constructor_call_counter = 0;
int TestClientData::destructor_call_counter = 0;
// Tests that MessageQueue doesn't destroy client data when expands and
// does destroy when it dies.
TEST(MessageQueueExpandAndDestroy) {
TestClientData::ResetCounters();
{ // Create a scope for the queue.
MessageQueue queue(1);
queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
new TestClientData()));
queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
new TestClientData()));
queue.Put(Message::NewHostDispatch(new TestClientData()));
ASSERT_EQ(0, TestClientData::destructor_call_counter);
queue.Get().Dispose();
ASSERT_EQ(1, TestClientData::destructor_call_counter);
queue.Put(Message::NewHostDispatch(new TestClientData()));
queue.Put(Message::NewHostDispatch(new TestClientData()));
queue.Put(Message::NewHostDispatch(new TestClientData()));
queue.Put(Message::NewOutput(v8::Handle<v8::String>(),
new TestClientData()));
queue.Put(Message::NewEmptyMessage());
ASSERT_EQ(1, TestClientData::destructor_call_counter);
queue.Get().Dispose();
ASSERT_EQ(2, TestClientData::destructor_call_counter);
}
// All the client data should be destroyed when the queue is destroyed.
ASSERT_EQ(TestClientData::destructor_call_counter,
TestClientData::destructor_call_counter);
}
static int handled_client_data_instances_count = 0;
static void MessageHandlerCountingClientData(
const uint16_t* message,
int length,
v8::Debug::ClientData* client_data) {
if (client_data) {
handled_client_data_instances_count++;
}
}
// Tests that all client data passed to the debugger are sent to the handler.
TEST(SendClientDataToHandler) {
// Create a V8 environment
v8::HandleScope scope;
DebugLocalContext env;
TestClientData::ResetCounters();
handled_client_data_instances_count = 0;
v8::Debug::SetMessageHandler(MessageHandlerCountingClientData,
false /* message_handler_thread */);
const char* source_1 = "a = 3; b = 4; c = new Object(); c.d = 5; debugger;";
const int kBufferSize = 1000;
uint16_t buffer[kBufferSize];
const char* command_1 =
"{\"seq\":117,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
"\"arguments\":{\"expression\":\"1+2\"}}";
const char* command_2 =
"{\"seq\":118,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
"\"arguments\":{\"expression\":\"1+a\"}}";
const char* command_continue =
"{\"seq\":106,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer),
new TestClientData());
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer), NULL);
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer),
new TestClientData());
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer),
new TestClientData());
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
CompileRun(source_1);
ASSERT_EQ(3, TestClientData::constructor_call_counter);
ASSERT_EQ(TestClientData::constructor_call_counter,
handled_client_data_instances_count);
ASSERT_EQ(TestClientData::constructor_call_counter,
TestClientData::destructor_call_counter);
}
/* Test ThreadedDebugging */
/* This test interrupts a running infinite loop that is
* occupying the v8 thread by a break command from the
@ -3508,7 +3619,7 @@ static v8::Handle<v8::Value> ThreadedAtBarrier1(const v8::Arguments& args) {
static void ThreadedMessageHandler(const uint16_t* message, int length,
void *data) {
v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
if (IsBreakEventMessage(print_buffer)) {
@ -3606,7 +3717,7 @@ Barriers* breakpoints_barriers;
static void BreakpointsMessageHandler(const uint16_t* message,
int length,
void *data) {
v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
printf("%s\n", print_buffer);
@ -3752,7 +3863,8 @@ TEST(SetDebugEventListenerOnUninitializedVM) {
static void DummyMessageHandler(const uint16_t* message,
int length, void *data) {
int length,
v8::Debug::ClientData* client_data) {
}
@ -3978,7 +4090,8 @@ TEST(DebuggerUnload) {
// Debugger message handler which counts the number of times it is called.
static int message_handler_hit_count = 0;
static void MessageHandlerHitCount(const uint16_t* message,
int length, void* data) {
int length,
v8::Debug::ClientData* client_data) {
message_handler_hit_count++;
const int kBufferSize = 1000;
@ -4026,9 +4139,10 @@ TEST(DebuggerClearMessageHandler) {
// Debugger message handler which clears the message handler while active.
static void MessageHandlerClearingMessageHandler(const uint16_t* message,
int length,
void* data) {
static void MessageHandlerClearingMessageHandler(
const uint16_t* message,
int length,
v8::Debug::ClientData* client_data) {
message_handler_hit_count++;
// Clear debug message handler.
@ -4059,7 +4173,7 @@ TEST(DebuggerClearMessageHandlerWhileActive) {
int host_dispatch_hit_count = 0;
static void HostDispatchHandlerHitCount(void* dispatch, void *data) {
static void HostDispatchHandlerHitCount(v8::Debug::ClientData* dispatch) {
host_dispatch_hit_count++;
}
@ -4085,8 +4199,7 @@ TEST(DebuggerHostDispatch) {
// Setup message and host dispatch handlers.
v8::Debug::SetMessageHandler(DummyMessageHandler);
v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount,
NULL);
v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount);
// Send a host dispatch by itself.
v8::Debug::SendHostDispatch(NULL);