|
4 | 4 | * 粒度: |
5 | 5 | * expr … stepIn / stepBack 全 AST ノード(最細粒度) |
6 | 6 | * human … humanStep / humanStepBack 人間にわかりやすい変化点 |
7 | | - * stmt … stepOver / stepOverBack 文単位 |
| 7 | + * stmt … #stmtForwardOnce / #stmtBackwardOnce 文単位(1クリック=1文。 |
| 8 | + * Program/BlockStatementを「複数の文の入れ物」として扱い、 |
| 9 | + * dbg.stepOver()をそのまま使わず内部でラップしている) |
8 | 10 | * call … callDepth 変化点 関数呼び出し/リターン境界(最粗粒度) |
9 | 11 | */ |
10 | 12 |
|
11 | 13 | import { sessionLogger } from './session-logger.js'; |
12 | 14 |
|
13 | 15 | /** @typedef {'expr'|'stmt'|'call'|'human'} Granularity */ |
14 | 16 |
|
| 17 | +/** |
| 18 | + * 「複数の文の入れ物」ノード種別。文単位ステップがここに滞在している間は |
| 19 | + * dbg.stepOver()(enter→対応するexitへジャンプ)を使わず、中へ入る(stepIn)。 |
| 20 | + * これを区別しないと、Program(cursor=0時点の現在ノード)や関数本体の |
| 21 | + * BlockStatement に対して stepOver() を適用してしまい、複数の文をまとめて |
| 22 | + * 1回でスキップしてしまう(実行直後に「文」を押すと最後まで進んでしまう不具合)。 |
| 23 | + */ |
| 24 | +const STMT_CONTAINER_TYPES = new Set(['Program', 'BlockStatement']); |
| 25 | + |
15 | 26 | export class StepController { |
16 | 27 | /** @type {import('./debugger-adapter.js').DebuggerAdapter} */ |
17 | 28 | #adapter; |
@@ -66,23 +77,23 @@ export class StepController { |
66 | 77 |
|
67 | 78 | // ── ステップ操作(文単位) ──────────────────────────────────────────────── |
68 | 79 |
|
69 | | - /** 文単位で 1 ステップ前進(stepOver) */ |
| 80 | + /** 文単位で 1 ステップ前進(次の文の完了地点まで、1クリック=1文) */ |
70 | 81 | stepStmtForward() { |
71 | 82 | const dbg = this.#adapter.getDebugger(); |
72 | 83 | if (!dbg || dbg.isDone()) return; |
73 | 84 | const before = dbg.cursor; |
74 | | - dbg.stepOver(); |
| 85 | + this.#stmtForwardOnce(dbg); |
75 | 86 | this.#adapter.moveTo(dbg.cursor); |
76 | 87 | const { loc, callDepth } = this.#locInfo(dbg); |
77 | 88 | sessionLogger.logStep('stmtFwd', before, dbg.cursor, loc, callDepth); |
78 | 89 | } |
79 | 90 |
|
80 | | - /** 文単位で 1 ステップ後退(stepOver の逆) */ |
| 91 | + /** 文単位で 1 ステップ後退(前の文の完了地点まで、1クリック=1文) */ |
81 | 92 | stepStmtBackward() { |
82 | 93 | const dbg = this.#adapter.getDebugger(); |
83 | 94 | if (!dbg || dbg.cursor === 0) return; |
84 | 95 | const before = dbg.cursor; |
85 | | - this.#stepOverBack(dbg); |
| 96 | + this.#stmtBackwardOnce(dbg); |
86 | 97 | this.#adapter.moveTo(dbg.cursor); |
87 | 98 | const { loc, callDepth } = this.#locInfo(dbg); |
88 | 99 | sessionLogger.logStep('stmtBack', before, dbg.cursor, loc, callDepth); |
@@ -245,18 +256,40 @@ export class StepController { |
245 | 256 | } |
246 | 257 |
|
247 | 258 | /** |
248 | | - * stmt 粒度の後退: |
249 | | - * 現在が exit → 対応する enter へ戻る。 |
250 | | - * 現在が enter → stepBack で 1 つ前へ。 |
| 259 | + * stmt 粒度の前進を1回分進める。 |
| 260 | + * - 実際の文の enter に到達するまで、入れ物ノード(STMT_CONTAINER_TYPES)の |
| 261 | + * enter・前の文の exit を stepIn() で1歩ずつ透過的に読み飛ばす |
| 262 | + * - 実際の文の enter に着いたら stepOver() で対応する exit へ一気に飛ぶ |
| 263 | + * (これが「1文実行」の着地点) |
251 | 264 | */ |
252 | | - #stepOverBack(dbg) { |
| 265 | + #stmtForwardOnce(dbg) { |
| 266 | + if (dbg.isDone()) return; |
| 267 | + while (!dbg.isDone()) { |
| 268 | + const ev = dbg.getCurrentEvent(); |
| 269 | + if (ev.phase === 'enter' && !STMT_CONTAINER_TYPES.has(ev.nodeType)) break; |
| 270 | + dbg.stepIn(); |
| 271 | + } |
| 272 | + if (!dbg.isDone()) dbg.stepOver(); |
| 273 | + } |
| 274 | + |
| 275 | + /** |
| 276 | + * stmt 粒度の後退を1回分進める。 |
| 277 | + * #stmtForwardOnce() を cursor=0 から再生し、目的の cursor の直前の着地点を採用する |
| 278 | + * (前進アルゴリズムと厳密に対称になることをテストで確認済み。ネストしたブロック・ |
| 279 | + * ループ・関数呼び出しなど、matchIdx を逆算する素朴な実装では正しく扱えないケースが |
| 280 | + * あったため、この「前進の再生」方式を採用した)。 |
| 281 | + */ |
| 282 | + #stmtBackwardOnce(dbg) { |
253 | 283 | if (dbg.cursor === 0) return; |
254 | | - dbg.cursor--; |
255 | | - const ev = dbg.getCurrentEvent(); |
256 | | - if (ev && ev.phase === 'exit') { |
257 | | - // matchIdx は対応する enter を指している |
258 | | - dbg.cursor = ev.matchIdx; |
| 284 | + const target = dbg.cursor; |
| 285 | + dbg.cursor = 0; |
| 286 | + let last = 0; |
| 287 | + while (dbg.cursor < target && !dbg.isDone()) { |
| 288 | + this.#stmtForwardOnce(dbg); |
| 289 | + if (dbg.cursor >= target) break; |
| 290 | + last = dbg.cursor; |
259 | 291 | } |
| 292 | + dbg.cursor = last; |
260 | 293 | } |
261 | 294 |
|
262 | 295 | /** |
|
0 commit comments