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.
This commit is contained in:
Matthias Koefferlein 2024-07-13 10:33:00 +02:00
parent 12d09687cd
commit acbbd92194
3 changed files with 52 additions and 25 deletions

View File

@ -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 <const tl::ExitException *> (&ex);
const tl::BreakException *gsi_break = dynamic_cast <const tl::BreakException *> (&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 ();

View File

@ -1511,26 +1511,25 @@ MacroEditorDialog::eventFilter (QObject *obj, QEvent *event)
if (lay::BusySection::is_busy ()) {
#if 0
if (dynamic_cast <QInputEvent *> (event) != 0 || dynamic_cast <QPaintEvent *> (event) != 0) {
if (m_in_breakpoint && (dynamic_cast <QInputEvent *> (event) != 0 || dynamic_cast <QPaintEvent *> (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<lay::HelpDialog *> (rec) && !dynamic_cast<lay::ProgressWidget *> (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

View File

@ -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
*/