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 {
338 let mut instructions = Vec::new();
339 instructions.push(op as u8);
340 let widths = &DEFINITIONS.get(&op).unwrap().operand_width;
341
342 for (o, w) in operands.iter().zip(widths) {
343 match w {
344 2 => {
345 assert!(
346 *o <= u16::MAX as usize,
347 "{:?} operand {} does not fit in 2 bytes; the compiler must \
348 reject it before emitting rather than truncate it here",
349 op,
350 o
351 );
352 instructions.write_u16::<BigEndian>(*o as u16).unwrap();
353 }
354 1 => {
355 assert!(
356 *o <= u8::MAX as usize,
357 "{:?} operand {} does not fit in 1 byte; the compiler must \
358 reject it before emitting rather than truncate it here",
359 op,
360 o
361 );
362 instructions.write_u8(*o as u8).unwrap();
363 }
364 _ => {
365 panic!("unsupported operand width {}", w)
366 }
367 }
368 }
369
370 return Instructions {
371 data: instructions,
372 };
373}
374
375pub fn read_operands(def: &OpcodeDefinition, ins: &[u8]) -> (Vec<usize>, usize) {
376 let mut operands = Vec::with_capacity(def.operand_width.len());
377 let mut offset = 0;
378
379 for w in &def.operand_width {
380 match w {
381 2 => {
382 operands.push(BigEndian::read_u16(&ins[offset..offset + 2]) as usize);
383 offset += 2;
384 }
385 1 => {
386 operands.push(ins[offset] as usize);
387 offset += 1;
388 }
389 0 => {}
390 _ => {
391 panic!("unsupported operand width {} for read", w)
392 }
393 }
394 }
395
396 return (operands, offset);
397}
398
399pub fn concat_instructions(expected: &Vec<Instructions>) -> Instructions {
400 let mut out = Instructions {
401 data: vec![],
402 };
403
404 for instruction in expected {
405 out = out.merge_instructions(instruction)
406 }
407
408 return out;
409}
410
411impl Instructions {
412 pub fn string(&self) -> String {
414 let mut ret = String::new();
415 let mut i = 0;
416 while i < self.data.len() {
417 let op: u8 = self.data[i];
418 let Some(opcode) = Opcode::from_repr(op) else {
419 ret.push_str(&format!("{:04} <unknown opcode 0x{:02x}>\n", i, op));
420 i += 1;
421 continue;
422 };
423
424 let definition = DEFINITIONS.get(&opcode).unwrap();
425 let width: usize = definition.operand_width.iter().map(|w| *w as usize).sum();
426 if i + 1 + width > self.data.len() {
427 ret.push_str(&format!("{:04} {} <truncated operands>\n", i, definition.name));
428 break;
429 }
430 let (operands, read_size) = read_operands(definition, &self.data[i + 1..]);
431 ret.push_str(&format!("{:04} {}\n", i, Self::fmt_instructions(definition, &operands)));
432 i = i + 1 + read_size;
433 }
434
435 return ret;
436 }
437
438 fn fmt_instructions(def: &OpcodeDefinition, operands: &[usize]) -> String {
439 match def.operand_width.len() {
440 2 => format!("{} {} {}", def.name, operands[0], operands[1]),
441 1 => format!("{} {}", def.name, operands[0]),
442 0 => def.name.to_string(),
443 _ => {
444 panic!("unsupported operand width {}", def.operand_width.len());
445 }
446 }
447 }
448
449 pub fn merge_instructions(&self, other: &Instructions) -> Instructions {
450 let ins = [self, other];
451 return Instructions {
454 data: ins
455 .iter()
456 .fold(vec![], |sum, &i| [sum.as_slice(), i.data.as_slice()].concat()),
457 };
458 }
459}