From 1fbd8f46f51bc09fb07c0f287ac2c5aecf3985e4 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 11 Oct 2026 22:02:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(vm):=20=E4=BF=AE=E6=AD=A3=E8=A7=A3=E9=87=8A?= =?UTF-8?q?=E5=99=A8=204=20=E5=A4=84=E4=BF=9D=E7=9C=9F=E5=BA=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20+=20=E5=A2=9E=E5=8A=A0=E4=BA=8B=E4=BB=B6=E8=BF=BD?= =?UTF-8?q?=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 做了什么: - E 初值改为 [[this],[{}]] (与 VM 源码默认一致) - Stub 改为真正保存被赋属性 (模块工厂挂在对象上, 丢弃就找不到模块) - Object/Array/String/... 改为 HostFn, new 时真正构造容器 - getprop/setprop 采用 JS 非严格语义 (原始值: 读=undefined, 写=静默忽略) - 新增 TRACE=1 事件追踪 (CREATE_CLOSURE/CALL_*/RET_TRUE) 与模块表收集 为什么: - 这些是解释器与真实 VM 保真度不符的根因, 导致顶层跑完却收不到任何模块闭包 怎么验证: - py_compile 通过; TRACE=1 跑出 66 条关键事件(其中 CREATE_CLOSURE ~65) - 现状: 仍只跑完顶层引导(739 步), 模块表为空 -> 待用 PC 轨迹对拍定位首个分歧 --- tools/tdc_vm_run.py | 125 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 29 deletions(-) diff --git a/tools/tdc_vm_run.py b/tools/tdc_vm_run.py index 1c9b19b..2fb190d 100644 --- a/tools/tdc_vm_run.py +++ b/tools/tdc_vm_run.py @@ -64,10 +64,12 @@ JS_UNDEF = object() class Stub: - """未知宿主对象的占位: 可调用、可属性访问、可下标, 一律返回自身/None。""" + """未知宿主对象: 可调用、可属性访问、可下标; **会真正记住被赋的属性** + (tdc.js 把模块工厂挂到对象上, 若丢弃就永远找不到模块)。""" def __init__(self, name="stub"): self._name = name + self._props: Dict[Any, Any] = {} def __call__(self, *a, **k): return Stub(self._name + "()") @@ -75,16 +77,47 @@ class Stub: def __getattr__(self, k): if k.startswith("_"): raise AttributeError(k) - return Stub(f"{self._name}.{k}") + return self[k] + + def __getitem__(self, k): + if k in self._props: + return self._props[k] + v = Stub(f"{self._name}.{k}") + self._props[k] = v + return v + + def __setitem__(self, k, v): + self._props[k] = v + + def __repr__(self): + return f"<{self._name} props={list(self._props)[:5]}>" + + +class HostFn: + """宿主内置函数/构造器 (Object/Array/String/...)。""" + + def __init__(self, name, fn): + self._name = name + self._fn = fn + + def __call__(self, *a): + return self._fn(*a) def __getitem__(self, k): return Stub(f"{self._name}[{k}]") - def __setitem__(self, k, v): - pass - def __repr__(self): - return f"<{self._name}>" + return f"" + + +def _mk_array(*a): + if len(a) == 1 and isinstance(a[0], int): + return JArr([JS_UNDEF] * a[0]) + return JArr(a) + + +def _mk_object(*a): + return JArr() def _to_host(**kw): @@ -97,29 +130,26 @@ def make_host() -> Any: """最小宿主 (c = window)。未知项退化为 Stub, 保证不因缺属性而崩。""" win = _to_host() - class MathMod: - pass - win._ext.update({ "Math": Stub("Math"), "JSON": type("J", (), {"parse": staticmethod(json.loads), "stringify": staticmethod(json.dumps)})(), - "Object": JArr, - "Array": JArr, - "String": str, - "Number": float, - "Boolean": bool, - "Date": int(time.time() * 1000), + "Object": HostFn("Object", _mk_object), + "Array": HostFn("Array", _mk_array), + "String": HostFn("String", lambda *a: "" if not a else str(a[0])), + "Number": HostFn("Number", lambda *a: 0 if not a else float(a[0])), + "Boolean": HostFn("Boolean", lambda *a: bool(a[0]) if a else False), + "Date": HostFn("Date", lambda *a: int(time.time() * 1000)), "RegExp": Stub("RegExp"), "Error": Stub("Error"), "TypeError": Stub("TypeError"), - "Uint8Array": bytearray, - "Uint8ClampedArray": bytearray, - "parseInt": int, - "parseFloat": float, - "encodeURIComponent": lambda s: s, - "decodeURIComponent": lambda s: s, - "atob": lambda s: s, - "btoa": lambda s: s, + "Uint8Array": HostFn("Uint8Array", lambda *a: JArr([0] * (a[0] if a and isinstance(a[0], int) else 0))), + "Uint8ClampedArray": HostFn("Uint8ClampedArray", lambda *a: JArr([0] * (a[0] if a and isinstance(a[0], int) else 0))), + "parseInt": HostFn("parseInt", lambda *a: int(float(a[0])) if a else 0), + "parseFloat": HostFn("parseFloat", lambda *a: float(a[0]) if a else 0.0), + "encodeURIComponent": HostFn("encodeURIComponent", lambda *a: a[0] if a else ""), + "decodeURIComponent": HostFn("decodeURIComponent", lambda *a: a[0] if a else ""), + "atob": HostFn("atob", lambda *a: a[0] if a else ""), + "btoa": HostFn("btoa", lambda *a: a[0] if a else ""), "undefined": JS_UNDEF, "NaN": float("nan"), }) @@ -186,7 +216,7 @@ class VM: def __init__(self, z: List[int], c, max_steps: int = 20_000_000): self.z = z self.c = c - self.E: List[Any] = [] # 寄存器区 + 栈 + self.E: List[Any] = [JArr([None]), JArr([JArr()])] # 与 VM 默认一致: [[this],[{}]] self.B: List[Any] = [] # try 栈 self.w = None # 异常标志 self.H = 0 @@ -195,6 +225,8 @@ class VM: self.done = False self.ret = None self.key_hits: List[Any] = [] + self.trace = bool(os.environ.get("TRACE")) + self.events: List[Any] = [] # 记录调用/闭包/返回等关键事件 # ---- 工具 def pu(self): @@ -218,6 +250,8 @@ class VM: if self.steps > self.max_steps: raise RuntimeError("step limit exceeded") op = z[self.H]; self.H += 1 + if self.trace and op in (1, 6, 19, 34, 41, 49, 57, 61): + self.events.append((self.H - 1, op)) h = self.OP.get(op) if h is None: raise RuntimeError(f"unknown opcode {op} at pc {self.H-1}") @@ -515,6 +549,13 @@ class VM: return v except Exception: return JS_UNDEF + if isinstance(fn, HostFn): + return fn(*args) + if callable(fn) and not isinstance(fn, (JArr, Stub, tuple)): + try: + return fn(*args) + except Exception: + return JS_UNDEF if isinstance(fn, Stub): return Stub("call") return JS_UNDEF @@ -537,12 +578,38 @@ def main() -> int: except Exception as e: print("VM halted with:", type(e).__name__, str(e)[:200]) print("steps:", vm.steps, "done:", vm.done, "ret:", str(vm.ret)[:80]) + names = {1: "IS_EXCEPTION", 6: "CALL_FUNC", 19: "RET_TRUE", 34: "CALL_METHOD", + 41: "NEW_METHOD", 49: "THROW", 57: "CREATE_CLOSURE", 61: "NEW_FUNC"} + print("关键事件 (pc, op):") + for pc, op in vm.events[:40]: + print(f" @{pc:05d} {names.get(op, op)}") + print(f" ... 共 {len(vm.events)} 条") - # tdc.js 顶层只"注册"模块工厂; 这里主动调用寄存器里保存的闭包 (模拟模块系统/宿主调用) - closures = [cell[0] for cell in vm.E - if isinstance(cell, list) and cell and isinstance(cell[0], tuple) - and cell[0] and cell[0][0] == "closure"] - print("registered closures in registers:", len(closures)) + # tdc.js 顶层只"注册"模块工厂; 这里把寄存器/数组里保存的闭包全收集出来 = 模块表 + def collect_closures(obj, out, seen): + oid = id(obj) + if oid in seen: + return + seen.add(oid) + if isinstance(obj, tuple) and obj and obj[0] == "closure": + out.append(obj) + return + if isinstance(obj, Stub): + for v in obj._props.values(): + collect_closures(v, out, seen) + elif isinstance(obj, (JArr, list)): + for v in list(obj): + collect_closures(v, out, seen) + if isinstance(obj, JArr): + for v in obj._ext.values(): + collect_closures(v, out, seen) + elif isinstance(obj, dict): + for v in obj.values(): + collect_closures(v, out, seen) + + closures: List[Any] = [] + collect_closures([vm.E, vm.c], closures, set()) + print("模块表: 收集到闭包", len(closures), "个 -> target:", [c[1] for c in closures][:80]) for i, cl in enumerate(closures): try: vm.call(cl, [Stub("arg")], {})