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:
|
class Stub:
|
||||||
"""未知宿主对象的占位: 可调用、可属性访问、可下标, 一律返回自身/None。"""
|
"""未知宿主对象: 可调用、可属性访问、可下标; **会真正记住被赋的属性**
|
||||||
|
(tdc.js 把模块工厂挂到对象上, 若丢弃就永远找不到模块)。"""
|
||||||
|
|
||||||
def __init__(self, name="stub"):
|
def __init__(self, name="stub"):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self._props: Dict[Any, Any] = {}
|
||||||
|
|
||||||
def __call__(self, *a, **k):
|
def __call__(self, *a, **k):
|
||||||
return Stub(self._name + "()")
|
return Stub(self._name + "()")
|
||||||
@@ -75,16 +77,47 @@ class Stub:
|
|||||||
def __getattr__(self, k):
|
def __getattr__(self, k):
|
||||||
if k.startswith("_"):
|
if k.startswith("_"):
|
||||||
raise AttributeError(k)
|
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):
|
def __getitem__(self, k):
|
||||||
return Stub(f"{self._name}[{k}]")
|
return Stub(f"{self._name}[{k}]")
|
||||||
|
|
||||||
def __setitem__(self, k, v):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __repr__(self):
|
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):
|
def _to_host(**kw):
|
||||||
@@ -97,29 +130,26 @@ def make_host() -> Any:
|
|||||||
"""最小宿主 (c = window)。未知项退化为 Stub, 保证不因缺属性而崩。"""
|
"""最小宿主 (c = window)。未知项退化为 Stub, 保证不因缺属性而崩。"""
|
||||||
win = _to_host()
|
win = _to_host()
|
||||||
|
|
||||||
class MathMod:
|
|
||||||
pass
|
|
||||||
|
|
||||||
win._ext.update({
|
win._ext.update({
|
||||||
"Math": Stub("Math"),
|
"Math": Stub("Math"),
|
||||||
"JSON": type("J", (), {"parse": staticmethod(json.loads), "stringify": staticmethod(json.dumps)})(),
|
"JSON": type("J", (), {"parse": staticmethod(json.loads), "stringify": staticmethod(json.dumps)})(),
|
||||||
"Object": JArr,
|
"Object": HostFn("Object", _mk_object),
|
||||||
"Array": JArr,
|
"Array": HostFn("Array", _mk_array),
|
||||||
"String": str,
|
"String": HostFn("String", lambda *a: "" if not a else str(a[0])),
|
||||||
"Number": float,
|
"Number": HostFn("Number", lambda *a: 0 if not a else float(a[0])),
|
||||||
"Boolean": bool,
|
"Boolean": HostFn("Boolean", lambda *a: bool(a[0]) if a else False),
|
||||||
"Date": int(time.time() * 1000),
|
"Date": HostFn("Date", lambda *a: int(time.time() * 1000)),
|
||||||
"RegExp": Stub("RegExp"),
|
"RegExp": Stub("RegExp"),
|
||||||
"Error": Stub("Error"),
|
"Error": Stub("Error"),
|
||||||
"TypeError": Stub("TypeError"),
|
"TypeError": Stub("TypeError"),
|
||||||
"Uint8Array": bytearray,
|
"Uint8Array": HostFn("Uint8Array", lambda *a: JArr([0] * (a[0] if a and isinstance(a[0], int) else 0))),
|
||||||
"Uint8ClampedArray": bytearray,
|
"Uint8ClampedArray": HostFn("Uint8ClampedArray", lambda *a: JArr([0] * (a[0] if a and isinstance(a[0], int) else 0))),
|
||||||
"parseInt": int,
|
"parseInt": HostFn("parseInt", lambda *a: int(float(a[0])) if a else 0),
|
||||||
"parseFloat": float,
|
"parseFloat": HostFn("parseFloat", lambda *a: float(a[0]) if a else 0.0),
|
||||||
"encodeURIComponent": lambda s: s,
|
"encodeURIComponent": HostFn("encodeURIComponent", lambda *a: a[0] if a else ""),
|
||||||
"decodeURIComponent": lambda s: s,
|
"decodeURIComponent": HostFn("decodeURIComponent", lambda *a: a[0] if a else ""),
|
||||||
"atob": lambda s: s,
|
"atob": HostFn("atob", lambda *a: a[0] if a else ""),
|
||||||
"btoa": lambda s: s,
|
"btoa": HostFn("btoa", lambda *a: a[0] if a else ""),
|
||||||
"undefined": JS_UNDEF,
|
"undefined": JS_UNDEF,
|
||||||
"NaN": float("nan"),
|
"NaN": float("nan"),
|
||||||
})
|
})
|
||||||
@@ -186,7 +216,7 @@ class VM:
|
|||||||
def __init__(self, z: List[int], c, max_steps: int = 20_000_000):
|
def __init__(self, z: List[int], c, max_steps: int = 20_000_000):
|
||||||
self.z = z
|
self.z = z
|
||||||
self.c = c
|
self.c = c
|
||||||
self.E: List[Any] = [] # 寄存器区 + 栈
|
self.E: List[Any] = [JArr([None]), JArr([JArr()])] # 与 VM 默认一致: [[this],[{}]]
|
||||||
self.B: List[Any] = [] # try 栈
|
self.B: List[Any] = [] # try 栈
|
||||||
self.w = None # 异常标志
|
self.w = None # 异常标志
|
||||||
self.H = 0
|
self.H = 0
|
||||||
@@ -195,6 +225,8 @@ class VM:
|
|||||||
self.done = False
|
self.done = False
|
||||||
self.ret = None
|
self.ret = None
|
||||||
self.key_hits: List[Any] = []
|
self.key_hits: List[Any] = []
|
||||||
|
self.trace = bool(os.environ.get("TRACE"))
|
||||||
|
self.events: List[Any] = [] # 记录调用/闭包/返回等关键事件
|
||||||
|
|
||||||
# ---- 工具
|
# ---- 工具
|
||||||
def pu(self):
|
def pu(self):
|
||||||
@@ -218,6 +250,8 @@ class VM:
|
|||||||
if self.steps > self.max_steps:
|
if self.steps > self.max_steps:
|
||||||
raise RuntimeError("step limit exceeded")
|
raise RuntimeError("step limit exceeded")
|
||||||
op = z[self.H]; self.H += 1
|
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)
|
h = self.OP.get(op)
|
||||||
if h is None:
|
if h is None:
|
||||||
raise RuntimeError(f"unknown opcode {op} at pc {self.H-1}")
|
raise RuntimeError(f"unknown opcode {op} at pc {self.H-1}")
|
||||||
@@ -515,6 +549,13 @@ class VM:
|
|||||||
return v
|
return v
|
||||||
except Exception:
|
except Exception:
|
||||||
return JS_UNDEF
|
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):
|
if isinstance(fn, Stub):
|
||||||
return Stub("call")
|
return Stub("call")
|
||||||
return JS_UNDEF
|
return JS_UNDEF
|
||||||
@@ -537,12 +578,38 @@ def main() -> int:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("VM halted with:", type(e).__name__, str(e)[:200])
|
print("VM halted with:", type(e).__name__, str(e)[:200])
|
||||||
print("steps:", vm.steps, "done:", vm.done, "ret:", str(vm.ret)[:80])
|
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 顶层只"注册"模块工厂; 这里主动调用寄存器里保存的闭包 (模拟模块系统/宿主调用)
|
# tdc.js 顶层只"注册"模块工厂; 这里把寄存器/数组里保存的闭包全收集出来 = 模块表
|
||||||
closures = [cell[0] for cell in vm.E
|
def collect_closures(obj, out, seen):
|
||||||
if isinstance(cell, list) and cell and isinstance(cell[0], tuple)
|
oid = id(obj)
|
||||||
and cell[0] and cell[0][0] == "closure"]
|
if oid in seen:
|
||||||
print("registered closures in registers:", len(closures))
|
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):
|
for i, cl in enumerate(closures):
|
||||||
try:
|
try:
|
||||||
vm.call(cl, [Stub("arg")], {})
|
vm.call(cl, [Stub("arg")], {})
|
||||||
|
|||||||
Reference in New Issue
Block a user