};
-/**
- * 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.
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.
// --- 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;
}
-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);
}
// 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() {
: 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();
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);
};
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);
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)),
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;
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);
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
}
-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();
}
-void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
- void* data) {
+void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler) {
host_dispatch_handler_ = handler;
- host_dispatch_handler_data_ = data;
}
// 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();
}
}
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());
// 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.
}
-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.
// 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();
}
}
}
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_;
}
-void MessageQueue::Put(const Vector<uint16_t>& message) {
+void MessageQueue::Put(const Message& message) {
if ((end_ + 1) % size_ == start_) {
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.
}
}
-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());
}
};
+// 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 {
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.
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_;
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);
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_;
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.
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)) {
fflush(stdout);
}
-
void MessageQueueDebuggerThread::Run() {
const int kBufferSize = 1000;
uint16_t buffer_1[kBufferSize];
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
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)) {
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);
static void DummyMessageHandler(const uint16_t* message,
- int length, void *data) {
+ int length,
+ v8::Debug::ClientData* client_data) {
}
// 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;
// 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.
int host_dispatch_hit_count = 0;
-static void HostDispatchHandlerHitCount(void* dispatch, void *data) {
+static void HostDispatchHandlerHitCount(v8::Debug::ClientData* dispatch) {
host_dispatch_hit_count++;
}
// 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);