做了什么: - 新增 tools/tdc_lift.py: 字节码 -> 可读伪代码 (AGENTS.md §3.3 步骤5) * 表达式栈还原 (算术/比较/调用/属性) * 寄存器引用惯用法还原 (PUSH_IMM_ARRAY n + MAKE_REG_PROP_REF -> rN[key]) * 控制流 label/goto/if; CREATE_CLOSURE 递归展开成具名函数 - 新增 tools/tdc_query.py: who rN / at @pc / funcs / grep RE (对齐 §4.2 验收) - 新增 tools/tdc_keytrace.js: 用真 VM 当神谕, 定位某值在哪段字节码诞生 - VM_SPEC §7/§8 重写为"自上而下五件套 + 路线", 并把 tdc_vm_run.py 标注为归档(打地鼠陷阱) - 修正 lift 的 [n]->rN 归一 与 标签刷屏 为什么: - 之前用 Python 复刻 JS 运行时去"跑"VM = §2.3 铁律2 的打地鼠陷阱, 收敛极慢 - §3.3 步骤5 要的是"读指令流得到算法; 闭包递归展开" -> 提升(lift)才是正路 - §2.1 最后一步: 读出算法后用目标语言原生重写, 跳过 VM/环境层 怎么验证: - lift 首次把 XTEA 轮读成可读式: r2=(r2+2654435769); r?+=(((r6<<4)^(r6>>>5))+r6)^(r2+(r7[((r2>>>11)&3)]+855311)) - query 三个验收问题均可秒答: who r2 / at @9285 / funcs
110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
TDC VM 工具链 ⑤ 查询 CLI —— 对齐 AGENTS.md §4.2 验收标准
|
|
|
|
验收要求能秒答三个问题:
|
|
* 「这个字段/寄存器谁写的」 -> who <rN>
|
|
* 「这段代码在哪(属于哪个函数)」 -> at @pc
|
|
* 「有多少个子程序/闭包」 -> funcs
|
|
另附 grep 正则查询。
|
|
|
|
用法(先跑 tools/tdc_lift.py 生成 <name>.lifted.txt):
|
|
.venv/bin/python tools/tdc_query.py <lifted.txt> who r7
|
|
.venv/bin/python tools/tdc_query.py <lifted.txt> at @9285
|
|
.venv/bin/python tools/tdc_query.py <lifted.txt> funcs
|
|
.venv/bin/python tools/tdc_query.py <lifted.txt> grep ">>> 11"
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
from typing import List, Tuple
|
|
|
|
|
|
def parse(path: str):
|
|
"""切分 lifted 文件 -> [(func_name, [ (pc或None, 行文本) ])]"""
|
|
funcs: List[Tuple[str, List[Tuple[int, str]]]] = []
|
|
cur_name, cur_lines = None, []
|
|
cur_pc = [-1]
|
|
for raw in open(path, encoding="utf-8"):
|
|
line = raw.rstrip("\n")
|
|
m = re.match(r"\s*function\s+([A-Za-z0-9_]+)\(\)\s*\{", line)
|
|
if m:
|
|
if cur_name is not None:
|
|
funcs.append((cur_name, cur_lines))
|
|
cur_name, cur_lines = m.group(1), []
|
|
continue
|
|
if line.strip() == "}" and cur_name is not None:
|
|
funcs.append((cur_name, cur_lines))
|
|
cur_name, cur_lines = None, []
|
|
continue
|
|
if cur_name is not None:
|
|
lm = re.match(r"\s*@(\d+):", line)
|
|
if lm:
|
|
cur_pc[0] = int(lm.group(1))
|
|
cur_lines.append((cur_pc[0], line))
|
|
if cur_name is not None:
|
|
funcs.append((cur_name, cur_lines))
|
|
return funcs
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) < 3:
|
|
print(__doc__)
|
|
return 2
|
|
path, cmd = sys.argv[1], sys.argv[2]
|
|
arg = sys.argv[3] if len(sys.argv) > 3 else ""
|
|
funcs = parse(path)
|
|
print(f"[{path}] 解析出 {len(funcs)} 个函数")
|
|
|
|
if cmd == "funcs":
|
|
for name, lines in funcs:
|
|
pcs = [p for p, _ in lines if p >= 0]
|
|
print(f" {name:16s} 行数={len(lines):5d} pc范围=@{min(pcs) if pcs else '-'}..@{max(pcs) if pcs else '-'}")
|
|
return 0
|
|
|
|
if cmd == "who":
|
|
# 谁写了 rN (赋值/属性写)
|
|
pat = re.compile(rf"(^|[^\w]){re.escape(arg)}\s*(=[^=]|\[[^\]]*\]\s*=)")
|
|
hits = 0
|
|
for name, lines in funcs:
|
|
for pc, line in lines:
|
|
if pat.search(line):
|
|
print(f" {name} @{pc if pc>=0 else '?'}: {line.strip()}")
|
|
hits += 1
|
|
if not hits:
|
|
print(f" 未发现写 {arg} 的语句")
|
|
return 0
|
|
|
|
if cmd == "at":
|
|
want = int(arg.lstrip("@"))
|
|
for name, lines in funcs:
|
|
pcs = [p for p, _ in lines if p >= 0]
|
|
if not pcs or pcs[0] > want:
|
|
continue
|
|
nxt = [p for p in pcs if p >= want]
|
|
if nxt and (want <= max(pcs)):
|
|
print(f" @{want} 属于函数 {name} (该函数 pc 范围 @{min(pcs)}..@{max(pcs)})")
|
|
# 打印附近 8 行
|
|
for pc, line in lines:
|
|
if 0 <= pc and want <= pc <= want + 20:
|
|
print(" " + line.strip())
|
|
break
|
|
return 0
|
|
|
|
if cmd == "grep":
|
|
pat = re.compile(arg)
|
|
for name, lines in funcs:
|
|
for pc, line in lines:
|
|
if pat.search(line):
|
|
print(f" {name} @{pc if pc>=0 else '?'}: {line.strip()}")
|
|
return 0
|
|
|
|
print(f"未知命令: {cmd}")
|
|
return 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|