From acbbd92194aecba58ace7249182cb6a290f84842 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Jul 2024 10:33:00 +0200 Subject: [PATCH] Fixes a potential crash during breakpoint handling This fixes the following problem: * Set a breakpoint in a PCell code * Change the PCell, breakpoint gets triggered * While debugger is in breakpoint, close the view with the PCell To prevent any kind of interference, events are now disabled during breakpoint mode. As this interferes with the help dialog, you cannot open the help dialog either. --- src/lay/lay/layApplication.cc | 31 ++++++++++++----------- src/lay/lay/layMacroEditorDialog.cc | 38 ++++++++++++++++++++++------- src/lay/lay/layMacroEditorDialog.h | 8 ++++++ 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index a087bab37..b38114915 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -94,16 +94,23 @@ namespace lay // -------------------------------------------------------------------------------- // Exception handlers +static void close_transaction () +{ + // if any transaction is pending (this may happen when an operation threw an exception) + // close transactions. + // NOTE: don't do this in breakpoint mode as we do not want to interfere with things happening outside + if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting () && + !(lay::MacroEditorDialog::instance () && lay::MacroEditorDialog::instance ()->in_breakpoint ())) { + lay::MainWindow::instance ()->manager ().commit (); + } +} + static void ui_exception_handler_tl (const tl::Exception &ex, QWidget *parent) { // Prevents severe side effects if there are pending deferred methods tl::NoDeferredMethods silent; - // if any transaction is pending (this may happen when an operation threw an exception) - // close transactions. - if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) { - lay::MainWindow::instance ()->manager ().commit (); - } + close_transaction (); const tl::ExitException *gsi_exit = dynamic_cast (&ex); const tl::BreakException *gsi_break = dynamic_cast (&ex); @@ -155,13 +162,9 @@ static void ui_exception_handler_std (const std::exception &ex, QWidget *parent) // Prevents severe side effects if there are pending deferred methods tl::NoDeferredMethods silent; - // if any transaction is pending (this may happen when an operation threw an exception) - // close transactions. - if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) { - lay::MainWindow::instance ()->manager ().commit (); - } + close_transaction (); - tl::error << ex.what (); + tl::error << ex.what (); if (! parent) { parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance (); } @@ -173,11 +176,7 @@ static void ui_exception_handler_def (QWidget *parent) // Prevents severe side effects if there are pending deferred methods tl::NoDeferredMethods silent; - // if any transaction is pending (this may happen when an operation threw an exception) - // close transactions. - if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) { - lay::MainWindow::instance ()->manager ().commit (); - } + close_transaction (); if (! parent) { parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance (); diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 08df2a280..380b50b3d 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -1511,26 +1511,25 @@ MacroEditorDialog::eventFilter (QObject *obj, QEvent *event) if (lay::BusySection::is_busy ()) { -#if 0 - if (dynamic_cast (event) != 0 || dynamic_cast (event) != 0) { + if (m_in_breakpoint && (dynamic_cast (event) != 0 || dynamic_cast (event) != 0)) { - // In breakpoint or execution mode and while processing the events inside the debugger, + // In breakpoint mode and while processing the events inside the debugger, // ignore all input or paint events targeted to widgets which are not children of this or the assistant dialog. // Ignoring the paint event is required because otherwise a repaint action would be triggered on a layout which // is potentially unstable or inconsistent. // We nevertheless allow events send to a HelpDialog or ProgressWidget since those are vital for the application's // functionality and are known not to cause any interference. QObject *rec = obj; - while (rec && (rec != this && !dynamic_cast (rec) && !dynamic_cast (rec))) { + while (rec && rec != this) { rec = rec->parent (); } if (! rec) { // TODO: reschedule the paint events (?) + event->accept (); return true; } } -#endif } else { @@ -2267,19 +2266,35 @@ END_PROTECTED } void -MacroEditorDialog::help_requested(const QString &s) +MacroEditorDialog::help_requested (const QString &s) { +BEGIN_PROTECTED + // Do not allow modal popups in breakpoint mode - this would interfere with + // event filtering during breakpoint execution + if (m_in_breakpoint) { + throw tl::Exception (tl::to_string (tr ("The help function is not available in breakpoint mode."))); + } + lay::MainWindow::instance ()->show_assistant_topic (tl::to_string (s)); +END_PROTECTED } void -MacroEditorDialog::help_button_clicked() +MacroEditorDialog::help_button_clicked () { +BEGIN_PROTECTED + // Do not allow modal popups in breakpoint mode - this would interfere with + // event filtering during breakpoint execution + if (m_in_breakpoint) { + throw tl::Exception (tl::to_string (tr ("The help function is not available in breakpoint mode."))); + } + lay::MainWindow::instance ()->show_assistant_url ("int:/code/index.xml"); +END_PROTECTED } void -MacroEditorDialog::add_button_clicked() +MacroEditorDialog::add_button_clicked () { BEGIN_PROTECTED new_macro (); @@ -2287,7 +2302,7 @@ END_PROTECTED } lym::Macro * -MacroEditorDialog::new_macro() +MacroEditorDialog::new_macro () { ensure_writeable_collection_selected (); @@ -3451,6 +3466,11 @@ MacroEditorDialog::leave_breakpoint_mode () mp_current_interpreter = 0; do_update_ui_to_run_mode (); set_exec_point (0, -1, -1); + + // refresh UI that might have been spoiled because we filter events + if (lay::MainWindow::instance ()) { + lay::MainWindow::instance ()->update (); + } } void diff --git a/src/lay/lay/layMacroEditorDialog.h b/src/lay/lay/layMacroEditorDialog.h index 001829924..85befea08 100644 --- a/src/lay/lay/layMacroEditorDialog.h +++ b/src/lay/lay/layMacroEditorDialog.h @@ -166,6 +166,14 @@ public: return m_in_exec; } + /** + * @brief Returns true while the macro IDE is in breakpoint mode + */ + bool in_breakpoint () const + { + return m_in_breakpoint; + } + /** * @brief Selects the current category in the tree view */