NewFunction = 3,
BeforeCompile = 4,
AfterCompile = 5,
- ScriptCollected = 6
+ ScriptCollected = 6,
+ BreakForCommand = 7
};
*/
virtual Handle<Value> GetCallbackData() const = 0;
+ /**
+ * Client data passed to DebugBreakForCommand function. The
+ * debugger takes ownership of the data and will delete it even if
+ * there is no message handler.
+ */
+ virtual ClientData* GetClientData() const = 0;
+
virtual ~EventDetails() {}
};
// Break execution of JavaScript.
static void DebugBreak();
+ // Break execution of JavaScript (this method can be invoked from a
+ // non-VM thread) for further client command execution on a VM
+ // thread. Client data is then passed in EventDetails to
+ // EventCallback at the moment when the VM actually stops.
+ static void DebugBreakForCommand(ClientData* data = NULL);
+
// Message based interface. The message protocol is JSON. NOTE the message
// handler thread is not supported any more parameter must be false.
static void SetMessageHandler(MessageHandler handler,
}
+void Debug::DebugBreakForCommand(ClientData* data) {
+ if (!i::V8::IsRunning()) return;
+ i::Debugger::EnqueueDebugCommand(data);
+}
+
+
static v8::Debug::MessageHandler message_handler = NULL;
static void MessageHandlerWrapper(const v8::Debug::Message& message) {
DebuggerAgent* Debugger::agent_ = NULL;
LockingCommandMessageQueue Debugger::command_queue_(kQueueInitialSize);
Semaphore* Debugger::command_received_ = OS::CreateSemaphore(0);
+LockingCommandMessageQueue Debugger::event_command_queue_(kQueueInitialSize);
Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name,
event_data,
auto_continue);
}
- // Notify registered debug event listener. This can be either a C or a
- // JavaScript function.
- if (!event_listener_.is_null()) {
- if (event_listener_->IsProxy()) {
- // C debug event listener.
- Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
- v8::Debug::EventCallback2 callback =
- FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->proxy());
- EventDetailsImpl event_details(
- event,
- Handle<JSObject>::cast(exec_state),
- event_data,
- event_listener_data_);
- callback(event_details);
- } else {
- // JavaScript debug event listener.
- ASSERT(event_listener_->IsJSFunction());
- Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_));
-
- // Invoke the JavaScript debug event listener.
- const int argc = 4;
- Object** argv[argc] = { Handle<Object>(Smi::FromInt(event)).location(),
- exec_state.location(),
- Handle<Object>::cast(event_data).location(),
- event_listener_data_.location() };
- Handle<Object> result = Execution::TryCall(fun, Top::global(),
- argc, argv, &caught_exception);
- // Silently ignore exceptions from debug event listeners.
+ // Notify registered debug event listener. This can be either a C or
+ // a JavaScript function. Don't call event listener for v8::Break
+ // here, if it's only a debug command -- they will be processed later.
+ if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) {
+ CallEventCallback(event, exec_state, event_data, NULL);
+ }
+ // Process pending debug commands.
+ if (event == v8::Break) {
+ while (!event_command_queue_.IsEmpty()) {
+ CommandMessage command = event_command_queue_.Get();
+ if (!event_listener_.is_null()) {
+ CallEventCallback(v8::BreakForCommand,
+ exec_state,
+ event_data,
+ command.client_data());
+ }
+ command.Dispose();
}
}
}
+void Debugger::CallEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data) {
+ if (event_listener_->IsProxy()) {
+ CallCEventCallback(event, exec_state, event_data, client_data);
+ } else {
+ CallJSEventCallback(event, exec_state, event_data);
+ }
+}
+
+
+void Debugger::CallCEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data) {
+ Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
+ v8::Debug::EventCallback2 callback =
+ FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->proxy());
+ EventDetailsImpl event_details(
+ event,
+ Handle<JSObject>::cast(exec_state),
+ Handle<JSObject>::cast(event_data),
+ event_listener_data_,
+ client_data);
+ callback(event_details);
+}
+
+
+void Debugger::CallJSEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data) {
+ ASSERT(event_listener_->IsJSFunction());
+ Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_));
+
+ // Invoke the JavaScript debug event listener.
+ const int argc = 4;
+ Object** argv[argc] = { Handle<Object>(Smi::FromInt(event)).location(),
+ exec_state.location(),
+ Handle<Object>::cast(event_data).location(),
+ event_listener_data_.location() };
+ bool caught_exception = false;
+ Execution::TryCall(fun, Top::global(), argc, argv, &caught_exception);
+ // Silently ignore exceptions from debug event listeners.
+}
+
+
Handle<Context> Debugger::GetDebugContext() {
never_unload_debugger_ = true;
EnterDebugger debugger;
bool sendEventMessage = false;
switch (event) {
case v8::Break:
+ case v8::BreakForCommand:
sendEventMessage = !auto_continue;
break;
case v8::Exception:
}
+void Debugger::EnqueueDebugCommand(v8::Debug::ClientData* client_data) {
+ CommandMessage message = CommandMessage::New(Vector<uint16_t>(), client_data);
+ event_command_queue_.Put(message);
+
+ // Set the debug command break flag to have the command processed.
+ if (!Debug::InDebugger()) {
+ StackGuard::DebugCommand();
+ }
+}
+
+
bool Debugger::IsDebuggerActive() {
ScopedLock with(debugger_access_);
EventDetailsImpl::EventDetailsImpl(DebugEvent event,
Handle<JSObject> exec_state,
Handle<JSObject> event_data,
- Handle<Object> callback_data)
+ Handle<Object> callback_data,
+ v8::Debug::ClientData* client_data)
: event_(event),
exec_state_(exec_state),
event_data_(event_data),
- callback_data_(callback_data) {}
+ callback_data_(callback_data),
+ client_data_(client_data) {}
DebugEvent EventDetailsImpl::GetEvent() const {
}
+v8::Debug::ClientData* EventDetailsImpl::GetClientData() const {
+ return client_data_;
+}
+
+
CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
client_data_(NULL) {
}
EventDetailsImpl(DebugEvent event,
Handle<JSObject> exec_state,
Handle<JSObject> event_data,
- Handle<Object> callback_data);
+ Handle<Object> callback_data,
+ v8::Debug::ClientData* client_data);
virtual DebugEvent GetEvent() const;
virtual v8::Handle<v8::Object> GetExecutionState() const;
virtual v8::Handle<v8::Object> GetEventData() const;
virtual v8::Handle<v8::Context> GetEventContext() const;
virtual v8::Handle<v8::Value> GetCallbackData() const;
+ virtual v8::Debug::ClientData* GetClientData() const;
private:
DebugEvent event_; // Debug event causing the break.
- Handle<JSObject> exec_state_; // Current execution state.
- Handle<JSObject> event_data_; // Data associated with the event.
- Handle<Object> callback_data_; // User data passed with the callback when
- // it was registered.
+ Handle<JSObject> exec_state_; // Current execution state.
+ Handle<JSObject> event_data_; // Data associated with the event.
+ Handle<Object> callback_data_; // User data passed with the callback
+ // when it was registered.
+ v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand.
};
// Check whether there are commands in the command queue.
static bool HasCommands();
+ // Enqueue a debugger command to the command queue for event listeners.
+ static void EnqueueDebugCommand(v8::Debug::ClientData* client_data = NULL);
+
static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data,
bool* pending_exception);
static bool IsDebuggerActive();
private:
+ static void CallEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data);
+ static void CallCEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data);
+ static void CallJSEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data);
static void ListenersChanged();
static Mutex* debugger_access_; // Mutex guarding debugger variables.
static LockingCommandMessageQueue command_queue_;
static Semaphore* command_received_; // Signaled for each command received.
+ static LockingCommandMessageQueue event_command_queue_;
+
friend class EnterDebugger;
};
CheckDebuggerUnloaded();
}
+
+static void* expected_break_data;
+static bool was_debug_break_called;
+static bool was_debug_event_called;
+static void DebugEventBreakDataChecker(const v8::Debug::EventDetails& details) {
+ if (details.GetEvent() == v8::BreakForCommand) {
+ CHECK_EQ(expected_break_data, details.GetClientData());
+ was_debug_event_called = true;
+ } else if (details.GetEvent() == v8::Break) {
+ was_debug_break_called = true;
+ }
+}
+
+// Check that event details contain context where debug event occured.
+TEST(DebugEventBreakData) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetDebugEventListener2(DebugEventBreakDataChecker);
+
+ TestClientData::constructor_call_counter = 0;
+ TestClientData::destructor_call_counter = 0;
+
+ expected_break_data = NULL;
+ was_debug_event_called = false;
+ was_debug_break_called = false;
+ v8::Debug::DebugBreakForCommand();
+ v8::Script::Compile(v8::String::New("(function(x){return x;})(1);"))->Run();
+ CHECK(was_debug_event_called);
+ CHECK(!was_debug_break_called);
+
+ TestClientData* data1 = new TestClientData();
+ expected_break_data = data1;
+ was_debug_event_called = false;
+ was_debug_break_called = false;
+ v8::Debug::DebugBreakForCommand(data1);
+ v8::Script::Compile(v8::String::New("(function(x){return x+1;})(1);"))->Run();
+ CHECK(was_debug_event_called);
+ CHECK(!was_debug_break_called);
+
+ expected_break_data = NULL;
+ was_debug_event_called = false;
+ was_debug_break_called = false;
+ v8::Debug::DebugBreak();
+ v8::Script::Compile(v8::String::New("(function(x){return x+2;})(1);"))->Run();
+ CHECK(!was_debug_event_called);
+ CHECK(was_debug_break_called);
+
+ TestClientData* data2 = new TestClientData();
+ expected_break_data = data2;
+ was_debug_event_called = false;
+ was_debug_break_called = false;
+ v8::Debug::DebugBreak();
+ v8::Debug::DebugBreakForCommand(data2);
+ v8::Script::Compile(v8::String::New("(function(x){return x+3;})(1);"))->Run();
+ CHECK(was_debug_event_called);
+ CHECK(was_debug_break_called);
+
+ CHECK_EQ(2, TestClientData::constructor_call_counter);
+ CHECK_EQ(TestClientData::constructor_call_counter,
+ TestClientData::destructor_call_counter);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
#endif // ENABLE_DEBUGGER_SUPPORT