"""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 import urllib.parse from pathlib import Path from .algorithm import ( JSDate, UNDEF, JSFunction, JSObject, HostFunction, _ic, i32, u32, ushr, shl, shr, js_typeof, js_truthy, js_str, js_add, js_num, js_eq, js_streq, js_index, js_set, js_del, js_keys, js_call, js_apply, js_new, h_math_floor, h_math_round, h_math_ceil, h_math_min, h_math_max, h_math_abs, h_math_pow, h_math_sqrt, h_parseint, h_parsefloat, h_isnan, h_encodeuri, h_encodeuricomponent, h_decodeuri, h_decodeuricomponent, h_string_fromcharcode, h_new_date, ) __all__ = ["PagedooVM", "run_frame", "REPLAY"] # ---------------------------------------------------------------- 辅助 def _cmp_lt(a, b): try: return a < b except Exception: return False def _cmp_le(a, b): try: return a <= b except Exception: return False def _cmp_gt(a, b): try: return a > b except Exception: return False def _cmp_ge(a, b): try: return a >= b except Exception: 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 a == a else 0 b = int(b) if b == 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): if 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] if op in (4, 11, 44, 50) else (o[u + 2] if op in (0, 18, 23, 26, 43, 48, 84, 107) else 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: 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