fix(vm): 3 处保真修复 + 精确定位到模块实参为 null
做了什么: - JArr.__getitem__ 缺失键返回 undefined (JS 语义), 原来返回 Python None - E 初值 E[0]=[this], 顶层 this=全局对象 c (原来错写成 None) - MAKE_REG_PROP_REF / CALL_METHOD / closure 调用 增加 DBGCALL 诊断输出 为什么: - 用 PC 轨迹对拍逐条缩小: 前 713 步一致, 分歧在 @206 CALL_METHOD - 根因链: @45631 CALL_FUNC(模块工厂) 的实参是 [null] -> 参数 r3=[null] -> 闭包 @20 捕获 r6=父r3 -> E[6][0]=null -> ref=[null,'call'] -> 调用不到 怎么验证: - node tools/tdc_trace.js ... -> 19266 步(真值); 我们仍 739 步 - 分歧点/根因链见上 (已用 DBGCALL 定位到具体指令与寄存器) 下一步线索: 追 "谁把该实参压成了 null" (候选: PUSH_NULL(29) 被误用, 或某次属性读取应得对象却拿到 null)
This commit is contained in:
+20
-5
@@ -41,13 +41,16 @@ class JArr(list):
|
||||
self._ext: Dict[str, Any] = {}
|
||||
|
||||
def __getitem__(self, k):
|
||||
# JS 数组/对象: 缺失的属性是 undefined (不是 null!), 这点必须严格
|
||||
if isinstance(k, int) or (isinstance(k, str) and k.lstrip("-").isdigit()):
|
||||
i = int(k)
|
||||
try:
|
||||
return list.__getitem__(self, i)
|
||||
except IndexError:
|
||||
return None
|
||||
return self._ext.get(k)
|
||||
return JS_UNDEF
|
||||
if k in self._ext:
|
||||
return self._ext[k]
|
||||
return JS_UNDEF
|
||||
|
||||
def __setitem__(self, k, v):
|
||||
if isinstance(k, int) or (isinstance(k, str) and k.lstrip("-").isdigit()):
|
||||
@@ -216,7 +219,8 @@ 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] = [JArr([None]), JArr([JArr()])] # 与 VM 默认一致: [[this],[{}]]
|
||||
# 与 VM 源码默认一致: E = [[this],[{}]]; 顶层是普通调用 -> this = 全局对象 (c)
|
||||
self.E: List[Any] = [JArr([c]), JArr([JArr()])]
|
||||
self.B: List[Any] = [] # try 栈
|
||||
self.w = None # 异常标志
|
||||
self.H = 0
|
||||
@@ -259,7 +263,10 @@ class VM:
|
||||
h = self.OP.get(op)
|
||||
if h is None:
|
||||
raise RuntimeError(f"unknown opcode {op} at pc {self.H-1}")
|
||||
if h() is True:
|
||||
r = h()
|
||||
if os.environ.get("DBGCALL") and self.E and isinstance(self.E[-1], list) and len(self.E[-1]) == 2 and self.E[-1][0] is None:
|
||||
print(f"[产生 [None,{self.E[-1][1]!r}] @pc={self.H-1}] op={op} steps={self.steps}", file=sys.stderr)
|
||||
if r is True:
|
||||
self.done = True
|
||||
return self.ret
|
||||
|
||||
@@ -391,7 +398,10 @@ class VM:
|
||||
a = E.pop()
|
||||
idx = E.pop()
|
||||
cell = E[idx[0]] if isinstance(idx, list) else E[idx]
|
||||
E.append([cell[0] if isinstance(cell, list) and cell else JS_UNDEF, a])
|
||||
val = cell[0] if isinstance(cell, list) and cell else JS_UNDEF
|
||||
if os.environ.get("DBGCALL") and (val is None or val is JS_UNDEF):
|
||||
print(f"[MAKE_REG_PROP_REF @{self.H-1}] reg={idx[0] if isinstance(idx,list) else idx} val={val!r} cell={str(cell)[:40]}", file=sys.stderr)
|
||||
E.append([val, a])
|
||||
|
||||
@op(34) # CALL_METHOD n
|
||||
def _34():
|
||||
@@ -550,6 +560,11 @@ class VM:
|
||||
sub.events = self.events # 共享关键事件
|
||||
sub.trace = self.trace
|
||||
sub.trace_full = self.trace_full
|
||||
if os.environ.get("DBGCALL"):
|
||||
print(f"[invoke closure target=@{fn[1]}] caps={fn[2]} params={fn[3]} args={[str(a)[:20] for a in args]}", file=sys.stderr)
|
||||
for (q, r) in fn[2]:
|
||||
src = self.E[r] if r < len(self.E) else "<oob>"
|
||||
print(f" cap r{q} <- parent r{r} = {str(src)[:50]}", file=sys.stderr)
|
||||
sub.E = [JArr() for _ in range(32)]
|
||||
sub.E[0] = JArr([this]); sub.E[1] = JArr([JArr(args)]); sub.E[2] = JArr([fn])
|
||||
for (q, r) in fn[2]:
|
||||
|
||||
Reference in New Issue
Block a user