| Commit message (Collapse) | Author | Age | Files | Lines |
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fetch per-instruction opcode bytes alongside the disasm listing and show
them in a column between the address and the mnemonic. Instruction length
comes from consecutive addresses (variable-length safe: x86 movabs shows
its full 10 bytes); the block over-fetches one instruction for the last
line's boundary, falling back to the function end for the final insn.
The column pads to the widest instruction across the whole function:
_fetch_block tracks a running max, and on load a background scan_bytes()
settles a stable width so padding doesn't jump as blocks stream in.
_op_field is shared by the rendered strip, _line_plain, and search _fmt
so cursor/match offsets stay aligned. 'o' toggles the column.
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
toggle_view's settle predicate (lambda: app._active != before) never
fired when tabbing toward pseudocode on a function Hex-Rays can't
decompile: App._apply_decomp snaps the view back to disasm, so _active
returns to its prior value -> full 20s settle timeout (x2 in _show_decomp,
~40s for drive pc). Recognize the decomp-failed fallback as settled.
Also harden two amplifiers surfaced by the same case:
- rpcclient: the CLI socket had no read timeout and would block forever
on any server slowness; add a bounded settimeout (IDATUI_RPC_TIMEOUT,
default 90s) with a clear error.
- domain.decompile: pass a bounded 15s timeout and cache failures, so a
failing decompile can't sit at the 30s client default or be re-run by
transport retries.
|
| |
|
|
|
|
|
|
| |
The pc command called only the read-only pseudocode RPC (runs off the UI
loop, never touches the screen), so LLM-driven sessions showed nothing on
the live pane. Compose goto + toggle_view + search so the real TUI
navigates to the function, makes the decomp pane the visibly-active view,
and jumps the cursor to the needle — then return the same text as before.
|
| |
|
|
|
|
|
|
|
|
| |
A design reference for bringing the idatui spawn/drive/introspect experience
to other terminal apps. Captures the invariants worth keeping, the reusable
core vs per-target Adapter split, a target taxonomy (embedded / control-
channel / black-box PTY), the three hard problems (injection, settle,
introspection) with technique gradients, tmux as the universal substrate,
and a concrete gdb worked example (TUI pane + gdb-Python plugin as the
adapter). No code.
|
| |
|
|
|
|
|
|
|
| |
The comment prompt is single-line, so a long note rendered as one giant
'//' line that ran off the right edge and got clipped. Interpret a literal
'\\n' (backslash-n) in the comment text as a real newline in _do_comment
(the single choke point for the ';' prompt and the RPC 'comment' verb) —
Hex-Rays renders each as its own '//' line. Verified live and locked in
the comment_func scenario (multi-line render); suite 104 green.
|
| |
|
|
|
|
|
|
| |
Round out idatui.drive with 'save' and 'retype <fn> <proto>' so the whole
common RE loop (orient/understand/act/persist) has a terse command. Update
docs/RPC.md and both skills (idatui, idatui-rpc) to recommend idatui.drive
as the day-to-day driving surface, with rpcclient/raw as the fallback for
unwrapped verbs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Driving via rpcclient meant re-exporting the socket every fresh shell,
long invocations, and piping ~every call through python -c to pull one
field or grep pseudocode. idatui.drive fixes that: it auto-resolves the
socket (the single live pane from the registry), prints compact text
instead of JSON, and bundles the common gestures:
where | go | pc <fn> [substr] | dis <fn> [n] | callees/callers <fn> |
names <substr> | rename <old> <new> | mv old=new... | note <fn> <text> |
screen | raw <method> k=v
So 'goto+rename+parse' becomes 'drive rename old new', and 'pseudocode |
python -c grep' becomes 'drive pc fn needle'. rpcclient stays the raw
transport. rpc_smoke drives it end-to-end (29 green).
|
| |
|
|
|
|
|
|
|
|
| |
Pressing ';' on the decompiler's signature or local-declaration lines did
nothing ('no address on this line to comment') because those lines carry
no /*0xEA*/ marker. Fall back to the current function's entry ea so
commenting the header annotates the function itself; the prompt says
'function comment @ ...'. Body-line comments are unchanged.
New scenario comment_func locks it (suite 104 green).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
The clock/title Header added noise (and made screen() non-deterministic);
remove it from the app itself — the Footer stays. Layout shifts up a row;
scenario suite still 101 green.
xrefs_from on a function was near-useless: the server's xrefs_from is
address-scoped, so passing the entry ea only returned the fall-through
from the first instruction. For a function target it now returns
whole-body references from the decompiler (callees + string/data refs:
{to,name,string,is_func,type}); an explicit 0xADDR stays address-scoped.
Verified live: xrefs_from main now lists setlocale/getopt_long/sub_* etc.
rpc_smoke: 25 green.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Before creating the TUI pane, spawn probes 127.0.0.1:8745 and, if nothing
is listening, launches ./spawn.sh in its own detached tmux pane and waits
for the port (server_started/server_pane are reported in the JSON). Only
for a local server, never a duplicate (spawn.sh binds the port), and it's
not killed on 'stop' (shared across TUIs). --no-ensure-server opts out;
IDATUI_SERVER_CMD overrides the launch command (used by the test).
tests/pane_smoke.py exercises the machinery with a dummy port-binder in a
pane (start / detect / idempotent / remote-guard), 5 green.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
cursor_on {word,line?,occurrence?} places the cursor on a token, verified
with the app's own tokenizer (so 'main' won't match inside 'domain');
rename/retype/follow gain an optional word= that runs it first — the
ergonomic way to edit a named symbol without a manual view+cursor dance.
Single-driver gate: the socket now serves one client at a time; a second
concurrent connection is refused with 'busy' (no multi-driver support yet,
by request). Sequential connections are unaffected.
screen gains format=text|html|svg (html/svg are colored — for an
out-of-band web viewer). rpc_smoke: 24 green.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
python -m idatui.pane spawn --open <bin> (or --db <session>) splits a
tmux pane running the TUI on a fresh RPC socket, blocks until it reports
ready (ping.ready; --open runs full auto-analysis so the default wait is
generous), and prints {sock,pane,ready,functions,module} as JSON. The
agent then drives it via idatui.rpcclient and tears it down with
'stop --sock <s>' (graceful quit -> kill-pane -> unlink). A small
registry in $XDG_RUNTIME_DIR tracks panes for 'list'/'stop'.
Verified end-to-end: spawn -> goto/pseudocode -> list -> stop.
|
| |
|
|
|
|
|
|
|
|
|
| |
Spawn-and-drive needs to know when the freshly-launched TUI is usable:
ping/state now carry {ready,functions,complete} (cheap; no health call)
and ping adds the module name. Add a graceful 'quit' verb (acks, then
exits on a short delay so the reply still flushes) and a self-documenting
'methods' table. Program-dependent verbs are refused with a clear
'not ready' instead of exploding in a worker while still loading.
rpc_smoke covers readiness, methods and quit (21 green).
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Introspection (offloaded to a thread so a big fetch can't freeze the
render): pseudocode (full body), disassembly (bounded), xrefs_to/from
(structured), resolve (name->ea). These let an agent reason about a
function and the call graph without scraping screen().
Semantic: select (choose the highlighted/nth item in the open xrefs or
symbol-palette list and activate it — the natural follow-up to xrefs/
symbols), search (/ or ? incremental in the active code view), save
(Ctrl+S). rpc_smoke exercises all of them end-to-end (18 green).
|
| | |
|
| |
|
|
|
|
|
|
|
|
| |
Drive the TUI from another tmux pane with no deps:
python -m idatui.rpcclient --sock <path> goto target=main
python -m idatui.rpcclient keys g m a i n enter
python -m idatui.rpcclient screen # dumps the raw screen text
Also importable as RpcClient for the agent. Socket via --sock or
IDATUI_RPC_SOCK. Booleans are coerced; numerics/addresses stay strings
(the server coerces numeric params).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Layer high-level verbs over the raw injection: goto/open, rename, comment,
retype, follow, back, toggle_view, hex, xrefs, symbols, structs, close,
plus fast move/cursor. Editing/goto verbs type through the real prompts
with a per-char delay (default 35ms) so viewers see it typed; movement
uses bare keypresses with a pump-only settle to stay snappy. Each verb
settles on an op-specific predicate where one exists (goto lands on the
target, follow grows the nav stack, toggle flips _active, modals open),
so the returned state is never stale.
rpc_smoke now drives the semantic path end-to-end (rename round-trip via
the prompt-fill seam), 11 green.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Run the TUI with --rpc <sock> and it renders normally while an asyncio
listener on the same loop lets another process drive it. Handlers touch
the UI directly (same loop, no thread hop), so every injected key has the
identical on-screen effect a keyboard would — the point for livestreaming.
Newline-delimited JSON. v1 methods: keys/text (raw injection, via the same
_press_keys the pilot uses; text can interleave wait:<ms> for a typed-out
look), state/view/screen/functions (introspection; screen() is a full
plain-text render of exactly what the viewer sees). No auth by design —
the socket is 0600, local only.
tests/rpc_smoke.py drives it end-to-end over a real socket (7 green).
|
| |
|
|
|
|
|
|
|
|
|
| |
Factor the pilot suite's ad-hoc 'poll until the UI reacted' loop into one
reusable module so the upcoming RPC driver and the tests share a single
source of truth for quiescence. wait_for() takes the yield strategy
(pilot.pause vs asyncio.sleep); drain()/workers_idle()/settle() give the
live driver a 'block until quiescent, then until pred holds' primitive
built on Textual's own _wait_for_screen + the worker manager.
Ctx.wait now delegates to wait_for; suite unchanged (101 green).
|
| |
|
|
|
|
| |
The scenario suite now covers every behavior the monolith did (all check
names accounted for, modulo renames), so drop the old single-session
runner.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the 1200-line monolithic run() — one giant pilot session whose
100 checks shared mutable app state, so a single break cascaded and every
edit meant re-running the whole ~49s suite — with independent @scenario
functions over one shared boot.
Each scenario re-establishes its own function context and is wrapped so a
crash (or a check failure) fails that scenario alone instead of aborting
the rest. A reset() baselines between scenarios: dismiss modals, hide the
names pane and input widgets, clear the filter, default _pref=decomp.
A Ctx helper wraps app+pilot (check/wait/press/open/goto_ui, biggest(),
pick_decomp_ref(), ...). CLI: --only <substr,...> runs a subset in a few
seconds (structs in ~1.5s vs 49s), --stop-after, --list. Per-scenario
timing is printed, which is what surfaced a decomp-default view-flip that
had been passing a jump+back check trivially.
Full run: 101 checks green in ~41s.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Add a file_regions server tool (get_fileregion_offset per segment) and
Program.file_regions/file_offset so a virtual address maps to its raw
on-disk offset without a format-specific header parser. The hex view now
shows a VA column and a file-offset column side by side, and the status
line reports file+off.
Goto moves to its own top-level #goto Input instead of overloading the
function-filter box (which is hidden with the names pane, so goto was
unreachable there). In the hex view 'g' jumps the cursor to an address
(bounds-checked against the image); elsewhere it navigates as before.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
New HexView: a line-virtualized 16-bytes/row dump of the whole loaded image,
addressed by virtual address. Format-agnostic — the range/segments come from IDA
(image_range over sections()), not any file header; gaps read back as zeros.
Backslash toggles it, synced to the address under the disasm/pseudocode cursor
(see the naked bytes of some code/data). In hex: hjkl/paging navigate a byte
cursor (status shows the section), Enter jumps the code view to the byte,
Tab/Esc/backslash return to the preferred code view. A third _active state; block
-cached + prefetched like the disasm model.
domain: HexModel + Program.hex_model/read_bytes/image_range (get_bytes regions).
Fixed a self-deadlock (hex_model held _lock while sections() re-locked).
Pilot: sync, actual bytes, byte-step, return. full suite 98/98.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Instead of parsing pseudocode text, add structured server tools (server/
patch_server.py, alongside del_type):
- func_types(addr): prototype + local variables (name/type/is_arg)
- set_lvar_type(addr,var,type): retype a decompiler local, working on auto/
register vars too (stock set_type only updates already-user-modified lvars)
'y' in a code view retypes what's under the cursor: a local variable (prompt
prefilled with its current type) or a function (prompt prefilled with its full
prototype). Applies via set_lvar_type / set_type, then recompiles + refreshes.
Copy-line moved off 'y' to Ctrl+Y.
domain: Program.func_types / set_function_type / set_lvar_type (LVar/FuncTypes).
Pilot: function-prototype retype (prefill + apply). full suite 94/94.
|
| |
|
|
|
|
|
|
| |
After a successful declare, re-fetch the canonical struct_source and put it back
in the editor, so the normalized layout/types show immediately instead of only
after re-selecting the struct from the list (e.g. a pasted one-liner becomes
multi-line, 'unsigned long' -> 'unsigned __int64'). _loaded_src is updated to the
formatted text so it isn't considered dirty. Pilot check. full suite 92/92.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A struct with a field IDA's C parser rejects (notably '__unused', a predefined
macro) failed to re-declare, but the failure was silent: the edit looked applied,
then reloading showed the old definition (lost work). Now:
- a failed declare shows a loud, styled 'save failed' status and, when it can,
names the offending reserved field ('__unused') instead of IDA's empty
'Failed to parse'; the type is left unchanged and the editor keeps your text.
- switching structs / starting new / closing with unsaved edits prompts to
discard first (ConfirmScreen), so edits aren't silently reloaded away.
Pilot: rejected save is loud/named/non-destructive; switching structs guards
unsaved edits. full suite 91/91.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
The app captures the mouse (SGR tracking), so terminal/tmux drag-select can't
grab text. Add explicit copy actions:
- code views: 'y' copies the current line
- struct editor: Ctrl+Y copies the selection, else the whole C definition
App._copy() emits OSC 52 (Textual copy_to_clipboard) AND, inside tmux, pipes
through 'tmux load-buffer -w -' — the combination is what actually reaches the
system clipboard over tmux/ssh. Status shows the char count.
full suite 87/87.
|
| |
|
|
|
|
|
|
|
|
| |
Delete was wired to the Del key but undiscoverable (modal has no footer) and
unguarded. Add a 'd' alias (Del still works), and route delete through a small
ConfirmScreen (Enter/y confirm, Esc/n cancel) since it's destructive. 'd' in the
editor pane types normally; only the list triggers delete. Hint updated to
'd/Del delete'.
Pilot: 'd' opens the confirm, Esc cancels (kept), Enter deletes. full suite 85/85.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
ida-pro-mcp has no delete-type tool, so struct-editor delete couldn't work. We
don't vendor/fork the server; instead server/patch_server.py idempotently appends
a del_type @tool to the installed ida_pro_mcp/ida_mcp/api_types.py, which every
worker (python -m ida_pro_mcp.idalib_server) imports -> it self-registers on the
shared MCP_SERVER. spawn.sh runs the patch before launching, so it's re-applied
on each start (survives reinstalls) and tracked in-repo instead of hand-editing
site-packages.
del_type calls ida_typeinf.del_named_type(get_idati(), name, NTF_TYPE). After a
supervisor restart, Program.delete_type() now succeeds; the struct editor's Del
removes the type and the list refreshes. Verified end-to-end; pilot 84/84.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
New StructEditor overlay: left is the list of local structs/unions, right is an
editable C definition. Enter loads a struct (reconstructed as C from its member
layout via type_inspect), Ctrl+S declares it (create or update via declare_type),
Ctrl+N starts a new one, Del deletes, Esc returns to the list then closes.
Domain: Struct model + Program.list_structs / struct_source / declare_type /
delete_type (anonymous $-types filtered).
Delete needs a 'del_type' tool the ida-pro-mcp server doesn't currently expose;
delete_type detects its absence and reports a clear message instead of failing.
Create/read/update work fully.
Pilot checks: open, list, view C, create, update-in-place, delete(-or-report),
close. full suite 84/84.
|
| |
|
|
|
|
|
|
|
|
|
| |
Opening a function that Hex-Rays can't decompile (import/PLT stubs, mis-analyzed
monsters) now shows the disassembly instead of a '/* decompilation failed */'
panel. A separate _pref (the preferred view, set by Tab) is kept, so only the
failing function falls back — the next decompilable function still opens as
pseudocode. _apply_decomp ignores a stale result if you've navigated away.
Pilot checks: failing func -> disasm (pref kept), next func -> pseudocode.
full suite 77/77.
|
| |
|
|
|
|
|
|
|
|
| |
Opening a function now shows the decompiler by default (Tab still toggles to
disassembly; the choice persists across opens). Startup focuses the decompiler
view, and Ctrl+B / back focus the active view rather than hard-coding disasm.
Tests: assert pseudocode-by-default on the first open, then normalize per-section
(the disasm-scrolling/search/follow blocks switch to disassembly explicitly).
full suite 75/75.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the docked names pane as the primary way to jump to symbols with a
command-palette overlay: Ctrl+N opens SymbolPalette, type to fuzzy-find (scored
subsequence match with matched-char highlighting), up/down to select, Enter to
open, Esc to close. Mouse-clickable too.
The docked FunctionsPanel now starts hidden and is still reachable/toggleable
with Ctrl+B (classic table view, sort, filter, goto all unchanged); a code view
takes focus on startup. Status hints 'Ctrl+N: find symbol'.
Pilot checks for open/fuzzy/subsequence/select/close. Tests reveal the docked
pane (Ctrl+B) for the existing table-driven checks. full suite 74/74.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Opening the xref dialog with 'x' now highlights the entry for the current site
(the instruction/pseudocode line under the cursor), so a long xref list can be
stepped through systematically without losing your place.
Capture the cursor's ea span when 'x' is pressed (disasm: the instruction .. the
next; decomp: the line's /*0xEA*/ anchor .. the next), and pre-select the xref
whose frm matches exactly, else the one within that span. Threaded through
_xrefs_{disasm,decomp} -> _xrefs_present -> XrefsScreen(preselect), which sets
OptionList.highlighted (also scrolls it into view).
Pilot check for the decomp path. full suite 68/68.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The xref list showed just the containing function name, so multiple sites in the
same function were indistinguishable ('sub_2300' repeated), and references not in
any function collapsed to a bare '?'.
- In-function sites now show fn_name+offset (e.g. main+0xb08), so each site is
distinct and you can see where in the function it is.
- Non-function sites (GOT/reloc data slots, loose thunks) show the section they
live in (.got, .data.rel.ro, .text, LOAD, ...) instead of '?'. Add
Program.sections()/section_of() over survey_binary's segment map (fetched once,
cached lazily).
Pilot checks for both. full suite 67/67.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously an xref jump only positioned the pseudocode line; the cursor sat at
column 0. Thread the referenced symbol's name (the token xrefs was invoked on,
or the callee's name) from the xref dialog through _on_xref_chosen -> _do_navigate,
and place the column at that token's whole-word start on the target line
(_decomp_col_for, computed on the marker-stripped line so it matches the display).
Works for both cross-function (via _apply_decomp cursor_x) and same-function (via
DecompView.goto) jumps.
Add a column assertion to the xref pilot test (asserts token-start when the token
is on the line, line-start otherwise -- robust to a stale server Hex-Rays cache).
full suite 65/65.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
DecompView.goto derived its scroll with _scroll_cursor_into_view() -> a plain
scroll_to, which updates scroll_offset but doesn't repaint at the new offset
(Textual only repaints on a rounded scroll change, and there's no layout pass
since the pane isn't reloaded). So a same-function xref jump landed the cursor
correctly but painted the old scroll until the next cursor move nudged a repaint.
Route goto's scroll (derived or explicit) through _apply_scroll, which re-applies
after the next refresh with layout=True -- the same pattern show() uses. The
cross-function path already worked because reloading + clearing the loading cover
forces a fresh paint. Document the gotcha in TEXTUAL_NOTES.
full suite 64/64.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The earlier xref->pseudocode-line fix only worked when the jump target was in a
DIFFERENT function (which reloads the pane). When the target was inside the
function already displayed, _show_active skipped the reload -> and never applied
the new dec_cursor, so the cursor stayed put (the reported 'jumping to xref in
decompile view doesn't work'; most xrefs land in the shown function).
_open_entry now detects the already-loaded case and repositions the decompiler
via a new DecompView.goto (moves cursor/scroll on the loaded text, no
re-highlight). Toggle (Tab) still goes through _show_active untouched. Add a
same-function xref-jump pilot check.
full suite 64/64.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Hex-Rays appends a /*0xEA*/ VMA marker to every pseudocode line (we fetch with
include_addresses). It's the only per-line address anchor the app has, so we
can't turn it off server-side without breaking follow/comments/xref-jumps.
Instead, strip it at display time: DecompView.show pulls each line's marker into
a parallel _line_eas list and highlights the cleaned text. _line_ea now reads
that list; _follow_decomp takes the line ea explicitly (was re-parsing the
marker). Stripping only edits within lines, so indices still align with the
domain's raw code (used by _decomp_line_for). Var-location comments (// [rsp+..])
are Hex-Rays output, not VMAs -> kept.
full suite 63/63.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Bind ';' (NavMixin, so both code views) to a comment prompt for the line under
the cursor. Resolve the line's address (_cursor_ea in disasm, the /*0xEA*/ marker
in pseudocode), prefill any existing comment (parsed from the rendered '//' text),
and apply via the set_comments tool; an empty value clears it.
After setting, reuse the name-generation invalidation (bump_names) so Hex-Rays is
force_recompiled lazily and the pseudocode/disasm refresh in place at the saved
position -- the new '// comment' shows on the line. Marks the IDB dirty (Ctrl+S).
Pilot checks for the pseudocode round-trip. full suite 63/63.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pressing 'n' on a Hex-Rays goto label (LABEL_n, or any 'goto TARGET') in the
decompiler routed to a 'local' rename, which the API rejects with a misleading
"Local variable 'LABEL_114' not found" — the user just saw a cryptic failure.
The rename tool has no label category (only func/global/local/stack) and idalib
exposes no other way to rename pseudocode labels.
Detect a label token under the cursor (_is_pseudocode_label) and report the
limitation up front instead of opening a prompt that's doomed to fail. Add a
pilot check (uses main's labels when the current function has none).
full suite 61/61.
|
| |
|
|
|
|
|
|
|
|
|
| |
- test_tui.py: add --stop-after <substr>, ending the run once a matching check
has been evaluated so you can iterate on one feature without paying for the
expensive tail (e.g. 'search finds' -> 5.6s vs 34.6s full). Default unchanged.
- app: skip _decomp_line_for on a plain function-entry jump (line 0 in both
views), avoiding a needless decompile on every by-name navigation in the
decompiler pane.
full suite 60/60.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Selecting an xref (or any address navigation) only positioned the disasm cursor
(NavEntry.cursor via index_of_ea); the pseudocode pane's dec_cursor stayed 0, so
in the decompiler view the jump landed at the top of the target function instead
of on the referencing line.
When the decompiler pane is active, resolve the target ea to its pseudocode line
via the per-line /*0xEA*/ markers (_decomp_line_for) and seed NavEntry.dec_cursor
so _apply_decomp lands + scrolls there. Add a pilot check for it.
Also halve the literal pilot.pause() settle delays in the suite (wait_until poll
step untouched): 40s -> 34.6s wall, stable across 3 runs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two latent ordering bugs, exposed once the suite actually opened the big function:
- the 'sub_1*' filter round-trip left the list filtered, so reusing biggest_i (an
unfiltered row index) clamped move_cursor to a tiny stub function (4-line
decompile, no calls), cascading into ~7 skipped/failed checks. Clear the filter
before reusing biggest_i.
- the xref-select sub-test navigates 'dis' to another function; the following
column-cursor block assumed the original function was still loaded at call_idx,
so h/l ran on an uncached (empty) line and clamped x to 0. Reload the original
function and materialize the call line first.
pilot suite 59/59.
|
| |
|
|
|
|
|
|
|
|
|
| |
xrefs_from on a call/branch returns the ordinary fall-through edge (target ==
the next instruction) as an indistinguishable type=code xref, and _follow_disasm
picked the first code edge -> 'follow' just advanced one line instead of entering
the callee (e.g. Enter on 'call cs:memset_ptr' stayed in the same function).
Plumb the next instruction's ea from the view and drop the edge whose target is
that next instruction, so follow lands on the real call/jump target. Mirrors the
ordinary-flow fix already applied to the xrefs path (e6375de).
|
| |
|
|
|
|
|
|
|
|
|
| |
xrefs data doesn't distinguish a call/jump from ordinary flow (both type=code),
so _xrefs_disasm's 'first code from-xref' subject picked the fall-through to the
NEXT instruction on a plain line. Its only xref is the flow edge back, so
selecting it jumped to the current function at line 0 instead of a real caller.
Drop the from-xref subject entirely: use the symbol under the cursor if any, else
the current address (xrefs-to a function entry == its callers). Now selecting an
xref lands on the exact referencing function AND line. pilot suite 59/59.
|