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