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")], {})