feat(vm): 提升器按闭包边界切分函数 + 递归展开全部子程序

做了什么:
- tdc_disasm.disassemble_cfg 增加 follow_closures 开关
  (提升单函数时关闭, 避免"子函数体的可达性溢进父函数")
- tdc_lift.run 递归提升所有被引用的闭包: 229 个独立函数
- 函数名统一为 sub_<target>, 便于按 pc 定位

为什么:
- 原来整个程序挤在一个 function 里(闭包 body 与父函数地址连续), 无法按子程序阅读
- AGENTS.md §3.3 步骤5 明确要求"子程序调用(闭包创建)递归展开"

怎么验证:
- .venv/bin/python tools/tdc_lift.py tcaptcha/core_js/tdc_sample.js
  -> lifted functions=229; XTEA 例程 = sub_9109, 内含 r2=(r2+2654435769)
- .venv/bin/python tools/tdc_query.py .../tdc_sample.lifted.txt who r2
  -> sub_9109 @9109: r2 = 0;  @9285: r2 = (r2 + 2654435769);
This commit is contained in:
yml2213
2026-10-11 22:18:42 +08:00
parent 942421d811
commit 9c889215f2
3 changed files with 39414 additions and 2348 deletions
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -219,7 +219,7 @@ def _decode_one(z: List[int], pc: int) -> Tuple[Optional[int], List[int], Option
return op, operands, note, pos
def disassemble_cfg(z: List[int], entry: int = 0) -> List[dict]:
def disassemble_cfg(z: List[int], entry: int = 0, follow_closures: bool = True) -> List[dict]:
"""从 entry 出发按可达性反汇编; 返回按 pc 排序的指令列表。"""
visited: Dict[int, dict] = {}
work = [entry]
@@ -244,9 +244,10 @@ def disassemble_cfg(z: List[int], entry: int = 0) -> List[dict]:
continue
if op == OP_PUSH_TRY and operands:
work.append(operands[0]) # catch 目标可达
if op == OP_CREATE_CLOSURE:
if op == OP_CREATE_CLOSURE and follow_closures:
# 57 的操作数 [0] 是闭包体入口: 该段代码在闭包被调用时才执行, 但它是**代码**,
# 必须作为遍历根加入, 否则大半个程序都算不到。
# (提升单个函数时应置 follow_closures=False, 避免子函数体溢进父函数)
work.append(z[pc + 1])
work.append(nxt)
return [visited[k] for k in sorted(visited)]
+10 -1
View File
@@ -79,6 +79,15 @@ class Lifter:
self.out.append(f"// ===== lifted from tdc.js bytecode ({len(self.z)} bytes) =====")
self.funcs[entry] = {"name": name}
self._lift_func(entry, name)
# 递归提升所有被引用到的闭包 (子程序展开)
done = {entry}
while True:
todo = [t for t in self.funcs if t not in done]
if not todo:
break
for t in todo:
done.add(t)
self._lift_func(t, self.funcs[t]["name"])
return "\n".join(self.out)
def _func_name(self, target: int) -> str:
@@ -87,7 +96,7 @@ class Lifter:
return self.funcs[target]["name"]
def _lift_func(self, entry: int, name: str):
insts = {i["pc"]: i for i in disassemble_cfg(self.z, entry) if i["pc"] >= entry}
insts = {i["pc"]: i for i in disassemble_cfg(self.z, entry, follow_closures=False) if i["pc"] >= entry}
# ---------- 第一遍: 沿执行流传播"求值栈快照"(DFS, 每 pc 记录首次到达时的栈) ----------
state: Dict[int, List[Expr]] = {entry: []}
seen: Dict[int, bool] = {}