1use std::collections::{HashMap, HashSet};
4use std::fmt::Debug;
5use std::mem::size_of;
6use std::rc::Rc;
7
8use crate::codegen::TraitBuilder;
9use crate::common::{ErrorKind, Location, RenderedSignature};
10use crate::gc::{Gc, GcRaw, Memory};
11use crate::value::{Closure, RawValue};
12
13#[derive(Clone, Copy, PartialEq, Eq, Hash)]
15pub struct Opr24 {
16 bytes: [u8; 3],
17}
18
19#[derive(Debug)]
21pub struct U32TooBig(());
22
23impl Opr24 {
24 pub const MAX: u32 = (1 << 24);
25
26 pub fn new(x: u32) -> Result<Self, U32TooBig> {
28 if x < Self::MAX {
29 Ok(Self {
30 bytes: [
31 (x & 0xFF) as u8,
32 ((x >> 8) & 0xFF) as u8,
33 ((x >> 16) & 0xFF) as u8,
34 ],
35 })
36 } else {
37 Err(U32TooBig(()))
38 }
39 }
40
41 pub fn pack<T>(x: T) -> Self
43 where
44 T: PackableToOpr24,
45 {
46 x.pack_to_opr24()
47 }
48
49 pub fn unpack<T>(self) -> T
51 where
52 T: PackableToOpr24,
53 {
54 T::unpack_from_opr24(self)
55 }
56}
57
58impl From<u8> for Opr24 {
59 fn from(value: u8) -> Self {
60 Self {
61 bytes: [value, 0, 0],
62 }
63 }
64}
65
66impl TryFrom<usize> for Opr24 {
67 type Error = U32TooBig;
68
69 fn try_from(value: usize) -> Result<Self, Self::Error> {
70 Self::new(u32::try_from(value).map_err(|_| U32TooBig(()))?)
71 }
72}
73
74impl From<Opr24> for u32 {
75 fn from(opr: Opr24) -> u32 {
76 (opr.bytes[0] as u32) | ((opr.bytes[1] as u32) << 8) | ((opr.bytes[2] as u32) << 16)
77 }
78}
79
80impl From<Opr24> for usize {
81 fn from(opr: Opr24) -> usize {
82 (opr.bytes[0] as usize) | ((opr.bytes[1] as usize) << 8) | ((opr.bytes[2] as usize) << 16)
83 }
84}
85
86impl std::fmt::Debug for Opr24 {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 write!(f, "{:x}", u32::from(*self))
89 }
90}
91
92impl std::fmt::Display for Opr24 {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 write!(f, "{:?}", self)
95 }
96}
97
98pub trait PackableToOpr24 {
99 fn pack_to_opr24(self) -> Opr24;
100 fn unpack_from_opr24(opr: Opr24) -> Self;
101}
102
103impl PackableToOpr24 for (u16, u8) {
104 fn pack_to_opr24(self) -> Opr24 {
105 unsafe { Opr24::new((self.0 as u32) << 8 | self.1 as u32).unwrap_unchecked() }
108 }
109
110 fn unpack_from_opr24(opr: Opr24) -> Self {
111 let x = u32::from(opr);
112 let big = ((x & 0xFFFF00) >> 8) as u16;
113 let small = (x & 0x0000FF) as u8;
114 (big, small)
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120#[repr(u8)]
121pub enum Opcode {
122 #[allow(unused)]
125 Nop,
126
127 PushNil,
129 PushTrue,
131 PushFalse,
133 PushNumber,
135 PushString,
137 CreateClosure,
139 CreateType,
142 CreateStruct,
145 CreateTrait,
147 CreateList,
149 CreateDict,
152
153 AssignGlobal,
155 SinkGlobal,
157 GetGlobal,
159 AssignLocal,
161 SinkLocal,
163 GetLocal,
165 AssignUpvalue,
167 SinkUpvalue,
169 GetUpvalue,
171 CloseLocal,
173 AssignField,
177 SinkField,
180 GetField,
183
184 Swap,
186 Discard,
188
189 JumpForward,
194 JumpForwardIfFalsy,
197 JumpForwardIfTruthy,
200 JumpBackward,
203 EnterBreakableBlock,
205 ExitBreakableBlock,
208
209 Call,
211 CallMethod,
214 Return,
216
217 Implement,
219
220 Negate,
222 Add,
224 Subtract,
226 Multiply,
228 Divide,
230
231 Not,
233 Equal,
235 Less,
237 LessEqual,
239
240 Halt,
242}
243
244#[derive(Debug)]
246pub struct JumpTooFar(());
247
248impl Opcode {
249 pub const INSTRUCTION_SIZE: usize = 4;
251
252 fn forward_jump_offset(from: usize, to: usize) -> Result<Opr24, JumpTooFar> {
254 assert!(to >= from);
255 let offset = to - from - Self::INSTRUCTION_SIZE;
256 if u32::try_from(offset).is_err() {
257 return Err(JumpTooFar(()));
258 }
259 Opr24::new(offset as u32).map_err(|_| JumpTooFar(()))
260 }
261
262 pub fn jump_forward(from: usize, to: usize) -> Result<(Self, Opr24), JumpTooFar> {
264 let offset = Self::forward_jump_offset(from, to)?;
265 Ok((Self::JumpForward, offset))
266 }
267
268 pub fn jump_forward_if_falsy(from: usize, to: usize) -> Result<(Self, Opr24), JumpTooFar> {
270 let offset = Self::forward_jump_offset(from, to)?;
271 Ok((Self::JumpForwardIfFalsy, offset))
272 }
273
274 pub fn jump_forward_if_truthy(from: usize, to: usize) -> Result<(Self, Opr24), JumpTooFar> {
276 let offset = Self::forward_jump_offset(from, to)?;
277 Ok((Self::JumpForwardIfTruthy, offset))
278 }
279
280 fn backward_jump_offset(from: usize, to: usize) -> Result<Opr24, JumpTooFar> {
282 assert!(to <= from);
283 let offset = from - to + Self::INSTRUCTION_SIZE;
284 if u32::try_from(offset).is_err() {
285 return Err(JumpTooFar(()));
286 }
287 Opr24::new(offset as u32).map_err(|_| JumpTooFar(()))
288 }
289
290 pub fn jump_backward(from: usize, to: usize) -> Result<(Self, Opr24), JumpTooFar> {
292 let offset = Self::backward_jump_offset(from, to)?;
293 Ok((Self::JumpBackward, offset))
294 }
295}
296
297pub struct Chunk {
299 pub module_name: Rc<str>,
301 bytes: Vec<u8>,
303 locations: Vec<Location>,
305 pub codegen_location: Location,
307 pub preallocate_stack_slots: u32,
309}
310
311impl Chunk {
312 pub fn new(module_name: Rc<str>) -> Self {
314 Self {
315 module_name,
316 bytes: Vec::new(),
317 locations: Vec::new(),
318 codegen_location: Location::UNINIT,
319 preallocate_stack_slots: 0,
320 }
321 }
322
323 pub fn emit(&mut self, what: impl EncodeInstruction) -> usize {
325 let position = self.bytes.len();
326 let mut bytes = [0; 4];
327 what.encode_instruction(&mut bytes);
328 self.bytes.extend_from_slice(&bytes);
329 let end = self.bytes.len();
330 let emitted = end - position;
331 for _ in 0..emitted / 4 {
332 self.locations.push(self.codegen_location);
333 }
334 position
335 }
336
337 pub fn emit_number(&mut self, number: f64) {
339 let bytes = number.to_le_bytes();
340 self.bytes.extend_from_slice(&bytes);
341 self.locations.push(self.codegen_location);
343 self.locations.push(self.codegen_location);
344 }
345
346 pub fn emit_string(&mut self, string: &str) {
350 let start = self.len();
351
352 let len = string.len() as u64;
355 let len_bytes: [u8; 8] = len.to_le_bytes();
356 self.bytes.extend_from_slice(&len_bytes);
357 self.bytes.extend(string.as_bytes());
358 let padded_len = (string.len() + 3) & !3;
359 let padding = padded_len - string.len();
360 for _ in 0..padding {
361 self.bytes.push(0);
362 }
363
364 let end = self.len();
365 for _ in (0..end - start).step_by(4) {
366 self.locations.push(self.codegen_location);
367 }
368 }
369
370 pub fn patch(&mut self, position: usize, instruction: impl EncodeInstruction) {
372 let mut bytes = [0; 4];
373 instruction.encode_instruction(&mut bytes);
374 self.bytes[position..position + Opcode::INSTRUCTION_SIZE].copy_from_slice(&bytes);
375 }
376
377 pub unsafe fn read_instruction(&self, pc: &mut usize) -> (Opcode, Opr24) {
382 let bytes = &self.bytes[*pc..*pc + Opcode::INSTRUCTION_SIZE];
383 let mut bytes = <[u8; Opcode::INSTRUCTION_SIZE]>::try_from(bytes).unwrap();
384 let opcode: Opcode = std::mem::transmute(bytes[0]);
385 bytes[0] = 0;
386 let operand = Opr24 {
387 bytes: [bytes[1], bytes[2], bytes[3]],
388 };
389 *pc += Opcode::INSTRUCTION_SIZE;
390 (opcode, operand)
391 }
392
393 pub unsafe fn read_number(&self, pc: &mut usize) -> f64 {
398 const SIZE: usize = std::mem::size_of::<f64>();
399 let bytes = &self.bytes[*pc..*pc + SIZE];
400 let bytes: [u8; SIZE] = bytes.try_into().unwrap();
401 let number = f64::from_le_bytes(bytes);
402 *pc += SIZE;
403 number
404 }
405
406 pub unsafe fn read_string(&self, pc: &mut usize) -> &str {
412 let len_bytes: [u8; 8] = self.bytes[*pc..*pc + size_of::<u64>()].try_into().unwrap();
413 let len = u64::from_le_bytes(len_bytes);
414 *pc += size_of::<u64>();
415 let string = &self.bytes[*pc..*pc + len as usize];
416 let padded_len = (len + 3) & !3;
417 *pc += padded_len as usize;
418 std::str::from_utf8_unchecked(string)
419 }
420
421 pub fn len(&self) -> usize {
423 self.bytes.len()
424 }
425
426 pub fn is_empty(&self) -> bool {
428 self.len() == 0
429 }
430
431 pub fn location(&self, pc: usize) -> Location {
433 let index = pc >> 2;
434 self.locations.get(index).copied().unwrap_or(Location::UNINIT)
435 }
436
437 pub fn at_end(&self, pc: usize) -> bool {
439 pc >= self.bytes.len()
440 }
441}
442
443impl Debug for Chunk {
444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445 f.debug_struct("Chunk")
446 .field("module_name", &self.module_name)
447 .field("preallocate_stack_slots", &self.preallocate_stack_slots)
448 .finish()?;
449 writeln!(f)?;
450
451 let mut pc = 0;
452 while !self.at_end(pc) {
453 let location = self.location(pc);
454 let show_pc = pc;
455 let (opcode, operand) = unsafe { self.read_instruction(&mut pc) };
456 write!(f, "{show_pc:06x} {} {opcode:?}({operand:?}) ", location)?;
457
458 #[allow(clippy::single_match)]
459 match opcode {
460 Opcode::PushNumber => write!(f, "{}", unsafe { self.read_number(&mut pc) })?,
461 Opcode::PushString | Opcode::CreateType => {
462 write!(f, "{:?}", unsafe { self.read_string(&mut pc) })?
463 }
464 | Opcode::JumpForward | Opcode::JumpForwardIfFalsy | Opcode::JumpForwardIfTruthy => {
465 write!(
466 f,
467 "-> {:06x}",
468 pc + u32::from(operand) as usize + Opcode::INSTRUCTION_SIZE
469 )?;
470 }
471 Opcode::JumpBackward => {
472 write!(
473 f,
474 "-> {:06x}",
475 pc - u32::from(operand) as usize + Opcode::INSTRUCTION_SIZE
476 )?;
477 }
478 Opcode::CallMethod => {
479 let (method_index, argument_count) = operand.unpack();
480 let operand = u32::from(operand);
481 write!(f, "{operand:06x}:[mi={method_index}, ac={argument_count}]")?;
482 }
483 _ => (),
484 }
485
486 writeln!(f)?;
487 }
488 Ok(())
489 }
490}
491
492pub trait EncodeInstruction {
494 fn encode_instruction(&self, bytes: &mut [u8; Opcode::INSTRUCTION_SIZE]);
495}
496
497impl EncodeInstruction for (Opcode, Opr24) {
498 fn encode_instruction(&self, bytes: &mut [u8; Opcode::INSTRUCTION_SIZE]) {
499 bytes[0] = self.0 as u8;
500 bytes[1] = self.1.bytes[0];
501 bytes[2] = self.1.bytes[1];
502 bytes[3] = self.1.bytes[2];
503 }
504}
505
506impl EncodeInstruction for (Opcode, u16) {
507 fn encode_instruction(&self, bytes: &mut [u8; Opcode::INSTRUCTION_SIZE]) {
508 bytes[0] = self.0 as u8;
509 let ubytes = self.1.to_le_bytes();
510 bytes[1] = ubytes[0];
511 bytes[2] = ubytes[1];
512 bytes[3] = 0;
513 }
514}
515
516impl EncodeInstruction for Opcode {
517 fn encode_instruction(&self, bytes: &mut [u8; Opcode::INSTRUCTION_SIZE]) {
518 (*self, Opr24 { bytes: [0, 0, 0] }).encode_instruction(bytes)
519 }
520}
521
522#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
524pub enum CaptureKind {
525 Local(Opr24),
527 Upvalue(Opr24),
529}
530
531pub type ForeignFunction = Box<dyn FnMut(&mut Memory, &[RawValue]) -> Result<RawValue, ErrorKind>>;
533
534#[derive(Debug, Clone, Copy, PartialEq, Eq)]
536pub enum Control {
537 GcCollect,
538}
539
540pub enum FunctionKind {
542 Bytecode {
543 chunk: Rc<Chunk>,
544 captured_locals: Vec<CaptureKind>,
545 },
546 Foreign(ForeignFunction),
547 Control(Control),
548}
549
550impl std::fmt::Debug for FunctionKind {
551 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
552 match self {
553 Self::Bytecode {
554 chunk,
555 captured_locals,
556 } => f
557 .debug_struct("Bytecode")
558 .field("chunk", chunk)
559 .field("captured_locals", captured_locals)
560 .finish(),
561 Self::Foreign(..) => f.debug_struct("Foreign").finish_non_exhaustive(),
562 Self::Control(ctl) => f.debug_tuple("Control").field(ctl).finish(),
563 }
564 }
565}
566
567#[derive(Debug)]
569pub struct Function {
570 pub name: Rc<str>,
571 pub parameter_count: Option<u16>,
572
573 pub kind: FunctionKind,
574
575 pub hidden_in_stack_traces: bool,
579}
580
581#[derive(Debug, Clone, PartialEq, Eq, Hash)]
583pub struct FunctionSignature {
584 pub name: Rc<str>,
585 pub arity: Option<u16>,
587 pub trait_id: Option<Opr24>,
590}
591
592impl FunctionSignature {
593 pub fn new(name: impl Into<Rc<str>>, arity: impl Into<Option<u16>>) -> Self {
595 Self {
596 name: name.into(),
597 arity: arity.into(),
598 trait_id: None,
599 }
600 }
601
602 pub fn render(&self, env: &Environment) -> RenderedSignature {
604 RenderedSignature {
605 name: Rc::clone(&self.name),
606 arity: self.arity.map(|x| x - 1),
608 trait_name: self
609 .trait_id
610 .and_then(|index| env.get_trait(index))
611 .map(|prototype| Rc::clone(&prototype.name)),
612 }
613 }
614}
615
616#[derive(Debug)]
618pub struct Environment {
619 globals: HashMap<String, Opr24>,
621 functions: Vec<Function>,
623 method_indices: HashMap<FunctionSignature, u16>,
625 method_signatures: Vec<FunctionSignature>,
627 pub builtin_dtables: BuiltinDispatchTables,
629 prototypes: Vec<Option<Prototype>>,
631 free_prototypes: Vec<Opr24>,
633 traits: Vec<TraitPrototype>,
635}
636
637impl Environment {
638 pub fn new(builtin_dtables: BuiltinDispatchTables) -> Self {
640 Self {
641 globals: HashMap::new(),
642 functions: Vec::new(),
643 method_indices: HashMap::new(),
644 method_signatures: Vec::new(),
645 builtin_dtables,
646 prototypes: Vec::new(),
647 free_prototypes: Vec::new(),
648 traits: Vec::new(),
649 }
650 }
651
652 pub fn create_global(&mut self, name: &str) -> Result<Opr24, ErrorKind> {
655 if self.globals.contains_key(name) {
656 Ok(*self.globals.get(name).unwrap())
657 } else {
658 let slot = Opr24::try_from(self.globals.len()).map_err(|_| ErrorKind::TooManyGlobals)?;
659 self.globals.insert(name.to_owned(), slot);
660 Ok(slot)
661 }
662 }
663
664 pub fn get_global(&self, name: &str) -> Option<Opr24> {
666 self.globals.get(name).copied()
667 }
668
669 pub fn create_function(&mut self, function: Function) -> Result<Opr24, ErrorKind> {
671 let slot = Opr24::try_from(self.functions.len()).map_err(|_| ErrorKind::TooManyFunctions)?;
672 self.functions.push(function);
673 Ok(slot)
674 }
675
676 pub(crate) unsafe fn get_function_unchecked(&self, id: Opr24) -> &Function {
680 self.functions.get_unchecked(u32::from(id) as usize)
681 }
682
683 pub(crate) unsafe fn get_function_unchecked_mut(&mut self, id: Opr24) -> &mut Function {
687 self.functions.get_unchecked_mut(u32::from(id) as usize)
688 }
689
690 pub fn get_method_index(&mut self, signature: &FunctionSignature) -> Result<u16, ErrorKind> {
693 if let Some(&index) = self.method_indices.get(signature) {
695 Ok(index)
696 } else {
697 let index =
700 u16::try_from(self.method_indices.len()).map_err(|_| ErrorKind::TooManyMethods)?;
701 self.method_indices.insert(signature.clone(), index);
702 self.method_signatures.push(signature.clone());
703 Ok(index)
704 }
705 }
706
707 pub fn get_method_signature(&self, method_index: u16) -> Option<&FunctionSignature> {
710 self.method_signatures.get(method_index as usize)
711 }
712
713 pub(crate) fn create_prototype(&mut self, proto: Prototype) -> Result<Opr24, ErrorKind> {
715 let slot = if let Some(slot) = self.free_prototypes.pop() {
716 self.prototypes[u32::from(slot) as usize] = Some(proto);
717 slot
718 } else {
719 let slot = Opr24::try_from(self.prototypes.len()).map_err(|_| ErrorKind::TooManyImpls)?;
720 self.prototypes.push(Some(proto));
721 slot
722 };
723 Ok(slot)
724 }
725
726 pub(crate) unsafe fn take_prototype_unchecked(&mut self, id: Opr24) -> Prototype {
730 let proto =
731 self.prototypes.get_unchecked_mut(u32::from(id) as usize).take().unwrap_unchecked();
732 self.free_prototypes.push(id);
733 proto
734 }
735
736 pub fn create_trait(&mut self, name: Rc<str>) -> Result<Opr24, ErrorKind> {
738 let slot_index = self.traits.len();
739 let slot = Opr24::try_from(slot_index).map_err(|_| ErrorKind::TooManyTraits)?;
740 self.traits.push(TraitPrototype {
741 name,
742 required: HashSet::new(),
743 shims: vec![],
744 });
745 Ok(slot)
746 }
747
748 pub fn get_trait(&self, id: Opr24) -> Option<&TraitPrototype> {
750 self.traits.get(usize::from(id))
751 }
752
753 pub fn get_trait_mut(&mut self, id: Opr24) -> Option<&mut TraitPrototype> {
755 self.traits.get_mut(usize::from(id))
756 }
757}
758
759#[derive(Debug)]
761pub struct DispatchTable {
762 pub pretty_name: Rc<str>,
764 pub type_name: Rc<str>,
765 pub instance: Option<GcRaw<DispatchTable>>,
767 methods: Vec<Option<GcRaw<Closure>>>,
769}
770
771impl DispatchTable {
772 fn new(pretty_name: impl Into<Rc<str>>, type_name: impl Into<Rc<str>>) -> Self {
774 Self {
775 pretty_name: pretty_name.into(),
776 type_name: type_name.into(),
777 instance: None,
778 methods: Vec::new(),
779 }
780 }
781
782 pub fn new_for_type(type_name: impl Into<Rc<str>>) -> Self {
784 let type_name = type_name.into();
785 Self::new(format!("type {type_name}"), type_name)
786 }
787
788 pub fn new_for_instance(type_name: impl Into<Rc<str>>) -> Self {
790 let type_name = type_name.into();
791 Self::new(Rc::clone(&type_name), type_name)
792 }
793
794 pub fn get_method(&self, index: u16) -> Option<GcRaw<Closure>> {
796 self.methods.get(index as usize).into_iter().flatten().copied().next()
797 }
798
799 pub fn set_method(&mut self, index: u16, closure: GcRaw<Closure>) {
801 let index = index as usize;
802 if index >= self.methods.len() {
803 self.methods.resize(index + 1, None);
804 }
805 self.methods[index] = Some(closure);
806 }
807
808 pub(crate) fn methods(&self) -> impl Iterator<Item = GcRaw<Closure>> + '_ {
810 self.methods.iter().copied().flatten()
811 }
812}
813
814#[derive(Debug)]
817pub struct BuiltinDispatchTables {
818 pub nil: Gc<DispatchTable>,
819 pub boolean: Gc<DispatchTable>,
820 pub number: Gc<DispatchTable>,
821 pub string: Gc<DispatchTable>,
822 pub function: Gc<DispatchTable>,
823 pub list: Gc<DispatchTable>,
824 pub dict: Gc<DispatchTable>,
825}
826
827impl BuiltinDispatchTables {
829 pub fn empty() -> Self {
830 Self {
831 nil: Gc::new(DispatchTable::new("Nil", "Nil")),
832 boolean: Gc::new(DispatchTable::new("Boolean", "Boolean")),
833 number: Gc::new(DispatchTable::new("Number", "Boolean")),
834 string: Gc::new(DispatchTable::new("String", "String")),
835 function: Gc::new(DispatchTable::new("Function", "Function")),
836 list: Gc::new(DispatchTable::new("List", "List")),
837 dict: Gc::new(DispatchTable::new("Dict", "Dict")),
838 }
839 }
840}
841
842pub struct BuiltinTraits {
844 pub iterator: Opr24,
845 pub iterator_has_next: u16,
846 pub iterator_next: u16,
847}
848
849impl BuiltinTraits {
850 fn try_register_in(env: &mut Environment) -> Result<Self, ErrorKind> {
852 let mut builder = TraitBuilder::new(env, None, Rc::from("Iterator"))?;
853 let iterator_has_next = builder.add_method(Rc::from("has_next"), 0)?;
854 let iterator_next = builder.add_method(Rc::from("next"), 0)?;
855 let (iterator, _) = builder.build();
856
857 Ok(Self {
858 iterator,
859 iterator_has_next,
860 iterator_next,
861 })
862 }
863
864 pub fn register_in(env: &mut Environment) -> Self {
869 Self::try_register_in(env).expect("environment should have enough space for built-in traits")
870 }
871}
872
873#[derive(Debug, Default)]
876pub(crate) struct Prototype {
877 pub(crate) instance: HashMap<u16, Opr24>,
880
881 pub(crate) trait_instance: HashMap<(Rc<str>, u16, u16), Opr24>,
885
886 pub(crate) statics: HashMap<u16, Opr24>,
892
893 pub(crate) implemented_trait_count: u16,
895}
896
897#[derive(Debug)]
899pub struct TraitPrototype {
900 pub name: Rc<str>,
901 pub required: HashSet<u16>,
903 pub shims: Vec<(u16, Opr24)>,
905}