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