Provide debug event listener with access to the debuggee context. Also introduce...
authoryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 20 May 2010 17:15:46 +0000 (17:15 +0000)
committeryurys@chromium.org <yurys@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 20 May 2010 17:15:46 +0000 (17:15 +0000)
Review URL: http://codereview.chromium.org/2108024

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4693 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8-debug.h
src/api.cc
src/debug.cc
src/debug.h
test/cctest/test-debug.cc

index f7b4fa1..c53b634 100644 (file)
@@ -144,6 +144,39 @@ class EXPORT Debug {
   
 
   /**
+   * An event details object passed to the debug event listener.
+   */
+  class EventDetails {
+   public:
+    /**
+     * Event type.
+     */
+    virtual DebugEvent GetEvent() const = 0;
+
+    /**
+     * Access to execution state and event data of the debug event. Don't store
+     * these cross callbacks as their content becomes invalid.
+     */
+    virtual Handle<Object> GetExecutionState() const = 0;
+    virtual Handle<Object> GetEventData() const = 0;
+
+    /**
+     * Get the context active when the debug event happened. Note this is not
+     * the current active context as the JavaScript part of the debugger is
+     * running in it's own context which is entered at this point.
+     */
+    virtual Handle<Context> GetEventContext() const = 0;
+
+    /**
+     * Client data passed with the corresponding callbak whet it was registered.
+     */
+    virtual Handle<Value> GetCallbackData() const = 0;
+
+    virtual ~EventDetails() {}
+  };
+
+
+  /**
    * Debug event callback function.
    *
    * \param event the type of the debug event that triggered the callback
@@ -157,6 +190,15 @@ class EXPORT Debug {
                                 Handle<Object> event_data,
                                 Handle<Value> data);
 
+  /**
+   * Debug event callback function.
+   *
+   * \param event_details object providing information about the debug event
+   *
+   * A EventCallback2 does not take possession of the event data,
+   * and must not rely on the data persisting after the handler returns.
+   */
+  typedef void (*EventCallback2)(const EventDetails& event_details);
 
   /**
    * Debug message callback function.
@@ -165,7 +207,7 @@ class EXPORT Debug {
    * \param length length of the message
    * \param client_data the data value passed when registering the message handler
 
-   * A MessageHandler does not take posession of the message string,
+   * A MessageHandler does not take possession of the message string,
    * and must not rely on the data persisting after the handler returns.
    *
    * This message handler is deprecated. Use MessageHandler2 instead.
@@ -178,7 +220,7 @@ class EXPORT Debug {
    *
    * \param message the debug message handler message object
 
-   * A MessageHandler does not take posession of the message data,
+   * A MessageHandler does not take possession of the message data,
    * and must not rely on the data persisting after the handler returns.
    */
   typedef void (*MessageHandler2)(const Message& message);
@@ -196,6 +238,8 @@ class EXPORT Debug {
   // Set a C debug event listener.
   static bool SetDebugEventListener(EventCallback that,
                                     Handle<Value> data = Handle<Value>());
+  static bool SetDebugEventListener2(EventCallback2 that,
+                                     Handle<Value> data = Handle<Value>());
 
   // Set a JavaScript debug event listener.
   static bool SetDebugEventListener(v8::Handle<v8::Object> that,
index 42c8aeb..d944d3c 100644 (file)
@@ -3992,10 +3992,40 @@ 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
+
+static v8::Debug::EventCallback event_callback = NULL;
+
+static void EventCallbackWrapper(const v8::Debug::EventDetails& event_details) {
+  if (event_callback) {
+    event_callback(event_details.GetEvent(),
+                   event_details.GetExecutionState(),
+                   event_details.GetEventData(),
+                   event_details.GetCallbackData());
+  }
+}
+
+
 bool Debug::SetDebugEventListener(EventCallback that, Handle<Value> data) {
   EnsureInitialized("v8::Debug::SetDebugEventListener()");
   ON_BAILOUT("v8::Debug::SetDebugEventListener()", return false);
   ENTER_V8;
+
+  event_callback = that;
+
+  HandleScope scope;
+  i::Handle<i::Object> proxy = i::Factory::undefined_value();
+  if (that != NULL) {
+    proxy = i::Factory::NewProxy(FUNCTION_ADDR(EventCallbackWrapper));
+  }
+  i::Debugger::SetEventListener(proxy, Utils::OpenHandle(*data));
+  return true;
+}
+
+
+bool Debug::SetDebugEventListener2(EventCallback2 that, Handle<Value> data) {
+  EnsureInitialized("v8::Debug::SetDebugEventListener2()");
+  ON_BAILOUT("v8::Debug::SetDebugEventListener2()", return false);
+  ENTER_V8;
   HandleScope scope;
   i::Handle<i::Object> proxy = i::Factory::undefined_value();
   if (that != NULL) {
index bf1f893..8cb95ef 100644 (file)
@@ -72,6 +72,17 @@ static Handle<Code> ComputeCallDebugPrepareStepIn(int argc) {
 }
 
 
+static v8::Handle<v8::Context> GetDebugEventContext() {
+  Handle<Context> context = Debug::debugger_entry()->GetContext();
+  // Top::context() may have been NULL when "script collected" event occured.
+  if (*context == NULL) {
+    return v8::Local<v8::Context>();
+  }
+  Handle<Context> global_context(context->global_context());
+  return v8::Utils::ToLocal(global_context);
+}
+
+
 BreakLocationIterator::BreakLocationIterator(Handle<DebugInfo> debug_info,
                                              BreakLocatorType type) {
   debug_info_ = debug_info;
@@ -2112,12 +2123,14 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event,
     if (event_listener_->IsProxy()) {
       // C debug event listener.
       Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
-      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(event_data),
-               v8::Utils::ToLocal(Handle<Object>::cast(event_listener_data_)));
+      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());
@@ -2643,14 +2656,10 @@ v8::Handle<v8::String> MessageImpl::GetJSON() const {
 
 
 v8::Handle<v8::Context> MessageImpl::GetEventContext() const {
-  Handle<Context> context = Debug::debugger_entry()->GetContext();
-  // Top::context() may have been NULL when "script collected" event occured.
-  if (*context == NULL) {
-    ASSERT(event_ == v8::ScriptCollected);
-    return v8::Local<v8::Context>();
-  }
-  Handle<Context> global_context(context->global_context());
-  return v8::Utils::ToLocal(global_context);
+  v8::Handle<v8::Context> context = GetDebugEventContext();
+  // Top::context() may be NULL when "script collected" event occures.
+  ASSERT(!context.IsEmpty() || event_ == v8::ScriptCollected);
+  return GetDebugEventContext();
 }
 
 
@@ -2659,6 +2668,41 @@ v8::Debug::ClientData* MessageImpl::GetClientData() const {
 }
 
 
+EventDetailsImpl::EventDetailsImpl(DebugEvent event,
+                                   Handle<JSObject> exec_state,
+                                   Handle<JSObject> event_data,
+                                   Handle<Object> callback_data)
+    : event_(event),
+      exec_state_(exec_state),
+      event_data_(event_data),
+      callback_data_(callback_data) {}
+
+
+DebugEvent EventDetailsImpl::GetEvent() const {
+  return event_;
+}
+
+
+v8::Handle<v8::Object> EventDetailsImpl::GetExecutionState() const {
+  return v8::Utils::ToLocal(exec_state_);
+}
+
+
+v8::Handle<v8::Object> EventDetailsImpl::GetEventData() const {
+  return v8::Utils::ToLocal(event_data_);
+}
+
+
+v8::Handle<v8::Context> EventDetailsImpl::GetEventContext() const {
+  return GetDebugEventContext();
+}
+
+
+v8::Handle<v8::Value> EventDetailsImpl::GetCallbackData() const {
+  return v8::Utils::ToLocal(callback_data_);
+}
+
+
 CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
                                    client_data_(NULL) {
 }
index 9b8d389..e2eecb8 100644 (file)
@@ -524,6 +524,27 @@ class MessageImpl: public v8::Debug::Message {
 };
 
 
+// Details of the debug event delivered to the debug event listener.
+class EventDetailsImpl : public v8::Debug::EventDetails {
+ public:
+  EventDetailsImpl(DebugEvent event,
+                   Handle<JSObject> exec_state,
+                   Handle<JSObject> event_data,
+                   Handle<Object> callback_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;
+ 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.
+};
+
+
 // 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
index d90be8e..4b4c950 100644 (file)
@@ -6196,7 +6196,28 @@ TEST(DebugContextIsPreservedBetweenAccesses) {
   v8::Local<v8::Context> context1 = v8::Debug::GetDebugContext();
   v8::Local<v8::Context> context2 = v8::Debug::GetDebugContext();
   CHECK_EQ(*context1, *context2);
-  // Make sure debugger is unloaded before running other tests.
-  v8::internal::ForceUnloadDebugger();
+}
+
+
+static v8::Handle<v8::Value> expected_callback_data;
+static void DebugEventContextChecker(const v8::Debug::EventDetails& details) {
+  CHECK(details.GetEventContext() == expected_context);
+  CHECK_EQ(expected_callback_data, details.GetCallbackData());
+}
+
+// Check that event details contain context where debug event occured.
+TEST(DebugEventContext) {
+  v8::HandleScope scope;
+  expected_callback_data = v8::Int32::New(2010);
+  v8::Debug::SetDebugEventListener2(DebugEventContextChecker,
+                                    expected_callback_data);
+  expected_context = v8::Context::New();
+  v8::Context::Scope context_scope(expected_context);
+  v8::Script::Compile(v8::String::New("(function(){debugger;})();"))->Run();
+  expected_context.Dispose();
+  expected_context.Clear();
+  v8::Debug::SetDebugEventListener(NULL);
+  expected_context_data = v8::Handle<v8::Value>();
   CheckDebuggerUnloaded();
 }
+