Fix #113479: Crash on script error after calling modal_handler_add()

Operators that added themselves as modal handlers would crash if there
was a Python exception in the script before returning.

Now modal handlers are removed an exception occurs in exec & invoke
operator callbacks.
This commit is contained in:
Campbell Barton
2024-02-27 16:16:52 +11:00
parent 341166c728
commit b16ef496a6
3 changed files with 46 additions and 2 deletions

View File

@@ -1338,13 +1338,18 @@ static int rna_operator_exec_cb(bContext *C, wmOperator *op)
RNA_parameter_list_create(&list, &opr, func);
RNA_parameter_set_lookup(&list, "context", &C);
op->type->rna_ext.call(C, &opr, func, &list);
const bool has_error = op->type->rna_ext.call(C, &opr, func, &list) == -1;
RNA_parameter_get_lookup(&list, "result", &ret);
result = *(int *)ret;
RNA_parameter_list_free(&list);
if (UNLIKELY(has_error)) {
/* A modal handler may have been added, ensure this is removed, see: #113479. */
WM_event_remove_modal_handler_all(op, false);
}
return result;
}
@@ -1388,13 +1393,18 @@ static int rna_operator_invoke_cb(bContext *C, wmOperator *op, const wmEvent *ev
RNA_parameter_list_create(&list, &opr, func);
RNA_parameter_set_lookup(&list, "context", &C);
RNA_parameter_set_lookup(&list, "event", &event);
op->type->rna_ext.call(C, &opr, func, &list);
const bool has_error = op->type->rna_ext.call(C, &opr, func, &list) == -1;
RNA_parameter_get_lookup(&list, "result", &ret);
result = *(int *)ret;
RNA_parameter_list_free(&list);
if (UNLIKELY(has_error)) {
/* A modal handler may have been added, ensure this is removed, see: #113479. */
WM_event_remove_modal_handler_all(op, false);
}
return result;
}

View File

@@ -545,6 +545,11 @@ wmEventHandler_Op *WM_event_add_modal_handler_ex(wmWindow *win,
ARegion *region,
wmOperator *op) ATTR_NONNULL(1, 4);
wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op) ATTR_NONNULL(1, 2);
void WM_event_remove_model_handler(ListBase *handlers, const wmOperator *op, bool postpone)
ATTR_NONNULL(1, 2);
void WM_event_remove_modal_handler_all(const wmOperator *op, bool postpone) ATTR_NONNULL(1);
/**
* Modal handlers store a pointer to an area which might be freed while the handler runs.
* Use this function to NULL all handler pointers to \a old_area.

View File

@@ -4468,6 +4468,35 @@ wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op)
return WM_event_add_modal_handler_ex(win, area, region, op);
}
void WM_event_remove_model_handler(ListBase *handlers, const wmOperator *op, const bool postpone)
{
LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
if (handler_base->type == WM_HANDLER_TYPE_OP) {
wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
if ((handler->op == op) || (op->opm && (handler->op == op->opm))) {
/* Handlers will be freed in #wm_handlers_do(). */
if (postpone) {
handler->head.flag |= WM_HANDLER_DO_FREE;
}
else {
BLI_remlink(handlers, handler);
wm_event_free_handler(&handler->head);
}
break;
}
}
}
}
void WM_event_remove_modal_handler_all(const wmOperator *op, const bool postpone)
{
Main *bmain = G_MAIN;
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
WM_event_remove_model_handler(&win->modalhandlers, op, postpone);
}
}
void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area)
{
LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {