1use std::collections::HashMap;
2
3use byteorder;
4use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
5
6use strum::{EnumCount, EnumIter, FromRepr};
7
8#[derive(Hash, Eq, Debug, Clone, PartialEq, PartialOrd)]
10pub struct Instructions {
11 pub data: Vec<u8>,
12}
13
14pub struct OpcodeDefinition {
15 pub(crate) name: &'static str,
16 operand_width: Vec<i32>,
17}
18
19impl OpcodeDefinition {
20 pub fn name(&self) -> &'static str {
21 self.name
22 }
23
24 pub fn operand_widths(&self) -> &[i32] {
25 &self.operand_width
26 }
27}
28
29#[repr(u8)]
30#[derive(Debug, Hash, Eq, Clone, Copy, PartialEq, EnumCount, EnumIter, FromRepr)]
31pub enum Opcode {
32 OpConst,
33 OpAdd,
34 OpPop,
35 OpSub,
36 OpMul,
37 OpDiv,
38 OpTrue,
39 OpFalse,
40 OpEqual,
41 OpNotEqual,
42 OpGreaterThan,
43 OpMinus,
44 OpBang,
45 OpJumpNotTruthy,
46 OpJump,
47 OpNull,
48 OpGetGlobal,
49 OpSetGlobal,
50 OpArray,
51 OpHash,
52 OpIndex,
53 OpCall,
54 OpReturnValue,
55 OpReturn,
56 OpGetLocal,
57 OpSetLocal,
58 OpGetBuiltin,
59 OpClosure,
60 OpGetFree,
61 OpCurrentClosure,
62 OpClass,
63 OpMethod,
64 OpGetProperty,
65 OpSetProperty,
66 OpNew,
67}
68
69lazy_static! {
70 pub static ref DEFINITIONS: HashMap<Opcode, OpcodeDefinition> = {
71 let mut m = HashMap::new();
72 m.insert(
73 Opcode::OpConst,
74 OpcodeDefinition {
75 name: "OpConst",
76 operand_width: vec![2],
77 },
78 );
79 m.insert(
80 Opcode::OpAdd,
81 OpcodeDefinition {
82 name: "OpAdd",
83 operand_width: vec![],
84 },
85 );
86 m.insert(
87 Opcode::OpPop,
88 OpcodeDefinition {
89 name: "OpPop",
90 operand_width: vec![],
91 },
92 );
93 m.insert(
94 Opcode::OpSub,
95 OpcodeDefinition {
96 name: "OpSub",
97 operand_width: vec![],
98 },
99 );
100 m.insert(
101 Opcode::OpMul,
102 OpcodeDefinition {
103 name: "OpMul",
104 operand_width: vec![],
105 },
106 );
107 m.insert(
108 Opcode::OpDiv,
109 OpcodeDefinition {
110 name: "OpDiv",
111 operand_width: vec![],
112 },
113 );
114 m.insert(
115 Opcode::OpTrue,
116 OpcodeDefinition {
117 name: "OpTrue",
118 operand_width: vec![],
119 },
120 );
121 m.insert(
122 Opcode::OpFalse,
123 OpcodeDefinition {
124 name: "OpFalse",
125 operand_width: vec![],
126 },
127 );
128 m.insert(
129 Opcode::OpEqual,
130 OpcodeDefinition {
131 name: "OpEqual",
132 operand_width: vec![],
133 },
134 );
135 m.insert(
136 Opcode::OpNotEqual,
137 OpcodeDefinition {
138 name: "OpNotEqual",
139 operand_width: vec![],
140 },
141 );
142 m.insert(
143 Opcode::OpGreaterThan,
144 OpcodeDefinition {
145 name: "OpGreatThan",
146 operand_width: vec![],
147 },
148 );
149 m.insert(
150 Opcode::OpMinus,
151 OpcodeDefinition {
152 name: "OpMinus",
153 operand_width: vec![],
154 },
155 );
156 m.insert(
157 Opcode::OpBang,
158 OpcodeDefinition {
159 name: "OpBang",
160 operand_width: vec![],
161 },
162 );
163 m.insert(
164 Opcode::OpJumpNotTruthy,
165 OpcodeDefinition {
166 name: "OpJumpNotTruthy",
167 operand_width: vec![2],
168 },
169 );
170 m.insert(
171 Opcode::OpJump,
172 OpcodeDefinition {
173 name: "OpJump",
174 operand_width: vec![2],
175 },
176 );
177 m.insert(
178 Opcode::OpNull,
179 OpcodeDefinition {
180 name: "OpNull",
181 operand_width: vec![],
182 },
183 );
184 m.insert(
185 Opcode::OpGetGlobal,
186 OpcodeDefinition {
187 name: "OpGetGlobal",
188 operand_width: vec![2],
189 },
190 );
191 m.insert(
192 Opcode::OpSetGlobal,
193 OpcodeDefinition {
194 name: "OpSetGlobal",
195 operand_width: vec![2],
196 },
197 );
198 m.insert(
199 Opcode::OpArray,
200 OpcodeDefinition {
201 name: "OpArray",
202 operand_width: vec![2],
203 },
204 );
205 m.insert(
206 Opcode::OpHash,
207 OpcodeDefinition {
208 name: "OpHash",
209 operand_width: vec![2],
210 },
211 );
212 m.insert(
213 Opcode::OpIndex,
214 OpcodeDefinition {
215 name: "OpIndex",
216 operand_width: vec![],
217 },
218 );
219 m.insert(
220 Opcode::OpCall,
221 OpcodeDefinition {
222 name: "OpCall",
223 operand_width: vec![1],
224 },
225 );
226 m.insert(
227 Opcode::OpReturn,
228 OpcodeDefinition {
229 name: "OpReturn",
230 operand_width: vec![],
231 },
232 );
233 m.insert(
234 Opcode::OpReturnValue,
235 OpcodeDefinition {
236 name: "OpReturnValue",
237 operand_width: vec![],
238 },
239 );
240 m.insert(
241 Opcode::OpGetLocal,
242 OpcodeDefinition {
243 name: "OpGetLocal",
244 operand_width: vec![1],
245 },
246 );
247 m.insert(
248 Opcode::OpSetLocal,
249 OpcodeDefinition {
250 name: "OpSetLocal",
251 operand_width: vec![1],
252 },
253 );
254 m.insert(
255 Opcode::OpGetBuiltin,
256 OpcodeDefinition {
257 name: "OpGetBuiltin",
258 operand_width: vec![1],
259 },
260 );
261 m.insert(
262 Opcode::OpClosure,
263 OpcodeDefinition {
264 name: "OpClosure",
265 operand_width: vec![2, 1],
266 },
267 );
268 m.insert(
269 Opcode::OpGetFree,
270 OpcodeDefinition {
271 name: "OpGetFree",
272 operand_width: vec![1],
273 },
274 );
275 m.insert(
276 Opcode::OpCurrentClosure,
277 OpcodeDefinition {
278 name: "OpCurrentClosure",
279 operand_width: vec![],
280 },
281 );
282 m.insert(
283 Opcode::OpClass,
284 OpcodeDefinition {
285 name: "OpClass",
286 operand_width: vec![2],
287 },
288 );
289 m.insert(
290 Opcode::OpMethod,
291 OpcodeDefinition {
292 name: "OpMethod",
293 operand_width: vec![2, 1],
294 },
295 );
296 m.insert(
297 Opcode::OpGetProperty,
298 OpcodeDefinition {
299 name: "OpGetProperty",
300 operand_width: vec![2],
301 },
302 );
303 m.insert(
304 Opcode::OpSetProperty,
305 OpcodeDefinition {
306 name: "OpSetProperty",
307 operand_width: vec![2],
308 },
309 );
310 m.insert(
311 Opcode::OpNew,
312 OpcodeDefinition {
313 name: "OpNew",
314 operand_width: vec![1],
315 },
316 );
317 return m;
318 };
319}
320
321pub fn make_instructions(op: Opcode, operands: &Vec<usize>) -> Instructions {
322 let mut instructions = Vec::new();
323 instructions.push(op as u8);
324 let widths = &DEFINITIONS.get(&op).unwrap().operand_width;
325
326 for (o, w) in operands.into_iter().zip(widths) {
327 match w {
328 2 => {
329 instructions.write_u16::<BigEndian>(*o as u16).unwrap();
330 }
331 1 => {
332 instructions.write_u8(*o as u8).unwrap();
333 }
334 _ => {
335 panic!("unsupported operand width {}", w)
336 }
337 }
338 }
339
340 return Instructions {
341 data: instructions,
342 };
343}
344
345pub fn read_operands(def: &OpcodeDefinition, ins: &[u8]) -> (Vec<usize>, usize) {
346 let mut operands = Vec::with_capacity(def.operand_width.len());
347 let mut offset = 0;
348
349 for w in &def.operand_width {
350 match w {
351 2 => {
352 operands.push(BigEndian::read_u16(&ins[offset..offset + 2]) as usize);
353 offset = offset + 2;
354 }
355 1 => {
356 operands.push(ins[offset] as usize);
357 offset = offset + 1;
358 }
359 0 => {}
360 _ => {
361 panic!("unsupported operand width {} for read", w)
362 }
363 }
364 }
365
366 return (operands, offset);
367}
368
369pub fn concat_instructions(expected: &Vec<Instructions>) -> Instructions {
370 let mut out = Instructions {
371 data: vec![],
372 };
373
374 for instruction in expected {
375 out = out.merge_instructions(instruction)
376 }
377
378 return out;
379}
380
381impl Instructions {
382 pub fn string(&self) -> String {
384 let mut ret = String::new();
385 let mut i = 0;
386 while i < self.data.len() {
387 let op: u8 = self.data[i];
388 let Some(opcode) = Opcode::from_repr(op) else {
389 ret.push_str(&format!("{:04} <unknown opcode 0x{:02x}>\n", i, op));
390 i += 1;
391 continue;
392 };
393
394 let definition = DEFINITIONS.get(&opcode).unwrap();
395 let width: usize = definition.operand_width.iter().map(|w| *w as usize).sum();
396 if i + 1 + width > self.data.len() {
397 ret.push_str(&format!("{:04} {} <truncated operands>\n", i, definition.name));
398 break;
399 }
400 let (operands, read_size) = read_operands(definition, &self.data[i + 1..]);
401 ret.push_str(&format!("{:04} {}\n", i, Self::fmt_instructions(definition, &operands)));
402 i = i + 1 + read_size;
403 }
404
405 return ret;
406 }
407
408 fn fmt_instructions(def: &OpcodeDefinition, operands: &Vec<usize>) -> String {
409 match def.operand_width.len() {
410 2 => format!("{} {} {}", def.name, operands[0], operands[1]),
411 1 => format!("{} {}", def.name, operands[0]),
412 0 => format!("{}", def.name),
413 _ => {
414 panic!("unsupported operand width {}", def.operand_width.len());
415 }
416 }
417 }
418
419 pub fn merge_instructions(&self, other: &Instructions) -> Instructions {
420 let ins = vec![self, other];
421 return Instructions {
424 data: ins
425 .iter()
426 .fold(vec![], |sum, &i| [sum.as_slice(), i.data.as_slice()].concat()),
427 };
428 }
429}