fix(vm): 修正解释器 4 处保真度问题 + 增加事件追踪
做了什么:
- 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 轨迹对拍定位首个分歧
This commit is contained in:
+96
-29
@@ -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"<host {self._name}>"
|
||||
|
||||
|
||||
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")], {})
|
||||
|
||||
Reference in New Issue
Block a user