tjs2dec/
emit_exec_tjs.rs

1use anyhow::{Result, bail};
2
3use std::collections::HashMap;
4
5use crate::model::{ConstPools, Tjs2File, Tjs2Object, Variant};
6use crate::vmcodes::vm;
7
8// This module emits a low-level, semantics-oriented TJS program that
9// replays the VM code using an explicit instruction-pointer state machine.
10//
11// Design goal: produce *executable* TJS that is as close as possible to the
12// original bytecode behavior, not human-readable source.
13
14const NEG_REG_MAX: i32 = 8; // we materialize rN1..rN8 unconditionally
15
16const FAT_NORMAL: i32 = 0;
17const FAT_EXPAND: i32 = 1;
18const FAT_UNNAMED_EXPAND: i32 = 2;
19
20pub fn emit_executable_tjs(file: &Tjs2File) -> Result<String> {
21    let mut out = String::new();
22
23    out.push_str("// Generated by tjs2dec (experimental)\n");
24    out.push_str(
25        "// This is a low-level, semantics-oriented re-materialization of TJS2 bytecode.\n",
26    );
27
28    out.push_str("function __tjs2dec_truthy(v) { return v ? true : false; }\n");
29    out.push_str("function __tjs2dec_to_int(v) { v = Number(v); if (isNaN(v)) return 0; return v < 0 ? Math.ceil(v) : Math.floor(v); }\n");
30    out.push_str("function __tjs2dec_idiv(a,b) { a = Number(a); b = Number(b); if (b == 0) return 0; var q = a / b; return q < 0 ? Math.ceil(q) : Math.floor(q); }\n");
31    out.push_str("function __tjs2dec_get_prop(o, k, ignore) { return ignore ? &(o[k]) : o[k]; }\n");
32    out.push_str("function __tjs2dec_set_prop(o, k, v, ignore) { if (ignore) &(o[k]) = v; else o[k] = v; }\n");
33    out.push_str("function __tjs2dec_del_prop(o, k) { return delete o[k]; }\n");
34    out.push_str("function __tjs2dec_expand_args(dst, v) {\n");
35    out.push_str("  if (v == void) return;\n");
36    out.push_str("  // Best-effort expansion: treat Array-like values as sequences.\n");
37    out.push_str("  if (v instanceof Array) {\n");
38    out.push_str("    for (var i = 0; i < v.length; i++) dst.push(v[i]);\n");
39    out.push_str("    return;\n");
40    out.push_str("  }\n");
41    out.push_str("  // If the value is not an Array, push it as-is.\n");
42    out.push_str("  dst.push(v);\n");
43    out.push_str("}\n\n");
44
45    out.push_str("var __tjs2dec_objs = [];\n\n");
46
47    // Emit object bodies first (so inter-object constants can reference __tjs2dec_objs).
48    for obj in &file.objects {
49        out.push_str(&emit_object(obj, &file.const_pools)?);
50        out.push('\n');
51        out.push_str(&format!(
52            "__tjs2dec_objs[{idx}] = __tjs2dec_obj_{idx};\n\n",
53            idx = obj.index
54        ));
55    }
56
57    out.push_str("// Entry point\n");
58    out.push_str(&format!("__tjs2dec_objs[{}]();\n", file.toplevel));
59
60    Ok(out)
61}
62
63fn emit_object(obj: &Tjs2Object, pools: &ConstPools) -> Result<String> {
64    let mut out = String::new();
65
66    out.push_str(&format!("function __tjs2dec_obj_{}() {{\n", obj.index));
67
68    // Registers.
69    let maxv = if obj.max_variable_count < 0 {
70        0
71    } else {
72        obj.max_variable_count
73    };
74    let mut reg_names: Vec<String> = Vec::new();
75    for i in 0..maxv {
76        reg_names.push(format!("r{}", i));
77    }
78    for i in 1..=NEG_REG_MAX {
79        reg_names.push(format!("rN{}", i));
80    }
81
82    // Emit in chunks to keep lines manageable.
83    let mut i = 0usize;
84    while i < reg_names.len() {
85        let end = (i + 16).min(reg_names.len());
86        out.push_str("  var ");
87        out.push_str(&reg_names[i..end].join(", "));
88        out.push_str(";\n");
89        i = end;
90    }
91
92    // Best-effort mapping for negative registers.
93    out.push_str("  // Negative registers are used for special VM bindings.\n");
94    out.push_str("  // rN1: this, rN2: global, rN3: arguments (best-effort).\n");
95    out.push_str("  rN1 = this;\n");
96    out.push_str("  rN2 = global;\n");
97    out.push_str("  rN3 = arguments;\n");
98    for k in 4..=NEG_REG_MAX {
99        out.push_str(&format!("  rN{} = void;\n", k));
100    }
101    out.push('\n');
102
103    // Per-object data area.
104    out.push_str("  // vdata (per-object)\n");
105    out.push_str("  var __d = [\n");
106    for (di, v) in obj.data.iter().enumerate() {
107        out.push_str("    ");
108        out.push_str(&variant_to_tjs(v, pools, obj.index));
109        out.push_str(&format!(", // *{}\n", di));
110    }
111    out.push_str("  ];\n\n");
112
113    // Helpers for dynamic register addressing (needed for exception handlers).
114    out.push_str("  function __get_reg(i) {\n");
115    out.push_str("    switch (i) {\n");
116    for r in 0..maxv {
117        out.push_str(&format!("      case {}: return r{};\n", r, r));
118    }
119    for r in 1..=NEG_REG_MAX {
120        out.push_str(&format!("      case -{}: return rN{};\n", r, r));
121    }
122    out.push_str("      default: return void;\n");
123    out.push_str("    }\n");
124    out.push_str("  }\n\n");
125
126    out.push_str("  function __set_reg(i, v) {\n");
127    out.push_str("    switch (i) {\n");
128    for r in 0..maxv {
129        out.push_str(&format!("      case {}: r{} = v; break;\n", r, r));
130    }
131    for r in 1..=NEG_REG_MAX {
132        out.push_str(&format!("      case -{}: rN{} = v; break;\n", r, r));
133    }
134    out.push_str("      default: break;\n");
135    out.push_str("    }\n");
136    out.push_str("  }\n\n");
137
138    out.push_str("  var __this = this;\n");
139    out.push_str("  var __rv = void;\n");
140    out.push_str("  var __flag = false;\n");
141    out.push_str("  var __try = []; // stack of [catch_ip, ex_reg]\n");
142    out.push_str("  var ip = 0;\n\n");
143
144    out.push_str("  while (true) {\n");
145    out.push_str("    try {\n");
146    out.push_str("      switch (ip) {\n");
147
148    let code = &obj.code;
149
150    // Build a pc -> disasm-line map once (avoid O(n^2)).
151    let disasm_txt = crate::disasm::disassemble_object(obj, pools)?;
152    let mut disasm_map: HashMap<usize, String> = HashMap::new();
153    for line in disasm_txt.lines() {
154        if let Some((addr, rest)) = line.split_once(' ') {
155            if let Ok(a) = addr.parse::<usize>() {
156                disasm_map.insert(a, rest.to_string());
157            }
158        }
159    }
160
161    let mut pc: usize = 0;
162    while pc < code.len() {
163        let line = disasm_map
164            .get(&pc)
165            .cloned()
166            .unwrap_or_else(|| vm::name(code[pc]).to_string());
167        let size = insn_len(code, pc)?;
168        let case_code = emit_case(obj, pools, pc, &line, size)?;
169        out.push_str(&case_code);
170        pc += size;
171    }
172
173    out.push_str("        default:\n");
174    out.push_str("          throw \"Invalid instruction pointer: \" + ip;\n");
175    out.push_str("      }\n");
176    out.push_str("    } catch (__e) {\n");
177    out.push_str("      if (__try.length > 0) {\n");
178    out.push_str("        var __h = __try.pop();\n");
179    out.push_str("        __set_reg(__h[1], __e);\n");
180    out.push_str("        ip = __h[0];\n");
181    out.push_str("        continue;\n");
182    out.push_str("      }\n");
183    out.push_str("      throw __e;\n");
184    out.push_str("    }\n");
185    out.push_str("  }\n");
186
187    out.push_str("}\n");
188
189    Ok(out)
190}
191
192fn emit_build_args(
193    code: &[i32],
194    pc: usize,
195    st: usize,
196    argc: i32,
197    r: &impl Fn(i32) -> String,
198) -> String {
199    let mut s = String::new();
200
201    if argc == -1 {
202        // Omit args: pass-through current arguments (best-effort).
203        s.push_str("          __args = arguments;\n");
204        return s;
205    }
206
207    if argc == -2 {
208        // Extended argument encoding.
209        let num = code.get(pc + st).copied().unwrap_or(0) as usize;
210        let base = pc + st + 1;
211        for j in 0..num {
212            let ty = code.get(base + j * 2).copied().unwrap_or(0);
213            let v = code.get(base + j * 2 + 1).copied().unwrap_or(0);
214            match ty {
215                0 => s.push_str(&format!("          __args.push({});\n", r(v))),
216                1 => s.push_str(&format!(
217                    "          __tjs2dec_expand_args(__args, {});\n",
218                    r(v)
219                )),
220                2 => s.push_str("          __tjs2dec_expand_args(__args, arguments);\n"),
221                _ => s.push_str(&format!("          __args.push({});\n", r(v))),
222            }
223        }
224        return s;
225    }
226
227    let n = if argc < 0 { 0 } else { argc as usize };
228    for j in 0..n {
229        let v = code.get(pc + st + j).copied().unwrap_or(0);
230        s.push_str(&format!("          __args.push({});\n", r(v)));
231    }
232    s
233}
234
235fn emit_op2_prop(
236    out: &mut String,
237    code: &[i32],
238    pc: usize,
239    kind: i32,
240    r: &impl Fn(i32) -> String,
241    op: &str,
242    is_idiv: bool,
243) -> Result<()> {
244    match kind {
245        0 => {
246            ensure(code, pc, 3)?;
247            let a = code[pc + 1];
248            let b = code[pc + 2];
249            if is_idiv {
250                out.push_str(&format!(
251                    "          {} = __tjs2dec_idiv({}, {});\n",
252                    r(a),
253                    r(a),
254                    r(b)
255                ));
256            } else {
257                out.push_str(&format!(
258                    "          {} = ({} {} {});\n",
259                    r(a),
260                    r(a),
261                    op,
262                    r(b)
263                ));
264            }
265        }
266        1 => {
267            ensure(code, pc, 5)?;
268            let dst = code[pc + 1];
269            let objr = code[pc + 2];
270            let key = code[pc + 3];
271            let rhs = code[pc + 4];
272            out.push_str(&format!("          var __k = __d[{}];\n", key));
273            out.push_str(&format!(
274                "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
275                r(objr)
276            ));
277            if is_idiv {
278                out.push_str(&format!(
279                    "          __t = __tjs2dec_idiv(__t, {});\n",
280                    r(rhs)
281                ));
282            } else {
283                out.push_str(&format!("          __t = (__t {} {});\n", op, r(rhs)));
284            }
285            out.push_str(&format!(
286                "          __tjs2dec_set_prop({}, __k, __t, false);\n",
287                r(objr)
288            ));
289            if dst != 0 {
290                out.push_str(&format!(
291                    "          {} = __t;
292",
293                    r(dst)
294                ));
295            }
296        }
297        2 => {
298            ensure(code, pc, 5)?;
299            let dst = code[pc + 1];
300            let objr = code[pc + 2];
301            let keyr = code[pc + 3];
302            let rhs = code[pc + 4];
303            out.push_str(&format!("          var __k = {};\n", r(keyr)));
304            out.push_str(&format!(
305                "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
306                r(objr)
307            ));
308            if is_idiv {
309                out.push_str(&format!(
310                    "          __t = __tjs2dec_idiv(__t, {});\n",
311                    r(rhs)
312                ));
313            } else {
314                out.push_str(&format!("          __t = (__t {} {});\n", op, r(rhs)));
315            }
316            out.push_str(&format!(
317                "          __tjs2dec_set_prop({}, __k, __t, false);\n",
318                r(objr)
319            ));
320            if dst != 0 {
321                out.push_str(&format!(
322                    "          {} = __t;
323",
324                    r(dst)
325                ));
326            }
327        }
328        3 => {
329            ensure(code, pc, 4)?;
330            let dst = code[pc + 1];
331            let propr = code[pc + 2];
332            let rhs = code[pc + 3];
333
334            // Property handler variant: read via unary '*', then write back via unary '*'.
335            out.push_str(&format!(
336                "          var __p = {};
337",
338                r(propr)
339            ));
340            out.push_str(
341                "          var __t = *__p;
342",
343            );
344            if is_idiv {
345                out.push_str(&format!(
346                    "          __t = __tjs2dec_idiv(__t, {});
347",
348                    r(rhs)
349                ));
350            } else {
351                out.push_str(&format!(
352                    "          __t = (__t {} {});
353",
354                    op,
355                    r(rhs)
356                ));
357            }
358            out.push_str(
359                "          *__p = __t;
360",
361            );
362            if dst != 0 {
363                out.push_str(&format!(
364                    "          {} = __t;
365",
366                    r(dst)
367                ));
368            }
369        }
370        _ => {
371            out.push_str(&format!(
372                "          throw \"Unimplemented opcode: {} (unknown op2_prop kind) at ip={}\n\";
373",
374                code[pc], pc
375            ));
376        }
377    }
378    Ok(())
379}
380
381fn emit_case(
382    obj: &Tjs2Object,
383    pools: &ConstPools,
384    pc: usize,
385    insn_str: &str,
386    size: usize,
387) -> Result<String> {
388    let code = &obj.code;
389    let op = code[pc];
390
391    let mut out = String::new();
392    out.push_str(&format!("        case {}: // {:08} {}\n", pc, pc, insn_str));
393
394    // Helpers to format registers.
395    let r = |idx: i32| -> String { reg_name(idx) };
396
397    // Core opcodes.
398    match op {
399        x if x == vm::VM_NOP => {
400            out.push_str(&format!("          ip += {};\n          continue;\n", size));
401        }
402        x if x == vm::VM_DEBUGGER => {
403            out.push_str(&format!("          ip += {};\n          continue;\n", size));
404        }
405
406        // Data/regs
407        x if x == vm::VM_CONST => {
408            ensure(code, pc, 3)?;
409            let dst = code[pc + 1];
410            let src = code[pc + 2];
411            out.push_str(&format!("          {} = __d[{}];\n", r(dst), src));
412            out.push_str("          ip += 3;\n          continue;\n");
413        }
414        x if x == vm::VM_CP => {
415            ensure(code, pc, 3)?;
416            let dst = code[pc + 1];
417            let src = code[pc + 2];
418            out.push_str(&format!("          {} = {};\n", r(dst), r(src)));
419            out.push_str("          ip += 3;\n          continue;\n");
420        }
421        x if x == vm::VM_CL => {
422            ensure(code, pc, 2)?;
423            let rr = code[pc + 1];
424            out.push_str(&format!("          {} = void;\n", r(rr)));
425            out.push_str("          ip += 2;\n          continue;\n");
426        }
427        x if x == vm::VM_CCL => {
428            ensure(code, pc, 4)?;
429            let base = code[pc + 1];
430            let count = code[pc + 2];
431            let step = code[pc + 3];
432            out.push_str("          // ccl base,count,step\n");
433            out.push_str(&format!(
434                "          for (var __i = 0; __i < {}; __i += {}) {{ __set_reg({} + __i, void); }}\n",
435                count, step, base
436            ));
437            out.push_str("          ip += 4;\n          continue;\n");
438        }
439
440        // Flag ops
441        x if x == vm::VM_TT => {
442            ensure(code, pc, 2)?;
443            let rr = code[pc + 1];
444            out.push_str(&format!(
445                "          __flag = __tjs2dec_truthy({});\n",
446                r(rr)
447            ));
448            out.push_str("          ip += 2;\n          continue;\n");
449        }
450        x if x == vm::VM_TF => {
451            ensure(code, pc, 2)?;
452            let rr = code[pc + 1];
453            out.push_str(&format!(
454                "          __flag = !__tjs2dec_truthy({});\n",
455                r(rr)
456            ));
457            out.push_str("          ip += 2;\n          continue;\n");
458        }
459        x if x == vm::VM_NF => {
460            ensure(code, pc, 1)?;
461            out.push_str("          __flag = !__flag;\n");
462            out.push_str("          ip += 1;\n          continue;\n");
463        }
464        x if x == vm::VM_SETF => {
465            ensure(code, pc, 2)?;
466            let rr = code[pc + 1];
467            out.push_str(&format!("          {} = true;\n", r(rr)));
468            out.push_str("          ip += 2;\n          continue;\n");
469        }
470        x if x == vm::VM_SETNF => {
471            ensure(code, pc, 2)?;
472            let rr = code[pc + 1];
473            out.push_str(&format!("          {} = false;\n", r(rr)));
474            out.push_str("          ip += 2;\n          continue;\n");
475        }
476
477        // Comparisons -> __flag
478        x if x == vm::VM_CEQ => {
479            ensure(code, pc, 3)?;
480            let a = code[pc + 1];
481            let b = code[pc + 2];
482            out.push_str(&format!("          __flag = ({} == {});\n", r(a), r(b)));
483            out.push_str("          ip += 3;\n          continue;\n");
484        }
485        x if x == vm::VM_CDEQ => {
486            ensure(code, pc, 3)?;
487            let a = code[pc + 1];
488            let b = code[pc + 2];
489            out.push_str(&format!("          __flag = ({} === {});\n", r(a), r(b)));
490            out.push_str("          ip += 3;\n          continue;\n");
491        }
492        x if x == vm::VM_CLT => {
493            ensure(code, pc, 3)?;
494            let a = code[pc + 1];
495            let b = code[pc + 2];
496            out.push_str(&format!("          __flag = ({} < {});\n", r(a), r(b)));
497            out.push_str("          ip += 3;\n          continue;\n");
498        }
499        x if x == vm::VM_CGT => {
500            ensure(code, pc, 3)?;
501            let a = code[pc + 1];
502            let b = code[pc + 2];
503            out.push_str(&format!("          __flag = ({} > {});\n", r(a), r(b)));
504            out.push_str("          ip += 3;\n          continue;\n");
505        }
506        x if x == vm::VM_CHKINS => {
507            ensure(code, pc, 3)?;
508            let a = code[pc + 1];
509            let b = code[pc + 2];
510            out.push_str(&format!("          __flag = ({} in {});\n", r(a), r(b)));
511            out.push_str("          ip += 3;\n          continue;\n");
512        }
513
514        // Jumps
515        x if x == vm::VM_JMP => {
516            ensure(code, pc, 2)?;
517            let tgt = code[pc + 1];
518            out.push_str(&format!("          ip = {};\n          continue;\n", tgt));
519        }
520        x if x == vm::VM_JF => {
521            ensure(code, pc, 2)?;
522            let tgt = code[pc + 1];
523            out.push_str(&format!(
524                "          if (!__flag) {{ ip = {}; }} else {{ ip += 2; }}\n          continue;\n",
525                tgt
526            ));
527        }
528        x if x == vm::VM_JNF => {
529            ensure(code, pc, 2)?;
530            let tgt = code[pc + 1];
531            out.push_str(&format!(
532                "          if (__flag) {{ ip = {}; }} else {{ ip += 2; }}\n          continue;\n",
533                tgt
534            ));
535        }
536
537        // Unary in-place
538        x if x == vm::VM_LNOT => {
539            ensure(code, pc, 2)?;
540            let rr = code[pc + 1];
541            out.push_str(&format!(
542                "          {} = !__tjs2dec_truthy({});\n",
543                r(rr),
544                r(rr)
545            ));
546            out.push_str("          ip += 2;\n          continue;\n");
547        }
548        x if x == vm::VM_BNOT => {
549            ensure(code, pc, 2)?;
550            let rr = code[pc + 1];
551            out.push_str(&format!("          {} = ~({});\n", r(rr), r(rr)));
552            out.push_str("          ip += 2;\n          continue;\n");
553        }
554        x if x == vm::VM_CHS => {
555            ensure(code, pc, 2)?;
556            let rr = code[pc + 1];
557            out.push_str(&format!("          {} = -({});\n", r(rr), r(rr)));
558            out.push_str("          ip += 2;\n          continue;\n");
559        }
560        x if x == vm::VM_TYPEOF => {
561            ensure(code, pc, 2)?;
562            let rr = code[pc + 1];
563            out.push_str(&format!("          {} = typeof({});\n", r(rr), r(rr)));
564            out.push_str("          ip += 2;\n          continue;\n");
565        }
566        x if x == vm::VM_EVAL || x == vm::VM_EEXP => {
567            ensure(code, pc, 2)?;
568            let rr = code[pc + 1];
569            out.push_str(&format!("          {} = eval({});\n", r(rr), r(rr)));
570            out.push_str("          ip += 2;\n          continue;\n");
571        }
572        x if x == vm::VM_ASC => {
573            ensure(code, pc, 2)?;
574            let rr = code[pc + 1];
575            out.push_str(&format!("          {} = String({});\n", r(rr), r(rr)));
576            out.push_str("          ip += 2;\n          continue;\n");
577        }
578        x if x == vm::VM_CHR => {
579            ensure(code, pc, 2)?;
580            let rr = code[pc + 1];
581            out.push_str(&format!(
582                "          {} = String.fromCharCode({});\n",
583                r(rr),
584                r(rr)
585            ));
586            out.push_str("          ip += 2;\n          continue;\n");
587        }
588        x if x == vm::VM_NUM => {
589            ensure(code, pc, 2)?;
590            let rr = code[pc + 1];
591            out.push_str(&format!("          {} = Number({});\n", r(rr), r(rr)));
592            out.push_str("          ip += 2;\n          continue;\n");
593        }
594        x if x == vm::VM_INV => {
595            ensure(code, pc, 2)?;
596            let rr = code[pc + 1];
597            out.push_str(&format!(
598                "          {} = ({} instanceof Variant ? {} : {});\n",
599                r(rr),
600                r(rr),
601                r(rr),
602                r(rr)
603            ));
604            out.push_str("          ip += 2;\n          continue;\n");
605        }
606        x if x == vm::VM_CHKINV => {
607            ensure(code, pc, 2)?;
608            let rr = code[pc + 1];
609            out.push_str(&format!(
610                "          if (({} instanceof Variant) == false) throw \"CHKINV failed\";\n",
611                r(rr)
612            ));
613            out.push_str("          ip += 2;\n          continue;\n");
614        }
615        x if x == vm::VM_INT => {
616            ensure(code, pc, 2)?;
617            let rr = code[pc + 1];
618            out.push_str(&format!(
619                "          {} = __tjs2dec_to_int({});\n",
620                r(rr),
621                r(rr)
622            ));
623            out.push_str("          ip += 2;\n          continue;\n");
624        }
625        x if x == vm::VM_REAL => {
626            ensure(code, pc, 2)?;
627            let rr = code[pc + 1];
628            out.push_str(&format!("          {} = Number({});\n", r(rr), r(rr)));
629            out.push_str("          ip += 2;\n          continue;\n");
630        }
631        x if x == vm::VM_STR => {
632            ensure(code, pc, 2)?;
633            let rr = code[pc + 1];
634            out.push_str(&format!("          {} = String({});\n", r(rr), r(rr)));
635            out.push_str("          ip += 2;\n          continue;\n");
636        }
637        x if x == vm::VM_OCTET => {
638            ensure(code, pc, 2)?;
639            let rr = code[pc + 1];
640            out.push_str(&format!("          {} = {};\n", r(rr), r(rr)));
641            out.push_str("          ip += 2;\n          continue;\n");
642        }
643
644        // inc/dec (incl. PD/PI; P is special)
645        x if x >= vm::VM_INC && x <= vm::VM_INCP => match x - vm::VM_INC {
646            0 => {
647                ensure(code, pc, 2)?;
648                let rr = code[pc + 1];
649                out.push_str(&format!("          {} = ({} + 1);\n", r(rr), r(rr)));
650                out.push_str("          ip += 2;\n          continue;\n");
651            }
652            1 => {
653                ensure(code, pc, 4)?;
654                let dst = code[pc + 1];
655                let objr = code[pc + 2];
656                let key = code[pc + 3];
657                out.push_str(&format!("          var __k = __d[{}];\n", key));
658                out.push_str(&format!(
659                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
660                    r(objr)
661                ));
662                out.push_str("          __t = (__t + 1);\n");
663                out.push_str(&format!(
664                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
665                    r(objr)
666                ));
667                if dst != 0 {
668                    out.push_str(&format!(
669                        "          {} = __t;
670",
671                        r(dst)
672                    ));
673                }
674                out.push_str(
675                    "          ip += 4;
676          continue;
677",
678                );
679            }
680            2 => {
681                ensure(code, pc, 4)?;
682                let dst = code[pc + 1];
683                let objr = code[pc + 2];
684                let keyr = code[pc + 3];
685                out.push_str(&format!("          var __k = {};\n", r(keyr)));
686                out.push_str(&format!(
687                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
688                    r(objr)
689                ));
690                out.push_str("          __t = (__t + 1);\n");
691                out.push_str(&format!(
692                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
693                    r(objr)
694                ));
695                if dst != 0 {
696                    out.push_str(&format!(
697                        "          {} = __t;
698",
699                        r(dst)
700                    ));
701                }
702                out.push_str(
703                    "          ip += 4;
704          continue;
705",
706                );
707            }
708            3 => {
709                ensure(code, pc, 3)?;
710                let dst = code[pc + 1];
711                let propr = code[pc + 2];
712                out.push_str(&format!(
713                    "          var __p = {};
714",
715                    r(propr)
716                ));
717                out.push_str(
718                    "          var __t = *__p;
719",
720                );
721                out.push_str(
722                    "          __t = (__t + 1);
723",
724                );
725                out.push_str(
726                    "          *__p = __t;
727",
728                );
729                if dst != 0 {
730                    out.push_str(&format!(
731                        "          {} = __t;
732",
733                        r(dst)
734                    ));
735                }
736                out.push_str(
737                    "          ip += 3;
738          continue;
739",
740                );
741            }
742            _ => {}
743        },
744        x if x >= vm::VM_DEC && x <= vm::VM_DECP => match x - vm::VM_DEC {
745            0 => {
746                ensure(code, pc, 2)?;
747                let rr = code[pc + 1];
748                out.push_str(&format!("          {} = ({} - 1);\n", r(rr), r(rr)));
749                out.push_str("          ip += 2;\n          continue;\n");
750            }
751            1 => {
752                ensure(code, pc, 4)?;
753                let dst = code[pc + 1];
754                let objr = code[pc + 2];
755                let key = code[pc + 3];
756                out.push_str(&format!("          var __k = __d[{}];\n", key));
757                out.push_str(&format!(
758                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
759                    r(objr)
760                ));
761                out.push_str("          __t = (__t - 1);\n");
762                out.push_str(&format!(
763                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
764                    r(objr)
765                ));
766                if dst != 0 {
767                    out.push_str(&format!(
768                        "          {} = __t;
769",
770                        r(dst)
771                    ));
772                }
773                out.push_str(
774                    "          ip += 4;
775          continue;
776",
777                );
778            }
779            2 => {
780                ensure(code, pc, 4)?;
781                let dst = code[pc + 1];
782                let objr = code[pc + 2];
783                let keyr = code[pc + 3];
784                out.push_str(&format!("          var __k = {};\n", r(keyr)));
785                out.push_str(&format!(
786                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
787                    r(objr)
788                ));
789                out.push_str("          __t = (__t - 1);\n");
790                out.push_str(&format!(
791                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
792                    r(objr)
793                ));
794                if dst != 0 {
795                    out.push_str(&format!(
796                        "          {} = __t;
797",
798                        r(dst)
799                    ));
800                }
801                out.push_str(
802                    "          ip += 4;
803          continue;
804",
805                );
806            }
807            3 => {
808                ensure(code, pc, 3)?;
809                let dst = code[pc + 1];
810                let propr = code[pc + 2];
811                out.push_str(&format!(
812                    "          var __p = {};
813",
814                    r(propr)
815                ));
816                out.push_str(
817                    "          var __t = *__p;
818",
819                );
820                out.push_str(
821                    "          __t = (__t - 1);
822",
823                );
824                out.push_str(
825                    "          *__p = __t;
826",
827                );
828                if dst != 0 {
829                    out.push_str(&format!(
830                        "          {} = __t;
831",
832                        r(dst)
833                    ));
834                }
835                out.push_str(
836                    "          ip += 3;
837          continue;
838",
839                );
840            }
841            _ => {}
842        },
843
844        // LOR/LAND (truthy semantics)
845        x if x >= vm::VM_LOR && x <= vm::VM_LORP => match x - vm::VM_LOR {
846            0 => {
847                ensure(code, pc, 3)?;
848                let a = code[pc + 1];
849                let b = code[pc + 2];
850                out.push_str(&format!(
851                    "          {} = (__tjs2dec_truthy({}) || __tjs2dec_truthy({}));\n",
852                    r(a),
853                    r(a),
854                    r(b)
855                ));
856                out.push_str("          ip += 3;\n          continue;\n");
857            }
858            1 => {
859                ensure(code, pc, 5)?;
860                let dst = code[pc + 1];
861                let objr = code[pc + 2];
862                let key = code[pc + 3];
863                let rhs = code[pc + 4];
864                out.push_str(&format!("          var __k = __d[{}];\n", key));
865                out.push_str(&format!(
866                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
867                    r(objr)
868                ));
869                out.push_str(&format!(
870                    "          __t = (__tjs2dec_truthy(__t) || __tjs2dec_truthy({}));\n",
871                    r(rhs)
872                ));
873                out.push_str(&format!(
874                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
875                    r(objr)
876                ));
877                if dst != 0 {
878                    out.push_str(&format!(
879                        "          {} = __t;
880",
881                        r(dst)
882                    ));
883                }
884                out.push_str(
885                    "          ip += 5;
886          continue;
887",
888                );
889            }
890            2 => {
891                ensure(code, pc, 5)?;
892                let dst = code[pc + 1];
893                let objr = code[pc + 2];
894                let keyr = code[pc + 3];
895                let rhs = code[pc + 4];
896                out.push_str(&format!("          var __k = {};\n", r(keyr)));
897                out.push_str(&format!(
898                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
899                    r(objr)
900                ));
901                out.push_str(&format!(
902                    "          __t = (__tjs2dec_truthy(__t) || __tjs2dec_truthy({}));\n",
903                    r(rhs)
904                ));
905                out.push_str(&format!(
906                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
907                    r(objr)
908                ));
909                if dst != 0 {
910                    out.push_str(&format!(
911                        "          {} = __t;
912",
913                        r(dst)
914                    ));
915                }
916                out.push_str(
917                    "          ip += 5;
918          continue;
919",
920                );
921            }
922            3 => {
923                ensure(code, pc, 4)?;
924                let dst = code[pc + 1];
925                let propr = code[pc + 2];
926                let rhs = code[pc + 3];
927                out.push_str(&format!(
928                    "          var __p = {};
929",
930                    r(propr)
931                ));
932                out.push_str(
933                    "          var __a = *__p;
934",
935                );
936                out.push_str(&format!(
937                    "          var __b = {};
938",
939                    r(rhs)
940                ));
941                out.push_str(&format!(
942                    "          var __t = (__tjs2dec_truthy(__a) || __tjs2dec_truthy(__b));
943"
944                ));
945                out.push_str(
946                    "          *__p = __t;
947",
948                );
949                if dst != 0 {
950                    out.push_str(&format!(
951                        "          {} = __t;
952",
953                        r(dst)
954                    ));
955                }
956                out.push_str(
957                    "          ip += 4;
958          continue;
959",
960                );
961            }
962            _ => {}
963        },
964        x if x >= vm::VM_LAND && x <= vm::VM_LANDP => match x - vm::VM_LAND {
965            0 => {
966                ensure(code, pc, 3)?;
967                let a = code[pc + 1];
968                let b = code[pc + 2];
969                out.push_str(&format!(
970                    "          {} = (__tjs2dec_truthy({}) && __tjs2dec_truthy({}));\n",
971                    r(a),
972                    r(a),
973                    r(b)
974                ));
975                out.push_str("          ip += 3;\n          continue;\n");
976            }
977            1 => {
978                ensure(code, pc, 5)?;
979                let dst = code[pc + 1];
980                let objr = code[pc + 2];
981                let key = code[pc + 3];
982                let rhs = code[pc + 4];
983                out.push_str(&format!("          var __k = __d[{}];\n", key));
984                out.push_str(&format!(
985                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
986                    r(objr)
987                ));
988                out.push_str(&format!(
989                    "          __t = (__tjs2dec_truthy(__t) && __tjs2dec_truthy({}));\n",
990                    r(rhs)
991                ));
992                out.push_str(&format!(
993                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
994                    r(objr)
995                ));
996                if dst != 0 {
997                    out.push_str(&format!(
998                        "          {} = __t;
999",
1000                        r(dst)
1001                    ));
1002                }
1003                out.push_str(
1004                    "          ip += 5;
1005          continue;
1006",
1007                );
1008            }
1009            2 => {
1010                ensure(code, pc, 5)?;
1011                let dst = code[pc + 1];
1012                let objr = code[pc + 2];
1013                let keyr = code[pc + 3];
1014                let rhs = code[pc + 4];
1015                out.push_str(&format!("          var __k = {};\n", r(keyr)));
1016                out.push_str(&format!(
1017                    "          var __t = __tjs2dec_get_prop({}, __k, false);\n",
1018                    r(objr)
1019                ));
1020                out.push_str(&format!(
1021                    "          __t = (__tjs2dec_truthy(__t) && __tjs2dec_truthy({}));\n",
1022                    r(rhs)
1023                ));
1024                out.push_str(&format!(
1025                    "          __tjs2dec_set_prop({}, __k, __t, false);\n",
1026                    r(objr)
1027                ));
1028                if dst != 0 {
1029                    out.push_str(&format!(
1030                        "          {} = __t;
1031",
1032                        r(dst)
1033                    ));
1034                }
1035                out.push_str(
1036                    "          ip += 5;
1037          continue;
1038",
1039                );
1040            }
1041            3 => {
1042                ensure(code, pc, 4)?;
1043                let dst = code[pc + 1];
1044                let propr = code[pc + 2];
1045                let rhs = code[pc + 3];
1046                out.push_str(&format!(
1047                    "          var __p = {};
1048",
1049                    r(propr)
1050                ));
1051                out.push_str(
1052                    "          var __a = *__p;
1053",
1054                );
1055                out.push_str(&format!(
1056                    "          var __b = {};
1057",
1058                    r(rhs)
1059                ));
1060                out.push_str(&format!(
1061                    "          var __t = (__tjs2dec_truthy(__a) && __tjs2dec_truthy(__b));
1062"
1063                ));
1064                out.push_str(
1065                    "          *__p = __t;
1066",
1067                );
1068                if dst != 0 {
1069                    out.push_str(&format!(
1070                        "          {} = __t;
1071",
1072                        r(dst)
1073                    ));
1074                }
1075                out.push_str(
1076                    "          ip += 4;
1077          continue;
1078",
1079                );
1080            }
1081            _ => {}
1082        },
1083        // Other op2_prop groups (P-variant remains special but executable)
1084        x if x >= vm::VM_BOR && x <= vm::VM_BORP => {
1085            emit_op2_prop(&mut out, code, pc, x - vm::VM_BOR, &r, "|", false)?;
1086            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1087        }
1088        x if x >= vm::VM_BXOR && x <= vm::VM_BXORP => {
1089            emit_op2_prop(&mut out, code, pc, x - vm::VM_BXOR, &r, "^", false)?;
1090            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1091        }
1092        x if x >= vm::VM_BAND && x <= vm::VM_BANDP => {
1093            emit_op2_prop(&mut out, code, pc, x - vm::VM_BAND, &r, "&", false)?;
1094            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1095        }
1096        x if x >= vm::VM_SAR && x <= vm::VM_SARP => {
1097            emit_op2_prop(&mut out, code, pc, x - vm::VM_SAR, &r, ">>", false)?;
1098            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1099        }
1100        x if x >= vm::VM_SAL && x <= vm::VM_SALP => {
1101            emit_op2_prop(&mut out, code, pc, x - vm::VM_SAL, &r, "<<", false)?;
1102            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1103        }
1104        x if x >= vm::VM_SR && x <= vm::VM_SRP => {
1105            emit_op2_prop(&mut out, code, pc, x - vm::VM_SR, &r, ">>>", false)?;
1106            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1107        }
1108        x if x >= vm::VM_ADD && x <= vm::VM_ADDP => {
1109            emit_op2_prop(&mut out, code, pc, x - vm::VM_ADD, &r, "+", false)?;
1110            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1111        }
1112        x if x >= vm::VM_SUB && x <= vm::VM_SUBP => {
1113            emit_op2_prop(&mut out, code, pc, x - vm::VM_SUB, &r, "-", false)?;
1114            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1115        }
1116        x if x >= vm::VM_MOD && x <= vm::VM_MODP => {
1117            emit_op2_prop(&mut out, code, pc, x - vm::VM_MOD, &r, "%", false)?;
1118            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1119        }
1120        x if x >= vm::VM_DIV && x <= vm::VM_DIVP => {
1121            emit_op2_prop(&mut out, code, pc, x - vm::VM_DIV, &r, "/", false)?;
1122            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1123        }
1124        x if x >= vm::VM_MUL && x <= vm::VM_MULP => {
1125            emit_op2_prop(&mut out, code, pc, x - vm::VM_MUL, &r, "*", false)?;
1126            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1127        }
1128        x if x >= vm::VM_IDIV && x <= vm::VM_IDIVP => {
1129            emit_op2_prop(&mut out, code, pc, x - vm::VM_IDIV, &r, "/", true)?;
1130            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1131        }
1132
1133        // deld/deli + typeofd/typeofi
1134        x if x == vm::VM_DELD || x == vm::VM_TYPEOFD => {
1135            ensure(code, pc, 4)?;
1136            let dst = code[pc + 1];
1137            let objr = code[pc + 2];
1138            let key = code[pc + 3];
1139            if x == vm::VM_DELD {
1140                out.push_str(&format!(
1141                    "          {} = __tjs2dec_del_prop({}, __d[{}], false);\n",
1142                    r(dst),
1143                    r(objr),
1144                    key
1145                ));
1146            } else {
1147                out.push_str(&format!(
1148                    "          {} = typeof(__tjs2dec_get_prop({}, __d[{}], false));\n",
1149                    r(dst),
1150                    r(objr),
1151                    key
1152                ));
1153            }
1154            out.push_str("          ip += 4;\n          continue;\n");
1155        }
1156        x if x == vm::VM_DELI || x == vm::VM_TYPEOFI => {
1157            ensure(code, pc, 4)?;
1158            let dst = code[pc + 1];
1159            let objr = code[pc + 2];
1160            let keyr = code[pc + 3];
1161            if x == vm::VM_DELI {
1162                out.push_str(&format!(
1163                    "          {} = __tjs2dec_del_prop({}, {}, false);\n",
1164                    r(dst),
1165                    r(objr),
1166                    r(keyr)
1167                ));
1168            } else {
1169                out.push_str(&format!(
1170                    "          {} = typeof(__tjs2dec_get_prop({}, {}, false));\n",
1171                    r(dst),
1172                    r(objr),
1173                    r(keyr)
1174                ));
1175            }
1176            out.push_str("          ip += 4;\n          continue;\n");
1177        }
1178
1179        // gpd/gpds, gpi/gpis
1180        x if x == vm::VM_GPD || x == vm::VM_GPDS => {
1181            ensure(code, pc, 4)?;
1182            let dst = code[pc + 1];
1183            let objr = code[pc + 2];
1184            let key = code[pc + 3];
1185            let ign = if x == vm::VM_GPDS { "true" } else { "false" };
1186            out.push_str(&format!(
1187                "          {} = __tjs2dec_get_prop({}, __d[{}], {});\n",
1188                r(dst),
1189                r(objr),
1190                key,
1191                ign
1192            ));
1193            out.push_str("          ip += 4;\n          continue;\n");
1194        }
1195        x if x == vm::VM_GPI || x == vm::VM_GPIS => {
1196            ensure(code, pc, 4)?;
1197            let dst = code[pc + 1];
1198            let objr = code[pc + 2];
1199            let keyr = code[pc + 3];
1200            let ign = if x == vm::VM_GPIS { "true" } else { "false" };
1201            out.push_str(&format!(
1202                "          {} = __tjs2dec_get_prop({}, {}, {});\n",
1203                r(dst),
1204                r(objr),
1205                r(keyr),
1206                ign
1207            ));
1208            out.push_str("          ip += 4;\n          continue;\n");
1209        }
1210
1211        // spd/spds/spde/spdeh, spi/spis/spie
1212        x if x == vm::VM_SPD || x == vm::VM_SPDS || x == vm::VM_SPDE || x == vm::VM_SPDEH => {
1213            ensure(code, pc, 4)?;
1214            let objr = code[pc + 1];
1215            let key = code[pc + 2];
1216            let valr = code[pc + 3];
1217            let ign = if x == vm::VM_SPDS { "true" } else { "false" };
1218            out.push_str(&format!(
1219                "          __tjs2dec_set_prop({}, __d[{}], {}, {});\n",
1220                r(objr),
1221                key,
1222                r(valr),
1223                ign
1224            ));
1225            out.push_str("          ip += 4;\n          continue;\n");
1226        }
1227        x if x == vm::VM_SPI || x == vm::VM_SPIS || x == vm::VM_SPIE => {
1228            ensure(code, pc, 4)?;
1229            let objr = code[pc + 1];
1230            let keyr = code[pc + 2];
1231            let valr = code[pc + 3];
1232            let ign = if x == vm::VM_SPIS { "true" } else { "false" };
1233            out.push_str(&format!(
1234                "          __tjs2dec_set_prop({}, {}, {}, {});\n",
1235                r(objr),
1236                r(keyr),
1237                r(valr),
1238                ign
1239            ));
1240            out.push_str("          ip += 4;\n          continue;\n");
1241        }
1242
1243        // call/callD/callI/new
1244        x if x == vm::VM_CALL || x == vm::VM_NEW => {
1245            ensure(code, pc, size)?;
1246            let dst = code[pc + 1];
1247            let func = code[pc + 2];
1248            let argc = code[pc + 3];
1249            out.push_str("          var __args = [];\n");
1250            out.push_str(&format!("          var __f = {};\n", r(func)));
1251            out.push_str(&emit_build_args(code, pc, 4, argc, &r));
1252            if x == vm::VM_NEW {
1253                out.push_str(&format!("          {} = new __f(...__args);\n", r(dst)));
1254            } else {
1255                out.push_str(&format!(
1256                    "          {} = __f.apply(__this, __args);\n",
1257                    r(dst)
1258                ));
1259            }
1260            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1261        }
1262        x if x == vm::VM_CALLD => {
1263            ensure(code, pc, size)?;
1264            // Layout: op, dst, obj, member_data, tmp_func_reg, argc, ...
1265            let dst = code[pc + 1];
1266            let objr = code[pc + 2];
1267            let key = code[pc + 3];
1268            let tmp = code[pc + 4];
1269            let argc = code[pc + 5];
1270            out.push_str("          var __args = [];\n");
1271            out.push_str(&format!("          var __o = {};\n", r(objr)));
1272            out.push_str(&format!("          var __m = __d[{}];\n", key));
1273            out.push_str("          var __f = __tjs2dec_get_prop(__o, __m, false);\n");
1274            out.push_str(&format!("          {} = __f;\n", r(tmp)));
1275            out.push_str(&emit_build_args(code, pc, 6, argc, &r));
1276            out.push_str(&format!("          {} = __f.apply(__o, __args);\n", r(dst)));
1277            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1278        }
1279        x if x == vm::VM_CALLI => {
1280            ensure(code, pc, size)?;
1281            // Layout: op, dst, obj, member_reg, tmp_func_reg, argc, ...
1282            let dst = code[pc + 1];
1283            let objr = code[pc + 2];
1284            let keyr = code[pc + 3];
1285            let tmp = code[pc + 4];
1286            let argc = code[pc + 5];
1287            out.push_str("          var __args = [];\n");
1288            out.push_str(&format!("          var __o = {};\n", r(objr)));
1289            out.push_str(&format!("          var __m = {};\n", r(keyr)));
1290            out.push_str("          var __f = __tjs2dec_get_prop(__o, __m, false);\n");
1291            out.push_str(&format!("          {} = __f;\n", r(tmp)));
1292            out.push_str(&emit_build_args(code, pc, 6, argc, &r));
1293            out.push_str(&format!("          {} = __f.apply(__o, __args);\n", r(dst)));
1294            out.push_str(&format!("          ip += {};\n          continue;\n", size));
1295        }
1296
1297        // setp/getp: property handler (unary '*' operator)
1298        x if x == vm::VM_SETP || x == vm::VM_GETP => {
1299            ensure(code, pc, 3)?;
1300            let a = code[pc + 1];
1301            let b = code[pc + 2];
1302            if op == vm::VM_GETP {
1303                // getp %dst, %propobj  => dst = *propobj
1304                if a != 0 {
1305                    out.push_str(&format!(
1306                        "          {} = *{};
1307",
1308                        r(a),
1309                        r(b)
1310                    ));
1311                } else {
1312                    out.push_str(&format!(
1313                        "          var __tmp = *{};
1314",
1315                        r(b)
1316                    ));
1317                }
1318            } else {
1319                // setp %propobj, %src  => *propobj = src
1320                out.push_str(&format!(
1321                    "          *{} = {};
1322",
1323                    r(a),
1324                    r(b)
1325                ));
1326            }
1327            out.push_str(
1328                "          ip += 3;
1329          continue;
1330",
1331            );
1332        }
1333
1334        // srv/global/throw
1335        x if x == vm::VM_SRV => {
1336            ensure(code, pc, 2)?;
1337            let rr = code[pc + 1];
1338            out.push_str(&format!("          __rv = {};\n", r(rr)));
1339            out.push_str("          ip += 2;\n          continue;\n");
1340        }
1341        x if x == vm::VM_GLOBAL => {
1342            ensure(code, pc, 2)?;
1343            let rr = code[pc + 1];
1344            out.push_str(&format!("          {} = global;\n", r(rr)));
1345            out.push_str("          ip += 2;\n          continue;\n");
1346        }
1347        x if x == vm::VM_THROW => {
1348            ensure(code, pc, 2)?;
1349            let rr = code[pc + 1];
1350            out.push_str(&format!("          throw {};\n", r(rr)));
1351        }
1352
1353        // try stack
1354        x if x == vm::VM_ENTRY => {
1355            ensure(code, pc, 3)?;
1356            let catch_ip = code[pc + 1];
1357            let exr = code[pc + 2];
1358            out.push_str(&format!("          __try.push([{}, {}]);\n", catch_ip, exr));
1359            out.push_str("          ip += 3;\n          continue;\n");
1360        }
1361        x if x == vm::VM_EXTRY => {
1362            ensure(code, pc, 1)?;
1363            out.push_str("          if (__try.length > 0) __try.pop();\n");
1364            out.push_str("          ip += 1;\n          continue;\n");
1365        }
1366
1367        // this / class-instance bookkeeping
1368        x if x == vm::VM_CHGTHIS => {
1369            ensure(code, pc, 3)?;
1370            let save = code[pc + 1];
1371            let newt = code[pc + 2];
1372            out.push_str(&format!("          {} = __this;\n", r(save)));
1373            out.push_str(&format!("          __this = {};\n", r(newt)));
1374            out.push_str("          ip += 3;\n          continue;\n");
1375        }
1376        x if x == vm::VM_ADDCI => {
1377            ensure(code, pc, 3)?;
1378            out.push_str("          ip += 3;\n          continue;\n");
1379        }
1380        x if x == vm::VM_REGMEMBER => {
1381            ensure(code, pc, 1)?;
1382            out.push_str("          ip += 1;\n          continue;\n");
1383        }
1384
1385        // ret
1386        x if x == vm::VM_RET => {
1387            ensure(code, pc, 1)?;
1388            out.push_str("          return __rv;\n");
1389        }
1390
1391        _ => {
1392            out.push_str(&format!(
1393                "          throw \"Unimplemented opcode: {} ({}) at ip={}\";\n",
1394                op,
1395                insn_str.split_whitespace().next().unwrap_or("?"),
1396                pc
1397            ));
1398        }
1399    }
1400
1401    Ok(out)
1402}
1403
1404fn emit_call_like(code: &[i32], pc: usize, obj: &Tjs2Object, op: i32) -> Result<(String, usize)> {
1405    // Layout (matches disasm::format_call):
1406    // CALL:  op, dst, func, argc, [args...]
1407    // NEW:   op, dst, func, argc, [args...]
1408    // CALLD: op, dst, obj, member_data, argc, ...
1409    // CALLI: op, dst, obj, member_reg,  argc, ...
1410    let mut out = String::new();
1411    let r = |idx: i32| -> String { reg_name(idx) };
1412
1413    match op {
1414        x if x == vm::VM_CALL || x == vm::VM_NEW => {
1415            ensure(code, pc, 4)?;
1416            let dst = code[pc + 1];
1417            let func = code[pc + 2];
1418            let argc = code[pc + 3];
1419            if argc >= 0 {
1420                let n = argc as usize;
1421                ensure(code, pc, 4 + n)?;
1422                let mut args = Vec::with_capacity(n);
1423                for j in 0..n {
1424                    args.push(r(code[pc + 4 + j]));
1425                }
1426                if op == vm::VM_NEW {
1427                    out.push_str(&format!(
1428                        "          {} = new {}({});\n",
1429                        r(dst),
1430                        r(func),
1431                        args.join(", ")
1432                    ));
1433                } else {
1434                    out.push_str(&format!(
1435                        "          {} = {}({});\n",
1436                        r(dst),
1437                        r(func),
1438                        args.join(", ")
1439                    ));
1440                }
1441                return Ok((out, 4 + n));
1442            }
1443
1444            if argc == -1 {
1445                // Omit args: forward current arguments.
1446                if op == vm::VM_NEW {
1447                    // There is no portable 'new' + 'apply' without eval.
1448                    // Keep the output executable by falling back to no-arg construction.
1449                    out.push_str(&format!("          // Omit-args NEW is not representable without eval; falling back.\n"));
1450                    out.push_str(&format!("          {} = new {}();\n", r(dst), r(func)));
1451                } else {
1452                    out.push_str(&format!("          {} = {}(...);\n", r(dst), r(func)));
1453                }
1454                return Ok((out, 4));
1455            }
1456
1457            if argc == -2 {
1458                ensure(code, pc, 5)?;
1459                let num = code[pc + 4] as usize;
1460                ensure(code, pc, 5 + num * 2)?;
1461                out.push_str("          var __args = [];\n");
1462                for j in 0..num {
1463                    let ty = code[pc + 5 + j * 2];
1464                    let v = code[pc + 5 + j * 2 + 1];
1465                    match ty {
1466                        FAT_NORMAL => {
1467                            out.push_str(&format!("          __args.push({});\n", r(v)));
1468                        }
1469                        FAT_EXPAND => {
1470                            out.push_str(&format!(
1471                                "          __tjs2dec_expand_args(__args, {});\n",
1472                                r(v)
1473                            ));
1474                        }
1475                        FAT_UNNAMED_EXPAND => {
1476                            out.push_str("          // Unnamed expand: best-effort ignored.\n");
1477                        }
1478                        _ => {
1479                            out.push_str(&format!("          throw \"Bad FAT type: {}\";\n", ty));
1480                        }
1481                    }
1482                }
1483                if op == vm::VM_NEW {
1484                    // There is no universally-available "new + apply" without eval.
1485                    // Keep this executable in most cases: fall back to no-arg construction.
1486                    out.push_str(&format!("          {} = new {}();\n", r(dst), r(func)));
1487                } else {
1488                    out.push_str(&format!(
1489                        "          {} = {}.apply(this, __args);\n",
1490                        r(dst),
1491                        r(func)
1492                    ));
1493                }
1494                return Ok((out, 5 + num * 2));
1495            }
1496
1497            // Unknown argc encoding.
1498            out.push_str(&format!("          {} = {}();\n", r(dst), r(func)));
1499            Ok((out, 4))
1500        }
1501        x if x == vm::VM_CALLD => {
1502            ensure(code, pc, 5)?;
1503            let dst = code[pc + 1];
1504            let objr = code[pc + 2];
1505            let memd = code[pc + 3];
1506            let argc = code[pc + 4];
1507
1508            // Member name is in vdata.
1509            let mem_expr = format!("__d[{}]", memd);
1510
1511            if argc >= 0 {
1512                let n = argc as usize;
1513                ensure(code, pc, 5 + n)?;
1514                let mut args = Vec::with_capacity(n);
1515                for j in 0..n {
1516                    args.push(r(code[pc + 5 + j]));
1517                }
1518                out.push_str(&format!(
1519                    "          {} = {}[{}]({});\n",
1520                    r(dst),
1521                    r(objr),
1522                    mem_expr,
1523                    args.join(", ")
1524                ));
1525                return Ok((out, 5 + n));
1526            }
1527            if argc == -1 {
1528                out.push_str(&format!(
1529                    "          {} = {}[{}](...);\n",
1530                    r(dst),
1531                    r(objr),
1532                    mem_expr
1533                ));
1534                return Ok((out, 5));
1535            }
1536            if argc == -2 {
1537                ensure(code, pc, 6)?;
1538                let num = code[pc + 5] as usize;
1539                ensure(code, pc, 6 + num * 2)?;
1540                out.push_str("          var __args = [];\n");
1541                for j in 0..num {
1542                    let ty = code[pc + 6 + j * 2];
1543                    let v = code[pc + 6 + j * 2 + 1];
1544                    match ty {
1545                        FAT_NORMAL => out.push_str(&format!("          __args.push({});\n", r(v))),
1546                        FAT_EXPAND => out.push_str(&format!(
1547                            "          __tjs2dec_expand_args(__args, {});\n",
1548                            r(v)
1549                        )),
1550                        FAT_UNNAMED_EXPAND => {
1551                            out.push_str("          // Unnamed expand: best-effort ignored.\n")
1552                        }
1553                        _ => out.push_str(&format!("          throw \"Bad FAT type: {}\";\n", ty)),
1554                    }
1555                }
1556                out.push_str(&format!(
1557                    "          {} = {}[{}].apply({}, __args);\n",
1558                    r(dst),
1559                    r(objr),
1560                    mem_expr,
1561                    r(objr)
1562                ));
1563                return Ok((out, 6 + num * 2));
1564            }
1565
1566            out.push_str(&format!(
1567                "          {} = {}[{}]();\n",
1568                r(dst),
1569                r(objr),
1570                mem_expr
1571            ));
1572            Ok((out, 5))
1573        }
1574        x if x == vm::VM_CALLI => {
1575            ensure(code, pc, 5)?;
1576            let dst = code[pc + 1];
1577            let objr = code[pc + 2];
1578            let memr = code[pc + 3];
1579            let argc = code[pc + 4];
1580            if argc >= 0 {
1581                let n = argc as usize;
1582                ensure(code, pc, 5 + n)?;
1583                let mut args = Vec::with_capacity(n);
1584                for j in 0..n {
1585                    args.push(r(code[pc + 5 + j]));
1586                }
1587                out.push_str(&format!(
1588                    "          {} = {}[{}]({});\n",
1589                    r(dst),
1590                    r(objr),
1591                    r(memr),
1592                    args.join(", ")
1593                ));
1594                return Ok((out, 5 + n));
1595            }
1596            if argc == -1 {
1597                out.push_str(&format!(
1598                    "          {} = {}[{}](...);\n",
1599                    r(dst),
1600                    r(objr),
1601                    r(memr)
1602                ));
1603                return Ok((out, 5));
1604            }
1605            if argc == -2 {
1606                ensure(code, pc, 6)?;
1607                let num = code[pc + 5] as usize;
1608                ensure(code, pc, 6 + num * 2)?;
1609                out.push_str("          var __args = [];\n");
1610                for j in 0..num {
1611                    let ty = code[pc + 6 + j * 2];
1612                    let v = code[pc + 6 + j * 2 + 1];
1613                    match ty {
1614                        FAT_NORMAL => out.push_str(&format!("          __args.push({});\n", r(v))),
1615                        FAT_EXPAND => out.push_str(&format!(
1616                            "          __tjs2dec_expand_args(__args, {});\n",
1617                            r(v)
1618                        )),
1619                        FAT_UNNAMED_EXPAND => {
1620                            out.push_str("          // Unnamed expand: best-effort ignored.\n")
1621                        }
1622                        _ => out.push_str(&format!("          throw \"Bad FAT type: {}\";\n", ty)),
1623                    }
1624                }
1625                out.push_str(&format!(
1626                    "          {} = {}[{}].apply({}, __args);\n",
1627                    r(dst),
1628                    r(objr),
1629                    r(memr),
1630                    r(objr)
1631                ));
1632                return Ok((out, 6 + num * 2));
1633            }
1634            out.push_str(&format!(
1635                "          {} = {}[{}]();\n",
1636                r(dst),
1637                r(objr),
1638                r(memr)
1639            ));
1640            Ok((out, 5))
1641        }
1642        _ => bail!("emit_call_like called with non-call opcode {}", op),
1643    }
1644}
1645
1646fn reg_name(idx: i32) -> String {
1647    if idx >= 0 {
1648        format!("r{}", idx)
1649    } else {
1650        format!("rN{}", -idx)
1651    }
1652}
1653
1654fn variant_to_tjs(v: &Variant, pools: &ConstPools, _obj_index: usize) -> String {
1655    match v {
1656        Variant::Void => "void".to_string(),
1657        Variant::NullObject => "null".to_string(),
1658        Variant::Unknown => "void /* Unknown */".to_string(),
1659        Variant::String(i) => {
1660            let s = pools
1661                .strings
1662                .get(*i as usize)
1663                .map(|x| x.as_str())
1664                .unwrap_or("");
1665            quote_tjs_string(s)
1666        }
1667        Variant::Octet(i) => {
1668            // We do not attempt to reconstruct binary octets at this stage.
1669            // Keep it executable: materialize as void.
1670            format!("void /* Octet #{} (not materialized) */", i)
1671        }
1672        Variant::Real(i) => pools
1673            .doubles
1674            .get(*i as usize)
1675            .copied()
1676            .unwrap_or(0.0)
1677            .to_string(),
1678        Variant::Byte(i) => pools
1679            .bytes
1680            .get(*i as usize)
1681            .copied()
1682            .unwrap_or(0)
1683            .to_string(),
1684        Variant::Short(i) => pools
1685            .shorts
1686            .get(*i as usize)
1687            .copied()
1688            .unwrap_or(0)
1689            .to_string(),
1690        Variant::Integer(i) => pools
1691            .ints
1692            .get(*i as usize)
1693            .copied()
1694            .unwrap_or(0)
1695            .to_string(),
1696        Variant::Long(i) => pools
1697            .longs
1698            .get(*i as usize)
1699            .copied()
1700            .unwrap_or(0)
1701            .to_string(),
1702        Variant::InterObject(i) => format!("__tjs2dec_objs[{}]", i),
1703        Variant::InterGenerator(i) => format!("__tjs2dec_objs[{}] /* InterGenerator */", i),
1704    }
1705}
1706
1707fn quote_tjs_string(s: &str) -> String {
1708    let mut out = String::new();
1709    out.push('"');
1710    for ch in s.chars() {
1711        match ch {
1712            '\\' => out.push_str("\\\\"),
1713            '"' => out.push_str("\\\""),
1714            '\n' => out.push_str("\\n"),
1715            '\r' => out.push_str("\\r"),
1716            '\t' => out.push_str("\\t"),
1717            _ => out.push(ch),
1718        }
1719    }
1720    out.push('"');
1721    out
1722}
1723
1724fn ensure(code: &[i32], i: usize, need: usize) -> Result<()> {
1725    if i + need > code.len() {
1726        bail!(
1727            "truncated instruction at {}: need {}, code_len {}",
1728            i,
1729            need,
1730            code.len()
1731        );
1732    }
1733    Ok(())
1734}
1735
1736fn insn_len(code: &[i32], pc: usize) -> Result<usize> {
1737    let op = code[pc];
1738
1739    if op == vm::VM_CONST {
1740        return Ok(3);
1741    }
1742    for base in [
1743        vm::VM_CP,
1744        vm::VM_CEQ,
1745        vm::VM_CDEQ,
1746        vm::VM_CLT,
1747        vm::VM_CGT,
1748        vm::VM_CHKINS,
1749        vm::VM_ADDCI,
1750        vm::VM_CHGTHIS,
1751    ] {
1752        if op == base {
1753            return Ok(3);
1754        }
1755    }
1756    for base in [
1757        vm::VM_CL,
1758        vm::VM_SRV,
1759        vm::VM_GLOBAL,
1760        vm::VM_THROW,
1761        vm::VM_TT,
1762        vm::VM_TF,
1763        vm::VM_SETF,
1764        vm::VM_SETNF,
1765        vm::VM_LNOT,
1766        vm::VM_BNOT,
1767        vm::VM_ASC,
1768        vm::VM_CHR,
1769        vm::VM_NUM,
1770        vm::VM_CHS,
1771        vm::VM_INV,
1772        vm::VM_CHKINV,
1773        vm::VM_TYPEOF,
1774        vm::VM_EVAL,
1775        vm::VM_EEXP,
1776        vm::VM_INT,
1777        vm::VM_REAL,
1778        vm::VM_STR,
1779        vm::VM_OCTET,
1780    ] {
1781        if op == base {
1782            return Ok(2);
1783        }
1784    }
1785    if op == vm::VM_CCL {
1786        return Ok(3);
1787    }
1788    for base in [vm::VM_JF, vm::VM_JNF, vm::VM_JMP] {
1789        if op == base {
1790            return Ok(2);
1791        }
1792    }
1793    if op == vm::VM_ENTRY {
1794        return Ok(3);
1795    }
1796    for base in [
1797        vm::VM_RET,
1798        vm::VM_NOP,
1799        vm::VM_NF,
1800        vm::VM_EXTRY,
1801        vm::VM_REGMEMBER,
1802        vm::VM_DEBUGGER,
1803    ] {
1804        if op == base {
1805            return Ok(1);
1806        }
1807    }
1808    if op == vm::VM_SETP || op == vm::VM_GETP {
1809        return Ok(3);
1810    }
1811    if op == vm::VM_DELD || op == vm::VM_TYPEOFD {
1812        return Ok(4);
1813    }
1814    if op == vm::VM_DELI || op == vm::VM_TYPEOFI {
1815        return Ok(4);
1816    }
1817    if op == vm::VM_GPD || op == vm::VM_GPDS {
1818        return Ok(4);
1819    }
1820    if op == vm::VM_SPD || op == vm::VM_SPDE || op == vm::VM_SPDEH || op == vm::VM_SPDS {
1821        return Ok(4);
1822    }
1823    if op == vm::VM_GPI || op == vm::VM_GPIS {
1824        return Ok(4);
1825    }
1826    if op == vm::VM_SPI || op == vm::VM_SPIE || op == vm::VM_SPIS {
1827        return Ok(4);
1828    }
1829
1830    // inc/dec variants
1831    for base in [vm::VM_INC, vm::VM_DEC] {
1832        if op == base {
1833            return Ok(2);
1834        }
1835        if op == base + 1 {
1836            return Ok(4);
1837        }
1838        if op == base + 2 {
1839            return Ok(4);
1840        }
1841        if op == base + 3 {
1842            return Ok(3);
1843        }
1844    }
1845    // binary op variants base..base+3
1846    for base in [
1847        vm::VM_LOR,
1848        vm::VM_LAND,
1849        vm::VM_BOR,
1850        vm::VM_BXOR,
1851        vm::VM_BAND,
1852        vm::VM_SAR,
1853        vm::VM_SAL,
1854        vm::VM_SR,
1855        vm::VM_ADD,
1856        vm::VM_SUB,
1857        vm::VM_MOD,
1858        vm::VM_DIV,
1859        vm::VM_IDIV,
1860        vm::VM_MUL,
1861    ] {
1862        if op == base {
1863            return Ok(3);
1864        }
1865        if op == base + 1 {
1866            return Ok(5);
1867        }
1868        if op == base + 2 {
1869            return Ok(5);
1870        }
1871        if op == base + 3 {
1872            return Ok(4);
1873        }
1874    }
1875
1876    // call/new and their variable length
1877    if op == vm::VM_CALL || op == vm::VM_NEW {
1878        ensure(code, pc, 4)?;
1879        let argc = code[pc + 3];
1880        if argc >= 0 {
1881            return Ok(4 + argc as usize);
1882        }
1883        if argc == -1 {
1884            return Ok(4);
1885        }
1886        if argc == -2 {
1887            ensure(code, pc, 5)?;
1888            let num = code[pc + 4] as usize;
1889            return Ok(5 + num * 2);
1890        }
1891        return Ok(4);
1892    }
1893    if op == vm::VM_CALLD || op == vm::VM_CALLI {
1894        ensure(code, pc, 5)?;
1895        let argc = code[pc + 4];
1896        if argc >= 0 {
1897            return Ok(5 + argc as usize);
1898        }
1899        if argc == -1 {
1900            return Ok(5);
1901        }
1902        if argc == -2 {
1903            ensure(code, pc, 6)?;
1904            let num = code[pc + 5] as usize;
1905            return Ok(6 + num * 2);
1906        }
1907        return Ok(5);
1908    }
1909
1910    Ok(1)
1911}