Files
2026-08-31 10:55:44 +08:00

1467 lines
52 KiB
Python

"""pagedoo shop CHAOS VM(108 opcode)纯 Python 移植 — mall PlaceOrder 加密地基。
对应浏览器 `p_5c660516.*` chunk 内的 `__TENCENT_CHAOS_VM`(pagedoo 变体,108 opcode 0..107)。
与 goods 侧(pyvm/algorithm.py,116 opcode)同属 CHAOS VM 家族:语义相同,opcode 编号不同(F-2055)。
JS 语义辅助复用 algorithm.py(_ic/i32/js_add/js_index/JSObject/JSFunction 等)。
当前验证锚点:
- 帧 e215058(encodeURI + %XX 解码 → 字节数组):Node 重放返回输入 JSON 串的 UTF-8 字节(F-2059)
- 帧 e377650 变换核心:Node 指令级复现(F-2060,254937/254938 一致)
输出编排帧链(e454218/e423160 getter 链)仍依赖 VM C 栈续延语义,见 cases/yyb-chaos-vm-xmidas-webnewencrypt.md 未决项。
"""
from __future__ import annotations
import json
import math
from pathlib import Path
from .algorithm import (
UNDEF,
HostFunction,
JSDate,
JSFunction,
JSObject,
Window,
_ic,
h_decodeuri,
h_decodeuricomponent,
h_encodeuri,
h_encodeuricomponent,
h_isnan,
h_math_abs,
h_math_ceil,
h_math_floor,
h_math_max,
h_math_min,
h_math_pow,
h_math_round,
h_math_sqrt,
h_new_date,
h_parsefloat,
h_parseint,
h_string_fromcharcode,
i32,
js_add,
js_apply,
js_call,
js_del,
js_eq,
js_index,
js_keys,
js_new,
js_num,
js_set,
js_str,
js_truthy,
js_typeof,
shl,
shr,
ushr,
)
REPLAY = Path(__file__).resolve().parent.parent / "replay"
__all__ = ["REPLAY", "PagedooVM", "run_frame"]
# ---------------------------------------------------------------- 辅助
def _cmp_lt(a, b):
try:
return a < b
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
def _cmp_le(a, b):
try:
return a <= b
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
def _cmp_gt(a, b):
try:
return a > b
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
def _cmp_ge(a, b):
try:
return a >= b
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
return False
# ---------------------------------------------------------------- 宿主函数
def h_arr_push(this, *args):
if this is UNDEF or this is None:
this = []
if len(args) == 1:
this.append(args[0])
else:
this.extend(args)
return len(this)
def h_arr_shift(this):
if not this:
return UNDEF
return this.pop(0)
def h_arr_join(this, sep=None):
if sep is None:
sep = ","
return sep.join("" if x is UNDEF or x is None else js_str(x) for x in this)
def h_arr_slice(this, a=None, b=None):
n = len(this)
if a is None or a is UNDEF:
a = 0
if b is None or b is UNDEF:
b = n
a = int(a) if not isinstance(a, float) or not math.isnan(a) else 0
b = int(b) if not isinstance(b, float) or not math.isnan(b) else n
if a < 0:
a = max(0, n + a)
if b < 0:
b = max(0, n + b)
return list(this[a:b])
def h_arr_indexof(this, x, frm=0):
try:
return this.index(x, frm)
except ValueError:
return -1
def h_arr_concat(this, *others):
out = list(this)
for o in others:
if isinstance(o, list):
out.extend(o)
else:
out.append(o)
return out
def h_arr_pop(this):
if not this:
return UNDEF
return this.pop()
def h_arr_unshift(this, *vals):
for v in reversed(vals):
this.insert(0, v)
return len(this)
def h_arr_reverse(this):
this.reverse()
return this
def h_arr_splice(this, start, delete_count=0, *items):
n = len(this)
if start < 0:
start = max(0, n + start)
if delete_count is UNDEF or delete_count is None:
delete_count = n - start
removed = this[start : start + delete_count]
del this[start : start + delete_count]
for i, it in enumerate(items):
this.insert(start + i, it)
return removed
def h_arr_map(this, fn):
return [js_call(fn, UNDEF, [x]) for x in this]
def h_arr_foreach(this, fn):
for x in this:
js_call(fn, UNDEF, [x])
return UNDEF
def h_arr_filter(this, fn):
return [x for x in this if js_truthy(js_call(fn, UNDEF, [x]))]
def h_char_code_at(this, s, i=0):
if isinstance(this, str) and isinstance(s, int) and 0 <= s < len(this):
return ord(this[s])
return float("nan")
def h_char_at(this, i=0):
if isinstance(this, str) and isinstance(i, int) and 0 <= i < len(this):
return this[i]
return ""
def h_str_indexof(this, search, frm=0):
if not isinstance(this, str):
return -1
try:
return this.index(search, frm)
except ValueError:
return -1
def h_str_slice(this, a=None, b=None):
return this[a:b] if isinstance(this, str) else ""
def h_str_split(this, sep=None):
return this.split(sep) if isinstance(this, str) else [this]
def h_str_tolower(this):
return this.lower() if isinstance(this, str) else this
def h_str_toupper(this):
return this.upper() if isinstance(this, str) else this
def h_str_substr(this, start=0, length=None):
if not isinstance(this, str):
return ""
n = len(this)
if start < 0:
start = max(0, n + start)
if length is None or length is UNDEF:
return this[start:]
return this[start : start + int(length)]
def h_str_substring(this, start=0, end=None):
if not isinstance(this, str):
return ""
if end is None or end is UNDEF:
end = len(this)
start = max(0, min(int(start), len(this)))
end = max(0, min(int(end), len(this)))
if start > end:
start, end = end, start
return this[start:end]
def h_str_tostring(this):
return js_str(this)
def h_object_ctor(this, *args):
if len(args) == 1 and args[0] is not UNDEF and args[0] is not None:
return args[0]
return JSObject()
def _js_to_py(v):
"""JSON.stringify 辅助:把 JS 值转 JSON 可序列化。"""
if v is UNDEF:
return None
if isinstance(v, list):
return [_js_to_py(x) for x in v]
if isinstance(v, JSObject):
keys = v._d if hasattr(v, "_d") else {}
return {
k: _js_to_py(val)
for k, val in (keys.items() if isinstance(keys, dict) else [])
}
if isinstance(v, Window):
return {}
return v
def _py_to_js(v):
if isinstance(v, dict):
o = JSObject()
for k, val in v.items():
o.set(k, _py_to_js(val))
return o
if isinstance(v, list):
return [_py_to_js(x) for x in v]
return v
def _pg_index(obj, key):
"""pagedoo 专用 js_index:补充字符串原型方法(goods 侧不需要)。"""
if isinstance(obj, JSDate):
if key == "getTime":
return HostFunction(
lambda this: this.t if isinstance(this, JSDate) else obj.t, "getTime"
)
if key == "toString":
return HostFunction(lambda this: str(this), "toString")
if key == "valueOf":
return HostFunction(
lambda this: this.t if isinstance(this, JSDate) else obj.t, "valueOf"
)
if isinstance(obj, str) and isinstance(key, str):
if key == "length":
return len(obj)
if key == "charCodeAt":
return HostFunction(h_char_code_at, "charCodeAt")
if key == "charAt":
return HostFunction(h_char_at, "charAt")
if key == "indexOf":
return HostFunction(h_str_indexof, "indexOf")
if key == "slice":
return HostFunction(h_str_slice, "slice")
if key == "split":
return HostFunction(h_str_split, "split")
if key == "toLowerCase":
return HostFunction(h_str_tolower, "toLowerCase")
if key == "toUpperCase":
return HostFunction(h_str_toupper, "toUpperCase")
if key == "toString":
return HostFunction(h_str_tostring, "toString")
if key == "substr":
return HostFunction(h_str_substr, "substr")
if key == "substring":
return HostFunction(h_str_substring, "substring")
return js_index(obj, key)
# ---------------------------------------------------------------- VM
class PagedooVM:
"""pagedoo CHAOS VM 解释器(108 opcode,0..107)。
bytecode: 字节码数组(如 vm/bytecode-478657.json)
constants: 常量数组(浏览器调用第 4 参,即 globals)
window: 宿主环境对象(JSObject/Window)
"""
def __init__(self, bytecode, constants, window, random_seed=None):
self.o = bytecode
self.constants = constants
self.window = window
self.inst_id = 0
self.use_init_c = False
self.init_c = None
self._root_this = UNDEF
self._host_log = []
import random as _r
self._random = _r
if random_seed is not None:
self._random.seed(random_seed)
self._hosts = self._build_hosts()
def _build_hosts(self):
m = {}
m["Math"] = JSObject()
m["Math"].set(
"random", HostFunction(lambda this: self._random.random(), "random")
)
m["Math"].set("floor", HostFunction(h_math_floor, "floor"))
m["Math"].set("round", HostFunction(h_math_round, "round"))
m["Math"].set("ceil", HostFunction(h_math_ceil, "ceil"))
m["Math"].set("min", HostFunction(h_math_min, "min"))
m["Math"].set("max", HostFunction(h_math_max, "max"))
m["Math"].set("abs", HostFunction(h_math_abs, "abs"))
m["Math"].set("pow", HostFunction(h_math_pow, "pow"))
m["Math"].set("sqrt", HostFunction(h_math_sqrt, "sqrt"))
m["parseInt"] = HostFunction(h_parseint, "parseInt")
m["parseFloat"] = HostFunction(h_parsefloat, "parseFloat")
m["isNaN"] = HostFunction(h_isnan, "isNaN")
m["encodeURI"] = HostFunction(h_encodeuri, "encodeURI")
m["encodeURIComponent"] = HostFunction(
h_encodeuricomponent, "encodeURIComponent"
)
m["decodeURI"] = HostFunction(h_decodeuri, "decodeURI")
m["decodeURIComponent"] = HostFunction(
h_decodeuricomponent, "decodeURIComponent"
)
m["String"] = JSObject()
m["String"].set(
"fromCharCode", HostFunction(h_string_fromcharcode, "fromCharCode")
)
m["Date"] = HostFunction(h_new_date, "Date")
m["Array"] = JSObject()
m["Array"].set(
"isArray", HostFunction(lambda this, x: isinstance(x, list), "isArray")
)
m["Object"] = HostFunction(h_object_ctor, "Object")
m["Number"] = HostFunction(
lambda this, x=None: js_num(x) if x is not None and x is not UNDEF else 0.0,
"Number",
)
m["Boolean"] = HostFunction(
lambda this, x=None: (
bool(js_truthy(x)) if x is not None and x is not UNDEF else False
),
"Boolean",
)
jso = JSObject()
jso.set(
"stringify",
HostFunction(
lambda this, x, *a: json.dumps(_js_to_py(x), ensure_ascii=False),
"stringify",
),
)
jso.set(
"parse", HostFunction(lambda this, s: _py_to_js(json.loads(s)), "parse")
)
m["JSON"] = jso
m["RegExp"] = HostFunction(lambda this, *a: JSObject(), "RegExp")
# Array.prototype 方法挂到 Array 对象上(VM 通过 i[A].push 等调用)
proto = JSObject()
for name, fn in [
("push", h_arr_push),
("shift", h_arr_shift),
("join", h_arr_join),
("slice", h_arr_slice),
("indexOf", h_arr_indexof),
("concat", h_arr_concat),
("pop", h_arr_pop),
("unshift", h_arr_unshift),
("reverse", h_arr_reverse),
("splice", h_arr_splice),
("map", h_arr_map),
("forEach", h_arr_foreach),
("filter", h_arr_filter),
]:
proto.set(name, HostFunction(fn, name))
m["Array.prototype"] = proto
return m
def host(self, name):
h = self.window.get(name)
if h is not UNDEF:
return h
return self._hosts.get(name, UNDEF)
def make(self, entry, args, s, n, t):
return JSFunction(self, entry, args, s, n, t)
# -- 原型方法查找:i[A].push(...) 形式,i[A] 是数组 --
def _arr_method(self, name):
p = self._hosts.get("Array.prototype")
if p is not UNDEF and isinstance(p, JSObject):
v = p.get(name)
if v is not UNDEF:
return v
return UNDEF
def run(self, fn: JSFunction, call_args, trace=None, csnap=None):
self.inst_id += 1
if trace is not None:
self._trace = trace
self._csnap = csnap
if self.use_init_c and self.init_c is not None:
C = self.init_c
self.use_init_c = False
else:
root_this = getattr(self, "_root_this", UNDEF)
C = [fn.s, fn.n, fn.args, root_this, call_args, fn, self.o, 0]
C = list(C)
p = UNDEF
u = fn.entry
d = [] # 异常续延栈(JS C)
t = UNDEF # 最近异常值(op3/op52)
o = self.o
l = fn.t # 异常处理器(JS l)
_get = _pg_index
_set = js_set
_arr = self._arr_method
while True:
try:
while True:
u += 1
op = o[u]
if (
op in (0, 4, 11, 18, 23, 26, 43, 44, 48, 50, 84, 107)
and len(self._host_log) < 2000
):
# 记录调用目标(简化)
_tgt = o[u + 2]
try:
_tv = C[_tgt] if 0 <= _tgt < len(C) else UNDEF
_tr = (
type(_tv).__name__
if not isinstance(
_tv, (int, float, str, bool, type(None))
)
else repr(_tv)[:30]
)
self._host_log.append((op, u, _tr))
except Exception: # noqa: BLE001 外部接口与任务边界需要保留宽泛异常兜底
self._host_log.append((op, u, "?"))
if (
getattr(self, "_trace", None) is not None
and len(self._trace) < 500000
):
self._trace.append((op, u))
if self._csnap is not None and len(self._csnap) < 500:
snap = []
for _si in range(min(42, len(C))):
_v = C[_si]
if isinstance(_v, str):
snap.append("s:" + _v[:30])
elif isinstance(_v, list):
snap.append(f"a[{len(_v)}]")
elif _v is UNDEF:
snap.append("u")
elif _v is None:
snap.append("n")
elif isinstance(_v, (int, float, bool)):
snap.append(_v)
else:
snap.append("o")
self._csnap.append((op, u, snap))
# ---------------- opcode dispatch(108,逐条对照 interpreter-clean.js)----------------
# 约定:S() 读下一个槽索引操作数并取 C[slot];imm 直接读。
if op == 0:
# for(h=[],f=c[++u];f>0;f--)h.push(i[c[++u]]);i[A]=i[B].apply(i[C],h)
f = o[u + 1]
u += 1
h = []
for _ in range(f):
h.append(_get(C, o[u + 1]))
u += 1
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_apply(_get(C, b), _get(C, c), h))
elif op == 1:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_num(_get(C, b)) - js_num(_get(C, c)))
elif op == 2:
a = o[u + 1]
u += 1
_set(C, a, False)
elif op == 3:
a = o[u + 1]
u += 1
_set(C, a, t)
elif op == 4:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
u += 4
_set(C, a, js_call(_get(C, b), p, [_get(C, c), _get(C, dd)]))
elif op == 5:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, _get(C, b))
c = o[u + 1]
dd = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, c, _get(_get(C, dd), imm))
e = o[u + 1]
u += 1
_set(C, e, "")
elif op == 6:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _cmp_lt(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 7:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_del(_get(C, b), _get(C, c)))
elif op == 8:
a = o[u + 1]
b = o[u + 2]
imm1 = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), imm1))
c = o[u + 1]
dd = o[u + 2]
imm2 = o[u + 3]
u += 3
_set(C, c, _get(_get(C, dd), imm2))
elif op == 9:
if d:
d.pop()
elif op == 10:
f = o[u + 1]
u += 1
h = []
for _ in range(f):
h.append(_get(C, o[u + 1]))
u += 1
dest = o[u + 1]
off = o[u + 2]
u += 2
_set(C, dest, self.make(u + off, h, C[0], C[1], l))
elif op == 11:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_call(_get(C, b), p, [_get(C, c)]))
elif op == 12:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, shl(js_num(_get(C, b)), imm))
elif op == 13:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
e = o[u + 5]
u += 5
_set(C, a, _get(C, b))
v = _cmp_lt(js_num(_get(C, dd)), js_num(_get(C, e)))
_set(C, c, v)
u0 = u
if js_truthy(v):
u = u0 + o[u0 + 1]
else:
u = u0 + o[u0 + 2]
elif op == 14:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, i32(_ic(_get(C, b))) | i32(_ic(_get(C, c))))
elif op == 15:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, js_num(_get(C, b)) - imm)
elif op == 16:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_new(_get(C, b), []))
elif op == 17:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, "")
_set(C, b, js_str(_get(C, b)) + chr(imm & 0xFFFF))
elif op == 18:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
e = o[u + 5]
f = o[u + 6]
u += 6
_set(
C,
a,
js_call(
_get(C, b),
_get(C, c),
[_get(C, dd), _get(C, e), _get(C, f)],
),
)
elif op == 19:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, _get(C, b))
c = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, c, imm)
dd = o[u + 1]
e = o[u + 2]
u += 2
_set(C, dd, _get(C, e))
elif op == 20:
a = o[u + 1]
u += 1
_set(C, a, "")
elif op == 21:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), _get(C, c)))
dd = o[u + 1]
e = o[u + 2]
imm = o[u + 3]
u += 3
v = _cmp_gt(js_num(_get(C, e)), imm)
_set(C, dd, v)
u0 = u
if js_truthy(v):
u = u0 + o[u0 + 1]
else:
u = u0 + o[u0 + 2]
elif op == 22:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_num(_get(C, b)) * js_num(_get(C, c)))
elif op == 23:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
u += 4
_set(C, a, js_call(_get(C, b), _get(C, c), [_get(C, dd)]))
elif op == 24:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_num(_get(C, b)))
c = o[u + 1]
dd = o[u + 2]
u += 2
_set(C, c, js_num(_get(C, dd)) + 1)
e = o[u + 1]
f = o[u + 2]
u += 2
_set(C, e, _get(C, f))
elif op == 25:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
e = o[u + 5]
u += 5
_set(
C,
a,
js_new(_get(C, b), [_get(C, c), _get(C, dd), _get(C, e)]),
)
elif op == 26:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), _get(C, c)))
dd = o[u + 1]
e = o[u + 2]
f = o[u + 3]
g = o[u + 4]
u += 4
_set(C, dd, js_call(_get(C, e), _get(C, f), [_get(C, g)]))
hh = o[u + 1]
ii = o[u + 2]
j = o[u + 3]
kk = o[u + 4]
ll = o[u + 5]
u += 5
_set(
C,
hh,
js_call(
_get(C, ii), _get(C, j), [_get(C, kk), _get(C, ll)]
),
)
elif op == 27:
a = o[u + 1]
n = o[u + 2]
u += 2
_set(C, a, [UNDEF] * int(n))
elif op == 28:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, i32(_ic(_get(C, b))) ^ i32(_ic(_get(C, c))))
elif op == 29:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _cmp_gt(js_num(_get(C, b)), imm))
elif op == 30:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, i32(_ic(_get(C, b))) & i32(_ic(_get(C, c))))
elif op == 31:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, _get(C, b))
c = o[u + 1]
dd = o[u + 2]
u += 2
_set(C, c, js_num(_get(C, dd)))
e = o[u + 1]
f = o[u + 2]
u += 2
_set(C, e, js_num(_get(C, f)) + 1)
elif op == 32:
# u += i[A] ? c[++u] : c[(++u,++u)] —— LHS u 在 body 开始读取
u0 = u
a = o[u0 + 1]
if js_truthy(_get(C, a)):
u = u0 + o[u0 + 2]
else:
u = u0 + o[u0 + 3]
elif op == 33:
a = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, a, js_str(_get(C, a)) + chr(imm & 0xFFFF))
b = o[u + 1]
c = o[u + 2]
dd = o[u + 3]
u += 3
_set(C, b, _get(_get(C, c), _get(C, dd)))
elif op == 34:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_typeof(_get(C, b)))
elif op == 35:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _cmp_le(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 36:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, -js_num(_get(C, b)))
elif op == 37:
a = o[u + 1]
u += 1
_set(C, a, True)
elif op == 38:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(
C,
a,
isinstance(_get(C, b), type(_get(C, c)))
if isinstance(_get(C, c), type)
else False,
)
elif op == 39:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, js_add(_get(C, b), imm))
elif op == 40:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
js_set(_get(C, a), _get(C, b), _get(C, c))
elif op == 41:
a = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, a, imm)
b = o[u + 1]
u += 1
_set(C, b, _get(C, b))
c = o[u + 1]
dd = o[u + 2]
e = o[u + 3]
u += 3
_set(C, c, _cmp_lt(js_num(_get(C, dd)), js_num(_get(C, e))))
elif op == 42:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_num(_get(C, b)) / js_num(_get(C, c)))
elif op == 43:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
e = o[u + 5]
u += 5
_set(
C,
a,
js_call(_get(C, b), _get(C, c), [_get(C, dd), _get(C, e)]),
)
elif op == 44:
a = o[u + 1]
b = o[u + 2]
u += 2
_bv = _get(C, b)
if getattr(self, "_dbg_u", None) == u:
print(
f" [dbg] op44@{u}: A={a} B={b} i[B]={type(_bv).__name__}: {str(_bv)[:80]}"
)
_set(C, a, js_call(_bv, p, []))
elif op == 45:
a = o[u + 1]
u += 1
_set(C, a, p)
b = o[u + 1]
n = o[u + 2]
u += 2
_set(C, b, [UNDEF] * int(n))
c = o[u + 1]
u += 1
_set(C, c, "")
elif op == 46:
a = o[u + 1]
u += 1
obj = JSObject()
_set(C, a, obj)
b = o[u + 1]
imm = o[u + 2]
c = o[u + 3]
u += 3
js_set(_get(C, b), imm, _get(C, c))
dd = o[u + 1]
imm2 = o[u + 2]
e = o[u + 3]
u += 3
js_set(_get(C, dd), imm2, _get(C, e))
elif op == 47:
a = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, a, js_str(_get(C, a)) + chr(imm & 0xFFFF))
elif op == 48:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
u += 4
_set(C, a, js_call(_get(C, b), _get(C, c), [_get(C, dd)]))
e = o[u + 1]
f = o[u + 2]
g = o[u + 3]
hh = o[u + 4]
ii = o[u + 5]
j = o[u + 6]
u += 6
_set(
C,
e,
js_call(_get(C, f), _get(C, g), [_get(C, hh), _get(C, ii)]),
)
return _get(C, j)
elif op == 49:
a = o[u + 1]
u += 1
_set(C, a, JSObject())
elif op == 50:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
u += 4
_set(C, a, js_call(_get(C, b), p, [_get(C, c), _get(C, dd)]))
elif op == 51:
a = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, a, js_str(_get(C, a)) + chr(imm & 0xFFFF))
f = o[u + 1]
u += 1
h = []
for _ in range(f):
h.append(_get(C, o[u + 1]))
u += 1
dest = o[u + 1]
off = o[u + 2]
u += 2
frame = self.make(u + off, h, C[0], C[1], l)
_set(C, dest, frame)
b = o[u + 1]
c = o[u + 2]
e = o[u + 3]
u += 3
js_set(_get(C, b), _get(C, c), _get(C, e))
elif op == 52:
t = _get(C, o[u + 1])
u += 1
raise _VMThrow(t)
elif op == 53:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_num(_get(C, b)))
elif op == 54:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _cmp_lt(js_num(_get(C, b)), imm))
elif op == 55:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), _get(C, c)))
elif op == 56:
# C.push(u+c[++u]) —— LHS u 在 c[++u] 前读取,且 u 推进 1
u0 = u
imm = o[u0 + 1]
d.append(u0 + imm)
u = u0 + 1
elif op == 57:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _cmp_ge(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 58:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_eq(_get(C, b), _get(C, c)))
elif op == 59:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_keys(_get(C, b)))
elif op == 60:
u += o[u + 1]
elif op == 61:
a = o[u + 1]
imm = o[u + 2]
b = o[u + 3]
u += 3
js_set(_get(C, a), imm, _get(C, b))
elif op == 62:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, _get(C, b))
c = o[u + 1]
dd = o[u + 2]
e = o[u + 3]
u += 3
js_set(_get(C, c), _get(C, dd), _get(C, e))
elif op == 63:
return _get(C, o[u + 1])
elif op == 64:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), imm))
c = o[u + 1]
u += 1
_set(C, c, "")
dd = o[u + 1]
imm2 = o[u + 2]
u += 2
_set(C, dd, js_str(_get(C, dd)) + chr(imm2 & 0xFFFF))
elif op == 65:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, shr(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 66:
a = o[u + 1]
imm1 = o[u + 2]
u += 2
_set(C, a, js_str(_get(C, a)) + chr(imm1 & 0xFFFF))
b = o[u + 1]
imm2 = o[u + 2]
u += 2
_set(C, b, js_str(_get(C, b)) + chr(imm2 & 0xFFFF))
elif op == 67:
a = o[u + 1]
u += 1
_set(C, a, p)
elif op == 68:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), _get(C, c)))
dd = o[u + 1]
e = o[u + 2]
u += 2
_set(C, dd, _get(C, e))
f = o[u + 1]
u += 1
_set(C, f, "")
elif op == 69:
a = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, a, js_num(_get(C, a)) - imm)
b = o[u + 1]
c = o[u + 2]
dd = o[u + 3]
e = o[u + 4]
u += 4
_set(C, b, js_new(_get(C, c), [_get(C, dd), _get(C, e)]))
f = o[u + 1]
g = o[u + 2]
u += 2
_set(C, f, _get(C, g))
elif op == 70:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, i32(_ic(_get(C, b))) | imm)
elif op == 71:
f = o[u + 1]
u += 1
h = []
for _ in range(f):
h.append(_get(C, o[u + 1]))
u += 1
dest = o[u + 1]
off = o[u + 2]
u += 2
_set(C, dest, self.make(u + off, h, C[0], C[1], l))
elif op == 72:
a = o[u + 1]
u += 1
_set(C, a, None)
elif op == 73:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, i32(_ic(_get(C, b))) & imm)
elif op == 74:
a = o[u + 1]
imm1 = o[u + 2]
b = o[u + 3]
u += 3
js_set(_get(C, a), imm1, _get(C, b))
c = o[u + 1]
u += 1
obj = JSObject()
_set(C, c, obj)
dd = o[u + 1]
imm2 = o[u + 2]
e = o[u + 3]
u += 3
js_set(_get(C, dd), imm2, _get(C, e))
elif op == 75:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, shr(js_num(_get(C, b)), imm))
elif op == 76:
a = o[u + 1]
imm = o[u + 2]
b = o[u + 3]
u += 3
_set(C, a, imm + js_num(_get(C, b)))
elif op == 77:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
dd = o[u + 4]
u += 4
_set(C, a, js_new(_get(C, b), [_get(C, c), _get(C, dd)]))
elif op == 78:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, ushr(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 79:
a = o[u + 1]
u += 1
_set(C, a, js_num(_get(C, a)) + 1)
elif op == 80:
a = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, a, imm)
elif op == 81:
a = o[u + 1]
imm = o[u + 2]
b = o[u + 3]
u += 3
_set(C, a, imm - js_num(_get(C, b)))
elif op == 82:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, _cmp_gt(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 83:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, ushr(js_num(_get(C, b)), imm))
elif op == 84:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_call(_get(C, b), _get(C, c), []))
elif op == 85:
a = o[u + 1]
imm1 = o[u + 2]
b = o[u + 3]
u += 3
js_set(_get(C, a), imm1, _get(C, b))
c = o[u + 1]
imm2 = o[u + 2]
dd = o[u + 3]
u += 3
js_set(_get(C, c), imm2, _get(C, dd))
e = o[u + 1]
u += 1
_set(C, e, "")
elif op == 86:
a = o[u + 1]
b = o[u + 2]
u += 2
h = _get(C, a)
if h is not UNDEF and h is not None and len(h):
_set(C, b, True)
c = o[u + 1]
u += 1
_set(C, c, h.pop(0))
else:
_set(C, b, False)
u += 1
elif op == 87:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), imm))
elif op == 88:
a = o[u + 1]
u += 1
_set(C, a, js_num(_get(C, a)) - 1)
elif op == 89:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, shl(js_num(_get(C, b)), js_num(_get(C, c))))
elif op == 90:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_eq(_get(C, b), _get(C, c)))
elif op == 91:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_new(_get(C, b), [_get(C, c)]))
elif op == 92:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _cmp_le(js_num(_get(C, b)), imm))
elif op == 93:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_num(_get(C, b)) % js_num(_get(C, c)))
elif op == 94:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, i32(_ic(_get(C, b))) ^ imm)
elif op == 95:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_new(_get(C, b), [_get(C, c)]))
dd = o[u + 1]
e = o[u + 2]
u += 2
_set(C, dd, _get(C, e))
f = o[u + 1]
imm = o[u + 2]
u += 2
_set(C, f, imm)
elif op == 96:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
obj2 = _get(C, c)
_set(
C,
a,
_get(C, b) in obj2
if isinstance(obj2, (list, dict, JSObject))
else False,
)
elif op == 97:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, not js_truthy(_get(C, b)))
elif op == 98:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _cmp_ge(js_num(_get(C, b)), imm))
elif op == 99:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_add(_get(C, b), _get(C, c)))
elif op == 100:
a = o[u + 1]
b = o[u + 2]
imm = o[u + 3]
u += 3
_set(C, a, _get(C, b) == imm)
elif op == 101:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
_set(C, a, js_eq(_get(C, b), _get(C, c)))
elif op == 102:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, _get(C, b))
elif op == 103:
a = o[u + 1]
b = o[u + 2]
imm1 = o[u + 3]
u += 3
_set(C, a, _get(_get(C, b), imm1))
c = o[u + 1]
dd = o[u + 2]
u += 2
_set(C, c, _get(C, dd))
e = o[u + 1]
f = o[u + 2]
imm2 = o[u + 3]
u += 3
_set(C, e, _get(_get(C, f), imm2))
elif op == 104:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_num(_get(C, b)))
elif op == 105:
a = o[u + 1]
b = o[u + 2]
c = o[u + 3]
u += 3
v = _get(_get(C, b), _get(C, c))
_set(C, a, v)
dd = o[u + 1]
e = o[u + 2]
f = o[u + 3]
u += 3
js_set(_get(C, dd), _get(C, e), _get(C, f))
u0 = u
u = u0 + o[u0 + 1]
elif op == 106:
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, ~i32(_ic(_get(C, b))))
elif op == 107:
f = o[u + 1]
u += 1
h = []
for _ in range(f):
h.append(_get(C, o[u + 1]))
u += 1
a = o[u + 1]
b = o[u + 2]
u += 2
_set(C, a, js_apply(_get(C, b), p, h))
else:
raise RuntimeError(f"未知 opcode {op} @u={u}")
except _VMThrow as ex:
t = ex.value
if d:
u = d.pop()
continue
if l is not UNDEF and l is not None:
return js_call(l, UNDEF, [t, C, []])
raise
except Exception:
if d:
u = d.pop()
continue
raise
class _VMThrow(Exception):
def __init__(self, value):
super().__init__("vm throw")
self.value = value
# ---------------------------------------------------------------- 便捷入口
def _mk_window(vmlog=None):
from .algorithm import Window
w = Window()
for k in ("window", "self", "globalThis", "top", "parent", "frames"):
w.set(k, w)
w.set("document", JSObject())
w.set("navigator", JSObject())
w.set("location", JSObject())
w.set("localStorage", JSObject())
w.set("sessionStorage", JSObject())
w.set("screen", JSObject())
w.set("history", JSObject())
w.set("XMLHttpRequest", HostFunction(lambda this, *a: None, "XHR"))
w.set("fetch", HostFunction(lambda this, *a: None, "fetch"))
return w
def run_frame(
entry, h, bytecode, constants, window=None, call_args=(), random_seed=None
):
"""便捷入口:用 pagedoo VM 执行指定 entry 的帧(h 为创建参数数组)。"""
if window is None:
window = _mk_window()
vm = PagedooVM(bytecode, constants, window, random_seed=random_seed)
# 挂宿主
for k, v in vm._hosts.items():
if k != "Array.prototype" and window.get(k) is UNDEF:
window.set(k, v)
frame = vm.make(entry, h, window, constants, None)
return vm.run(frame, list(call_args)), vm