From: yurys@chromium.org Date: Tue, 21 Apr 2009 14:06:48 +0000 (+0000) Subject: As long as all debugger messages are handled by a single static method we need a... X-Git-Tag: upstream/4.7.83~24276 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7fc551ecc3a8a9796248ba197d183a8773eb2dee;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/include/v8-debug.h b/include/v8-debug.h index 4c639c183..fc93d76dd 100644 --- a/include/v8-debug.h +++ b/include/v8-debug.h @@ -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 exec_state, - Handle event_data, - Handle 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 exec_state, + Handle event_data, + Handle 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 data = Handle()); // 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. diff --git a/src/api.cc b/src/api.cc index 9fd1ee0f5..7845525da 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3230,7 +3230,7 @@ Local Exception::Error(v8::Handle raw_message) { // --- D e b u g S u p p o r t --- #ifdef ENABLE_DEBUGGER_SUPPORT -bool Debug::SetDebugEventListener(DebugEventCallback that, Handle data) { +bool Debug::SetDebugEventListener(EventCallback that, Handle 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(command, length)); + i::Debugger::ProcessCommand(i::Vector(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); } diff --git a/src/debug-agent.cc b/src/debug-agent.cc index 24d89b0ac..983874653 100644 --- a/src/debug-agent.cc +++ b/src/debug-agent.cc @@ -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(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() { diff --git a/src/debug-agent.h b/src/debug-agent.h index 54dc6839f..08f13724e 100644 --- a/src/debug-agent.h +++ b/src/debug-agent.h @@ -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); }; diff --git a/src/debug.cc b/src/debug.cc index 99ae4239b..5ab4d3450 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -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 callback_obj(Handle::cast(event_listener_)); - v8::DebugEventCallback callback = - FUNCTION_CAST(callback_obj->proxy()); + v8::Debug::EventCallback callback = + FUNCTION_CAST(callback_obj->proxy()); callback(event, v8::Utils::ToLocal(Handle::cast(exec_state)), v8::Utils::ToLocal(Handle::cast(event_data)), @@ -1813,18 +1811,20 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, StackGuard::Continue(DEBUGCOMMAND); // Get the command from the queue. - Vector 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(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(command.start()), - command.length()); + + request = v8::String::New(command.text().start(), + command.text().length()); + command.text().Dispose(); static const int kArgc = 1; v8::Handle argv[kArgc] = { request }; v8::Local 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 str(reinterpret_cast(*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 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 strings, which // are allocated in various places and deallocated by the calling function // sometime after this call. -void Debugger::InvokeMessageHandler(Vector 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 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 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 event_data) { if (FLAG_trace_debug_json) { PrintLn(json_event_string); } - v8::String::Value val(json_event_string); - Vector str(reinterpret_cast(*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::empty()); + SendMessage(Message::NewEmptyMessage()); } } else { PrintLn(try_catch.Exception()); @@ -2026,13 +2021,15 @@ bool Debugger::SendEventMessage(Handle 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 command) { - // Make a copy of the command. Need to cast away const for Clone to work. - Vector command_copy = +void Debugger::ProcessCommand(Vector command, + v8::Debug::ClientData* client_data) { + // Need to cast away const. + Message message = Message::NewCommand( Vector(const_cast(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(dispatch) >> 16; - hack[2] = reinterpret_cast(dispatch) & 0xFFFF; Logger::DebugTag("Put dispatch on command_queue."); - command_queue_.Put(Vector(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 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(NULL, 0)); + Debugger::SendMessage(Message::NewEmptyMessage()); Join(); } +Message::Message() : text_(Vector::empty()), + client_data_(NULL), + is_host_dispatch_(false) { +} + + +Message::Message(const Vector& 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& command, + v8::Debug::ClientData* data) { + return Message(command.Clone(), data, false); +} + + +Message Message::NewHostDispatch(v8::Debug::ClientData* dispatch) { + return Message(Vector::empty(), dispatch, true); +} + + +Message Message::NewOutput(v8::Handle output, + v8::Debug::ClientData* data) { + Vector text; + if (!output.IsEmpty()) { + text = Vector::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 >(size); + messages_ = NewArray(size); } MessageQueue::~MessageQueue() { + while(!IsEmpty()) { + Message m = Get(); + m.Dispose(); + } DeleteArray(messages_); } -Vector MessageQueue::Get() { +Message MessageQueue::Get() { ASSERT(!IsEmpty()); int result = start_; start_ = (start_ + 1) % size_; @@ -2167,7 +2224,7 @@ Vector MessageQueue::Get() { } -void MessageQueue::Put(const Vector& 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* 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 LockingMessageQueue::Get() { +Message LockingMessageQueue::Get() { ScopedLock sl(lock_); - Vector result = queue_.Get(); - Logger::DebugEvent("Get", result); + Message result = queue_.Get(); + Logger::DebugEvent("Get", result.text()); return result; } -void LockingMessageQueue::Put(const Vector& message) { +void LockingMessageQueue::Put(const Message& message) { ScopedLock sl(lock_); queue_.Put(message); - Logger::DebugEvent("Put", message); + Logger::DebugEvent("Put", message.text()); } diff --git a/src/debug.h b/src/debug.h index e46b8164c..69e5c0e77 100644 --- a/src/debug.h +++ b/src/debug.h @@ -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& command, + v8::Debug::ClientData* data); + static Message NewHostDispatch(v8::Debug::ClientData* dispatch); + static Message NewOutput(v8::Handle output, + v8::Debug::ClientData* data); + static Message NewEmptyMessage(); + Message(); + ~Message(); + + // Deletes user data and disposes of the text. + void Dispose(); + bool IsHostDispatch() const; + Vector text() const { return text_; } + v8::Debug::ClientData* client_data() const { return client_data_; } + private: + Message(const Vector& text, + v8::Debug::ClientData* data, + bool is_host_dispatch); + + Vector text_; + v8::Debug::ClientData* client_data_; + bool is_host_dispatch_; +}; + // A Queue of Vector 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 Get(); - void Put(const Vector& 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* 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 Get(); - void Put(const Vector& message); + Message Get(); + void Put(const Message& message); void Clear(); private: MessageQueue queue_; @@ -473,29 +503,29 @@ class Debugger { Handle event_data, bool auto_continue); static void SetEventListener(Handle callback, Handle 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 message); + static void SendMessage(Message message); // Send the JSON message for a debug event. static bool SendEventMessage(Handle event_data); // Add a debugger command to the command queue. - static void ProcessCommand(Vector command); + static void ProcessCommand(Vector 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 Call(Handle fun, Handle 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_; diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index c15359360..bc105555d 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -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::empty(), + new TestClientData())); + queue.Put(Message::NewCommand(Vector::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(), + 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 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);