diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 0996db9d890..e8a6a5075bf 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" #include "core/debugger/engine_debugger.h" +#include "core/debugger/script_debugger.h" #include "core/io/file_access_compressed.h" #include "core/io/file_access_encrypted.h" #include "core/io/marshalls.h" @@ -1919,6 +1920,16 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) { ::EngineDebugger::get_singleton()->send_message(p_msg, p_data); } +void EngineDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { + ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't send debug. No active debugger"); + ::EngineDebugger::get_singleton()->debug(p_can_continue, p_is_error_breakpoint); +} + +void EngineDebugger::script_debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_is_error_breakpoint) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't send debug. No active debugger"); + ::EngineDebugger::get_script_debugger()->debug(p_lang, p_can_continue, p_is_error_breakpoint); +} + Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { Callable &capture = *(Callable *)p_user; if (!capture.is_valid()) { @@ -1935,6 +1946,56 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra return OK; } +void EngineDebugger::line_poll() { + ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't poll. No active debugger"); + ::EngineDebugger::get_singleton()->line_poll(); +} + +void EngineDebugger::set_lines_left(int p_lines) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't set lines left. No active debugger"); + ::EngineDebugger::get_script_debugger()->set_lines_left(p_lines); +} + +int EngineDebugger::get_lines_left() const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), 0, "Can't get lines left. No active debugger"); + return ::EngineDebugger::get_script_debugger()->get_lines_left(); +} + +void EngineDebugger::set_depth(int p_depth) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't set depth. No active debugger"); + ::EngineDebugger::get_script_debugger()->set_depth(p_depth); +} + +int EngineDebugger::get_depth() const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), 0, "Can't get depth. No active debugger"); + return ::EngineDebugger::get_script_debugger()->get_depth(); +} + +bool EngineDebugger::is_breakpoint(int p_line, const StringName &p_source) const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), false, "Can't check breakpoint. No active debugger"); + return ::EngineDebugger::get_script_debugger()->is_breakpoint(p_line, p_source); +} + +bool EngineDebugger::is_skipping_breakpoints() const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), false, "Can't check skipping breakpoint. No active debugger"); + return ::EngineDebugger::get_script_debugger()->is_skipping_breakpoints(); +} + +void EngineDebugger::insert_breakpoint(int p_line, const StringName &p_source) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't insert breakpoint. No active debugger"); + ::EngineDebugger::get_script_debugger()->insert_breakpoint(p_line, p_source); +} + +void EngineDebugger::remove_breakpoint(int p_line, const StringName &p_source) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't remove breakpoint. No active debugger"); + ::EngineDebugger::get_script_debugger()->remove_breakpoint(p_line, p_source); +} + +void EngineDebugger::clear_breakpoints() { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't clear breakpoints. No active debugger"); + ::EngineDebugger::get_script_debugger()->clear_breakpoints(); +} + EngineDebugger::~EngineDebugger() { for (const KeyValue &E : captures) { ::EngineDebugger::unregister_message_capture(E.key); @@ -1960,7 +2021,23 @@ void EngineDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EngineDebugger::unregister_message_capture); ClassDB::bind_method(D_METHOD("has_capture", "name"), &EngineDebugger::has_capture); + ClassDB::bind_method(D_METHOD("line_poll"), &EngineDebugger::line_poll); + ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EngineDebugger::send_message); + ClassDB::bind_method(D_METHOD("debug", "can_continue", "is_error_breakpoint"), &EngineDebugger::debug, DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("script_debug", "language", "can_continue", "is_error_breakpoint"), &EngineDebugger::script_debug, DEFVAL(true), DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("set_lines_left", "lines"), &EngineDebugger::set_lines_left); + ClassDB::bind_method(D_METHOD("get_lines_left"), &EngineDebugger::get_lines_left); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &EngineDebugger::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &EngineDebugger::get_depth); + + ClassDB::bind_method(D_METHOD("is_breakpoint", "line", "source"), &EngineDebugger::is_breakpoint); + ClassDB::bind_method(D_METHOD("is_skipping_breakpoints"), &EngineDebugger::is_skipping_breakpoints); + ClassDB::bind_method(D_METHOD("insert_breakpoint", "line", "source"), &EngineDebugger::insert_breakpoint); + ClassDB::bind_method(D_METHOD("remove_breakpoint", "line", "source"), &EngineDebugger::remove_breakpoint); + ClassDB::bind_method(D_METHOD("clear_breakpoints"), &EngineDebugger::clear_breakpoints); } } // namespace core_bind diff --git a/core/core_bind.h b/core/core_bind.h index 148e0ad83e4..febc33a9c16 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -576,9 +576,25 @@ public: bool has_capture(const StringName &p_name); void send_message(const String &p_msg, const Array &p_data); + void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false); + void script_debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false); static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); + void line_poll(); + + void set_lines_left(int p_lines); + int get_lines_left() const; + + void set_depth(int p_depth); + int get_depth() const; + + bool is_breakpoint(int p_line, const StringName &p_source) const; + bool is_skipping_breakpoints() const; + void insert_breakpoint(int p_line, const StringName &p_source); + void remove_breakpoint(int p_line, const StringName &p_source); + void clear_breakpoints(); + EngineDebugger() { singleton = this; } ~EngineDebugger(); }; diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index a18ef8d4d7f..7b643e46378 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -132,6 +132,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_debug_get_stack_level_line, "level"); GDVIRTUAL_BIND(_debug_get_stack_level_function, "level"); + GDVIRTUAL_BIND(_debug_get_stack_level_source, "level"); GDVIRTUAL_BIND(_debug_get_stack_level_locals, "level", "max_subitems", "max_depth"); GDVIRTUAL_BIND(_debug_get_stack_level_members, "level", "max_subitems", "max_depth"); GDVIRTUAL_BIND(_debug_get_stack_level_instance, "level"); diff --git a/doc/classes/EditorDebuggerPlugin.xml b/doc/classes/EditorDebuggerPlugin.xml index 4c602b83594..a519e43bc69 100644 --- a/doc/classes/EditorDebuggerPlugin.xml +++ b/doc/classes/EditorDebuggerPlugin.xml @@ -47,6 +47,21 @@ + + + + + + + Override this method to be notified when a breakpoint is set in the editor. + + + + + + Override this method to be notified when all breakpoints are cleared in the editor. + + @@ -56,6 +71,14 @@ Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the message (which you can retrieve via [method get_session]). + + + + + + Override this method to be notified when a breakpoint line has been clicked in the debugger breakpoint panel. + + diff --git a/doc/classes/EditorDebuggerSession.xml b/doc/classes/EditorDebuggerSession.xml index c6c632be010..b4e754cc7e1 100644 --- a/doc/classes/EditorDebuggerSession.xml +++ b/doc/classes/EditorDebuggerSession.xml @@ -50,6 +50,15 @@ Sends the given [param message] to the attached remote instance, optionally passing additionally [param data]. See [EngineDebugger] for how to retrieve those messages. + + + + + + + Enables or disables a specific breakpoint based on [param enabled], updating the Editor Breakpoint Panel accordingly. + + diff --git a/doc/classes/EngineDebugger.xml b/doc/classes/EngineDebugger.xml index 29ac04f097e..7583520da0d 100644 --- a/doc/classes/EngineDebugger.xml +++ b/doc/classes/EngineDebugger.xml @@ -9,6 +9,32 @@ + + + + Clears all breakpoints. + + + + + + + + Starts a debug break in script execution, optionally specifying whether the program can continue based on [param can_continue] and whether the break was due to a breakpoint. + + + + + + Returns the current debug depth. + + + + + + Returns the number of lines that remain. + + @@ -23,12 +49,28 @@ Returns [code]true[/code] if a profiler with the given name is present otherwise [code]false[/code]. + + + + + + Inserts a new breakpoint with the given [param source] and [param line]. + + Returns [code]true[/code] if the debugger is active otherwise [code]false[/code]. + + + + + + Returns [code]true[/code] if the given [param source] and [param line] represent an existing breakpoint. + + @@ -36,6 +78,18 @@ Returns [code]true[/code] if a profiler with the given name is present and active otherwise [code]false[/code]. + + + + Returns [code]true[/code] if the debugger is skipping breakpoints otherwise [code]false[/code]. + + + + + + Forces a processing loop of debugger events. The purpose of this method is just processing events every now and then when the script might get too busy, so that bugs like infinite loops can be caught + + @@ -70,6 +124,23 @@ Registers a profiler with the given [param name]. See [EngineProfiler] for more information. + + + + + + Removes a breakpoint with the given [param source] and [param line]. + + + + + + + + + Starts a debug break in script execution, optionally specifying whether the program can continue based on [param can_continue] and whether the break was due to a breakpoint. + + @@ -78,6 +149,20 @@ Sends a message with given [param message] and [param data] array. + + + + + Sets the current debugging depth. + + + + + + + Sets the current debugging lines that remain. + + diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml index a453866e276..cc47ca274d1 100644 --- a/doc/classes/ScriptLanguageExtension.xml +++ b/doc/classes/ScriptLanguageExtension.xml @@ -108,6 +108,13 @@ + + + + + Returns the source associated with a given debug stack position. + + diff --git a/editor/plugins/editor_debugger_plugin.cpp b/editor/plugins/editor_debugger_plugin.cpp index c96bec6e7f9..fbb389ccb40 100644 --- a/editor/plugins/editor_debugger_plugin.cpp +++ b/editor/plugins/editor_debugger_plugin.cpp @@ -56,6 +56,7 @@ void EditorDebuggerSession::_bind_methods() { ClassDB::bind_method(D_METHOD("is_active"), &EditorDebuggerSession::is_active); ClassDB::bind_method(D_METHOD("add_session_tab", "control"), &EditorDebuggerSession::add_session_tab); ClassDB::bind_method(D_METHOD("remove_session_tab", "control"), &EditorDebuggerSession::remove_session_tab); + ClassDB::bind_method(D_METHOD("set_breakpoint", "path", "line", "enabled"), &EditorDebuggerSession::set_breakpoint); ADD_SIGNAL(MethodInfo("started")); ADD_SIGNAL(MethodInfo("stopped")); @@ -100,6 +101,11 @@ bool EditorDebuggerSession::is_active() { return debugger->is_session_active(); } +void EditorDebuggerSession::set_breakpoint(const String &p_path, int p_line, bool p_enabled) { + ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger."); + debugger->set_breakpoint(p_path, p_line, p_enabled); +} + void EditorDebuggerSession::detach_debugger() { if (!debugger) { return; @@ -184,10 +190,31 @@ bool EditorDebuggerPlugin::capture(const String &p_message, const Array &p_data, return false; } +void EditorDebuggerPlugin::goto_script_line(const Ref