tjs2dec\decompile/
decode.rs1use anyhow::{Result, bail};
2
3use crate::model::Tjs2Object;
4use crate::vmcodes::vm;
5
6#[derive(Debug, Clone)]
8pub struct Insn {
9 pub pc: usize,
10 pub op: i32,
11 pub size: usize,
12 pub words: Vec<i32>,
13}
14
15impl Insn {
16 pub fn mnemonic(&self) -> &'static str {
17 vm::name(self.op)
18 }
19
20 pub fn operands(&self) -> &[i32] {
21 &self.words[1..]
22 }
23}
24
25pub fn decode_object(obj: &Tjs2Object) -> Result<Vec<Insn>> {
27 let mut out = Vec::new();
28 let code = &obj.code;
29 let mut pc: usize = 0;
30 while pc < code.len() {
31 let insn = decode_one(code, pc)?;
32 pc += insn.size;
33 out.push(insn);
34 }
35 Ok(out)
36}
37
38pub fn decode_one(code: &[i32], pc: usize) -> Result<Insn> {
39 if pc >= code.len() {
40 bail!("pc out of range: {} (len={})", pc, code.len());
41 }
42 let op = code[pc];
43
44 let size: usize = if op == vm::VM_CONST {
46 3
47 } else if matches!(
48 op,
49 vm::VM_CP
50 | vm::VM_CEQ
51 | vm::VM_CDEQ
52 | vm::VM_CLT
53 | vm::VM_CGT
54 | vm::VM_CHKINS
55 | vm::VM_ADDCI
56 | vm::VM_CHGTHIS
57 ) {
58 3
59 } else if matches!(
60 op,
61 vm::VM_CL
62 | vm::VM_SRV
63 | vm::VM_GLOBAL
64 | vm::VM_THROW
65 | vm::VM_TT
66 | vm::VM_TF
67 | vm::VM_SETF
68 | vm::VM_SETNF
69 | vm::VM_LNOT
70 | vm::VM_BNOT
71 | vm::VM_ASC
72 | vm::VM_CHR
73 | vm::VM_NUM
74 | vm::VM_CHS
75 | vm::VM_INV
76 | vm::VM_CHKINV
77 | vm::VM_TYPEOF
78 | vm::VM_EVAL
79 | vm::VM_EEXP
80 | vm::VM_INT
81 | vm::VM_REAL
82 | vm::VM_STR
83 | vm::VM_OCTET
84 ) {
85 2
86 } else if op == vm::VM_CCL {
87 3
88 } else if op == vm::VM_JF || op == vm::VM_JNF || op == vm::VM_JMP {
89 2
90 } else if op == vm::VM_ENTRY {
91 3
92 } else if matches!(
93 op,
94 vm::VM_RET | vm::VM_NOP | vm::VM_NF | vm::VM_EXTRY | vm::VM_REGMEMBER | vm::VM_DEBUGGER
95 ) {
96 1
97 } else if op == vm::VM_SETP || op == vm::VM_GETP {
98 3
99 } else if op == vm::VM_DELD || op == vm::VM_TYPEOFD {
100 4
101 } else if op == vm::VM_DELI || op == vm::VM_TYPEOFI {
102 4
103 } else if op == vm::VM_GPD || op == vm::VM_GPDS {
104 4
105 } else if matches!(op, vm::VM_SPD | vm::VM_SPDE | vm::VM_SPDEH | vm::VM_SPDS) {
106 4
107 } else if op == vm::VM_GPI || op == vm::VM_GPIS {
108 4
109 } else if op == vm::VM_SPI || op == vm::VM_SPIE || op == vm::VM_SPIS {
110 4
111 } else if (vm::VM_INC..=vm::VM_INCP).contains(&op) {
112 match op - vm::VM_INC {
114 0 => 2,
115 1 | 2 => 4,
116 3 => 3,
117 _ => 1,
118 }
119 } else if (vm::VM_DEC..=vm::VM_DECP).contains(&op) {
120 match op - vm::VM_DEC {
122 0 => 2,
123 1 | 2 => 4,
124 3 => 3,
125 _ => 1,
126 }
127 } else if is_op2_prop(op) {
128 let base = op2_prop_base(op);
130 match op - base {
131 0 => 3,
132 1 | 2 => 5,
133 3 => 4,
134 _ => 1,
135 }
136 } else if op == vm::VM_CALL || op == vm::VM_CALLD || op == vm::VM_CALLI || op == vm::VM_NEW {
137 decode_call_size(code, pc)?
138 } else {
139 1
140 };
141
142 ensure(code, pc, size)?;
143 let mut words = Vec::with_capacity(size);
144 for j in 0..size {
145 words.push(code[pc + j]);
146 }
147 Ok(Insn {
148 pc,
149 op,
150 size,
151 words,
152 })
153}
154
155fn is_op2_prop(op: i32) -> bool {
156 matches!(
157 op,
158 _ if (vm::VM_LOR..=vm::VM_LOR + 3).contains(&op)
160 || (vm::VM_LAND..=vm::VM_LAND + 3).contains(&op)
161 || (vm::VM_BOR..=vm::VM_BOR + 3).contains(&op)
162 || (vm::VM_BXOR..=vm::VM_BXOR + 3).contains(&op)
163 || (vm::VM_BAND..=vm::VM_BAND + 3).contains(&op)
164 || (vm::VM_SAR..=vm::VM_SAR + 3).contains(&op)
165 || (vm::VM_SAL..=vm::VM_SAL + 3).contains(&op)
166 || (vm::VM_SR..=vm::VM_SR + 3).contains(&op)
167 || (vm::VM_ADD..=vm::VM_ADD + 3).contains(&op)
168 || (vm::VM_SUB..=vm::VM_SUB + 3).contains(&op)
169 || (vm::VM_MOD..=vm::VM_MOD + 3).contains(&op)
170 || (vm::VM_DIV..=vm::VM_DIV + 3).contains(&op)
171 || (vm::VM_IDIV..=vm::VM_IDIV + 3).contains(&op)
172 || (vm::VM_MUL..=vm::VM_MUL + 3).contains(&op)
173 )
174}
175
176fn op2_prop_base(op: i32) -> i32 {
177 for base in [
179 vm::VM_LOR,
180 vm::VM_LAND,
181 vm::VM_BOR,
182 vm::VM_BXOR,
183 vm::VM_BAND,
184 vm::VM_SAR,
185 vm::VM_SAL,
186 vm::VM_SR,
187 vm::VM_ADD,
188 vm::VM_SUB,
189 vm::VM_MOD,
190 vm::VM_DIV,
191 vm::VM_IDIV,
192 vm::VM_MUL,
193 ] {
194 if (base..=base + 3).contains(&op) {
195 return base;
196 }
197 }
198 op
199}
200
201fn decode_call_size(code: &[i32], pc: usize) -> Result<usize> {
202 let op = code[pc];
209 let header = match op {
210 x if x == vm::VM_CALL => 4,
211 x if x == vm::VM_NEW => 4,
212 _ => 5,
213 };
214 ensure(code, pc, header)?;
215 let argc = code[pc + header - 1];
216
217 if argc == -1 {
218 Ok(header)
219 } else if argc == -2 {
220 ensure(code, pc, header + 1)?;
221 let num = code[pc + header] as usize;
222 Ok(header + 1 + num * 2)
224 } else {
225 let num = argc as usize;
226 Ok(header + num)
227 }
228}
229
230fn ensure(code: &[i32], pc: usize, need: usize) -> Result<()> {
231 if pc + need > code.len() {
232 bail!(
233 "truncated instruction at {}: need {}, code_len {}",
234 pc,
235 need,
236 code.len()
237 );
238 }
239 Ok(())
240}