Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions devel/2050.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# [2050] 修复 get_bounding_rectangle 在 editor 无效时 SIGSEGV

## 1 相关文档
- [dddd.md](dddd.md) - 任务文档模板

## 2 任务相关的代码文件
- `src/Scheme/L5/glue_l5_extra.hpp` - `get_bounding_rectangle` 加入 `is_nil(ed)` 空指针检查
- `src/Edit/Interface/edit_mouse.cpp` - 拖拽事件改为异步分发,避免 GC 重入导致 editor vtable 损坏

## 3 如何测试

### 3.1 确定性测试(单元测试)
```
xmake b stem
```

### 3.2 非确定性测试(文档验证)
在包含图片的文档中对图片进行鼠标拖拽操作(`start-drag-left`),确认不再崩溃。

## 4 如何提交

提交前执行以下最少步骤:

```bash
gf fmt --changed-since=main
xmake b stem
```

## 5 What
两处修改配合修复 SIGSEGV:

1. **`glue_l5_extra.hpp`**:在 `get_bounding_rectangle` 中增加 `if (is_nil(ed)) return array<int>();` 空指针守卫
2. **`edit_mouse.cpp`**:将所有拖拽事件(`start-drag-left/right`、`dragging-left/right`、`end-drag-left/right`)从同步 `call_mouse_event` 改为异步 `delayed_call_mouse_event`,通过 `(delayed (:idle 1) ...)` 排队在空闲时执行,保证 FIFO 顺序

## 6 Why
Qt 事件循环处理鼠标事件时,`handle_mouse` 检测到图片上的 `start-drag-left`,同步调用 `call_mouse_event` 进入 Scheme。Scheme 的 `mouse-event` handler 触发 `(tree-bounding-rectangle t)`,回调 C++ 的 `get_bounding_rectangle`。该函数对 `get_current_editor()` 返回的 editor 调用虚函数 `get_window_extents()`。

在此同步 Scheme 调用期间,`tmscm_object_rep` 的构造/析构会触发 S7 GC。GC 过程可能通过 `gc_free` 回调或 finalizer 重新进入 Scheme,再回调 C++ 函数。在此 GC 重入路径中,`edit_interface_rep` 对象的 vtable 已损坏(对象可能处于部分析构状态),导致虚调用 `get_window_extents()` 分发到相邻 vtable 槽位的 `cancel_menu_action` thunk 上并 SIGSEGV。

`is_nil(ed)` 守卫作为纵深防御覆盖 editor 为 null 的情况,但单靠它不够——因为 editor 的 `rep` 指针非空,只是指向的对象 vtable 已损坏。

## 7 How
**edit_mouse.cpp**:将拖拽事件从 `call_mouse_event`(同步 call 进 Scheme)改为 `delayed_call_mouse_event`(`eval` 拼装 `(delayed (:idle 1) (mouse-event ...))` 排队)。同类型事件全部异步后,FIFO 顺序自然保持(`start-drag-left` 先入队先执行),消除了同步 Scheme 执行期间的 GC 重入窗口。
10 changes: 8 additions & 2 deletions src/Edit/Interface/edit_mouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,15 +1225,21 @@ edit_interface_rep::handle_mouse (string kind, SI x, SI y, int m, time_t t,
SI dist= (SI) (5 * PIXEL / magf);
rew = detect_left_drag ((void*) this, rew, x, y, t, m, dist);
if (rew == "start-drag-left") {
call_mouse_event (rew, left_x, left_y, m, t, data);
delayed_call_mouse_event (rew, left_x, left_y, m, t, data);
delayed_call_mouse_event ("dragging-left", x, y, m, t, data);
}
else if (rew == "dragging-left" || rew == "end-drag-left") {
delayed_call_mouse_event (rew, x, y, m, t, data);
}
else {
rew= detect_right_drag ((void*) this, rew, x, y, t, m, dist);
if (rew == "start-drag-right") {
call_mouse_event (rew, right_x, right_y, m, t, data);
delayed_call_mouse_event (rew, right_x, right_y, m, t, data);
delayed_call_mouse_event ("dragging-right", x, y, m, t, data);
}
else if (rew == "dragging-right" || rew == "end-drag-right") {
delayed_call_mouse_event (rew, x, y, m, t, data);
}
else call_mouse_event (rew, x, y, m, t, data);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/Scheme/L5/glue_l5_extra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ cpp_error () {

inline array<int>
get_bounding_rectangle (tree t) {
editor ed = get_current_editor ();
editor ed= get_current_editor ();
if (is_nil (ed)) return array<int> ();
rectangle wr = ed->get_window_extents ();
path p = reverse (obtain_ip (t));
selection sel = ed->search_selection (p * start (t), p * end (t));
Expand Down
Loading