1use std::collections::{HashMap, HashSet};
2
3use byteorder::{BigEndian, ByteOrder};
4use compiler::compiler::{BindingDebugInfo, Bytecode, DebugInfo};
5use compiler::op_code::Opcode;
6use object::builtins::{BuiltIns, BuiltinId};
7use object::Object;
8use parser::lexer::token::Span;
9use serde::Serialize;
10
11use crate::debugger::{collect_hit, DebuggerHit, HitContext, MAX_DEBUGGER_HITS};
12use crate::frame::Frame;
13use crate::report::{
14 empty_value_kind_counts, select_global_roots, summarize_gc_object, GcCollectionReport,
15 GlobalRoot,
16};
17use crate::value::{
18 alloc_value, call_builtin_with_output, export_object, get_value, get_value_mut, import_object,
19 try_export_object, value_to_string, GcBoundMethod, GcClass, GcClosure, GcInstance, HashKey,
20 Value,
21};
22use crate::{GcHeap, GcId, GcRef};
23
24const STACK_SIZE: usize = 2048;
25pub const GLOBAL_SIZE: usize = 65536;
26const MAX_FRAMES: usize = 1024;
27pub const DEFAULT_INSTRUCTION_BUDGET: usize = 100_000;
28
29#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
31#[serde(rename_all = "camelCase")]
32pub struct GcRuntimeError {
33 pub message: String,
34 pub span: Option<Span>,
35}
36
37#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
39#[serde(rename_all = "camelCase")]
40pub struct GcClassifiedRuntimeError {
41 pub kind: GcRuntimeErrorKind,
42 pub message: String,
43 pub span: Option<Span>,
44}
45
46impl From<GcClassifiedRuntimeError> for GcRuntimeError {
47 fn from(error: GcClassifiedRuntimeError) -> Self {
48 Self {
49 message: error.message,
50 span: error.span,
51 }
52 }
53}
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
56#[serde(rename_all = "camelCase")]
57pub enum GcRuntimeErrorKind {
58 Arithmetic,
59 Call,
60 ExecutionLimit,
61 Index,
62 Property,
63 Stack,
64 Type,
65 InvalidBytecode,
66}
67
68impl GcRuntimeErrorKind {
69 pub fn as_str(self) -> &'static str {
70 match self {
71 Self::Arithmetic => "arithmetic",
72 Self::Call => "call",
73 Self::ExecutionLimit => "executionLimit",
74 Self::Index => "index",
75 Self::Property => "property",
76 Self::Stack => "stack",
77 Self::Type => "type",
78 Self::InvalidBytecode => "invalidBytecode",
79 }
80 }
81}
82
83enum CalleeKind {
84 Closure(GcClosure),
85 Builtin(BuiltinId),
86 BoundMethod(GcBoundMethod),
87 Class(String),
88 Other(String),
89}
90
91pub struct GcVM {
92 heap: GcHeap,
93 constants: Vec<GcRef>,
94 stack: Vec<GcRef>,
95 sp: usize,
96 globals: Vec<GcRef>,
97 global_bindings: Vec<BindingDebugInfo>,
98 globals_initialized: Vec<bool>,
102 frames: Vec<Frame>,
103 frame_index: usize,
104 null: GcRef,
105 last_popped: GcRef,
106 main_debug_info: DebugInfo,
107 function_debug_info: HashMap<GcRef, DebugInfo>,
108 debugger_hits: Vec<DebuggerHit>,
109 dropped_debugger_hits: usize,
112 output: Option<String>,
113}
114
115impl GcVM {
116 pub fn new(bytecode: Bytecode) -> Self {
117 let Bytecode {
118 instructions,
119 constants: object_constants,
120 debug_info: main_debug_info,
121 function_debug_info: object_function_debug_info,
122 } = bytecode;
123 let mut heap = GcHeap::new();
124 let null = alloc_value(&mut heap, Value::Null);
125 let constants = object_constants
126 .iter()
127 .map(|constant| import_object(&mut heap, constant))
128 .collect::<Vec<_>>();
129 let function_debug_info = object_function_debug_info
130 .into_iter()
131 .filter_map(|(index, debug_info)| {
132 constants
133 .get(index)
134 .copied()
135 .map(|reference| (reference, debug_info))
136 })
137 .collect();
138
139 let main_fn = alloc_value(
140 &mut heap,
141 Value::CompiledFunction(object::CompiledFunction {
142 name: String::new(),
143 instructions: instructions.data,
144 num_locals: 0,
145 num_parameters: 0,
146 }),
147 );
148 let main_instructions = compiled_instructions(&heap, main_fn);
149 let main_frame = Frame::new(
152 GcClosure {
153 func: main_fn,
154 free: vec![],
155 },
156 main_instructions,
157 0,
158 0,
159 0,
160 );
161
162 let empty_frame = Frame::new(
163 GcClosure {
164 func: main_fn,
165 free: vec![],
166 },
167 vec![],
168 0,
169 0,
170 0,
171 );
172
173 let mut frames = vec![empty_frame; MAX_FRAMES];
174 frames[0] = main_frame;
175
176 let stack = (0..STACK_SIZE).map(|_| heap.dup(null)).collect();
177 let globals = (0..GLOBAL_SIZE).map(|_| heap.dup(null)).collect();
178 let last_popped = heap.dup(null);
179
180 GcVM {
181 heap,
182 constants,
183 stack,
184 sp: 0,
185 globals,
186 global_bindings: Vec::new(),
187 globals_initialized: vec![false; GLOBAL_SIZE],
188 frames,
189 frame_index: 1,
190 null,
191 last_popped,
192 main_debug_info,
193 function_debug_info,
194 debugger_hits: Vec::new(),
195 dropped_debugger_hits: 0,
196 output: None,
197 }
198 }
199
200 pub fn load_bytecode(&mut self, bytecode: Bytecode) {
206 let Bytecode {
207 instructions,
208 constants: object_constants,
209 debug_info: main_debug_info,
210 function_debug_info: object_function_debug_info,
211 } = bytecode;
212
213 self.clear_stack_range(0, self.sp);
214 self.sp = 0;
215
216 self.heap.free(self.last_popped);
219 self.last_popped = self.heap.dup(self.null);
220
221 for reference in self.constants.drain(..) {
222 self.heap.free(reference);
223 }
224 self.function_debug_info.clear();
225
226 let old_main = self.frames[0].cl.func;
227 self.heap.free(old_main);
228
229 self.constants = object_constants
230 .iter()
231 .map(|constant| import_object(&mut self.heap, constant))
232 .collect::<Vec<_>>();
233 self.function_debug_info = object_function_debug_info
234 .into_iter()
235 .filter_map(|(index, debug_info)| {
236 self.constants
237 .get(index)
238 .copied()
239 .map(|reference| (reference, debug_info))
240 })
241 .collect();
242 self.main_debug_info = main_debug_info;
243
244 let main_fn = alloc_value(
245 &mut self.heap,
246 Value::CompiledFunction(object::CompiledFunction {
247 name: String::new(),
248 instructions: instructions.data,
249 num_locals: 0,
250 num_parameters: 0,
251 }),
252 );
253 let main_instructions = compiled_instructions(&self.heap, main_fn);
254 let main_frame = Frame::new(
255 GcClosure {
256 func: main_fn,
257 free: vec![],
258 },
259 main_instructions,
260 0,
261 0,
262 0,
263 );
264 let empty_frame = Frame::new(
265 GcClosure {
266 func: main_fn,
267 free: vec![],
268 },
269 vec![],
270 0,
271 0,
272 0,
273 );
274 self.frames = vec![empty_frame; MAX_FRAMES];
275 self.frames[0] = main_frame;
276 self.frame_index = 1;
277
278 self.debugger_hits.clear();
281 self.dropped_debugger_hits = 0;
282 }
283
284 pub fn heap(&self) -> &GcHeap {
285 &self.heap
286 }
287
288 pub fn heap_mut(&mut self) -> &mut GcHeap {
289 &mut self.heap
290 }
291
292 pub fn set_capture_output(&mut self, capture: bool) {
294 self.output = capture.then(String::new);
295 }
296
297 pub fn take_output(&mut self) -> String {
299 self.output.as_mut().map(std::mem::take).unwrap_or_default()
300 }
301
302 pub fn set_global_bindings(&mut self, bindings: Vec<BindingDebugInfo>) {
306 self.global_bindings = bindings;
307 }
308
309 pub fn globals_initialized(&self) -> &[bool] {
312 &self.globals_initialized
313 }
314
315 pub fn take_debugger_hits(&mut self) -> (Vec<DebuggerHit>, usize) {
318 let dropped = std::mem::take(&mut self.dropped_debugger_hits);
319 return (std::mem::take(&mut self.debugger_hits), dropped);
320 }
321
322 fn record_debugger_hit(&mut self) {
323 if self.debugger_hits.len() >= MAX_DEBUGGER_HITS {
324 self.dropped_debugger_hits += 1;
325 return;
326 }
327 let hit = collect_hit(HitContext {
328 heap: &self.heap,
329 frames: &self.frames[..self.frame_index],
330 stack: &self.stack,
331 sp: self.sp,
332 globals: &self.globals,
333 global_bindings: &self.global_bindings,
334 globals_initialized: &self.globals_initialized,
335 main_debug_info: &self.main_debug_info,
336 function_debug_info: &self.function_debug_info,
337 index: self.debugger_hits.len() + 1,
338 });
339 self.debugger_hits.push(hit);
340 }
341
342 pub fn collect_garbage(&mut self) -> GcCollectionReport {
343 let mut visible: HashMap<&str, usize> = HashMap::new();
349 for binding in &self.global_bindings {
350 visible.insert(binding.name.as_str(), binding.slot);
351 }
352 let mut named: Vec<(&str, usize)> = visible.into_iter().collect();
353 named.sort_unstable();
354 let global_roots = named
355 .into_iter()
356 .filter(|(_, slot)| *slot < self.globals.len())
357 .map(|(name, slot)| GlobalRoot {
358 name: name.to_string(),
359 object_id: self.globals[slot].0,
360 })
361 .collect();
362 let before_kinds = self.heap.value_kinds_by_id();
363 let before = self.heap.snapshot();
364 let diagnostics = self.heap.run_gc_with_stats_bundle();
365 let after = self.heap.snapshot();
366 let mut collected_by_value_kind = empty_value_kind_counts();
367 for (id, kind) in before_kinds {
368 if !self.heap.runtime().object_exists(id) {
369 *collected_by_value_kind.entry(kind).or_default() += 1;
370 }
371 }
372 let mut objects = diagnostics.objects;
373 let cataloged: HashSet<GcId> = objects.iter().map(|object| object.id).collect();
374 let (global_roots, omitted_global_roots) = select_global_roots(global_roots, &cataloged);
375 let mut uncataloged: Vec<GcId> = global_roots
380 .iter()
381 .map(|root| root.object_id)
382 .filter(|id| !cataloged.contains(id))
383 .collect();
384 uncataloged.sort_unstable();
385 uncataloged.dedup();
386 for id in uncataloged {
387 objects.push(summarize_gc_object(self.heap.runtime(), id));
388 }
389 objects.sort_unstable_by_key(|object| object.id);
390 GcCollectionReport {
391 before,
392 after,
393 objects,
394 global_roots,
395 omitted_global_roots,
396 phases: diagnostics.phases,
397 collected_by_value_kind,
398 }
399 }
400
401 fn runtime_error(
404 &self,
405 kind: GcRuntimeErrorKind,
406 message: impl Into<String>,
407 ) -> GcClassifiedRuntimeError {
408 let frame = &self.frames[self.frame_index - 1];
409 let debug_info = if self.frame_index == 1 {
410 Some(&self.main_debug_info)
411 } else {
412 self.function_debug_info.get(&frame.cl.func)
413 };
414 let span = debug_info.and_then(|debug_info| {
415 (frame.ip >= 0)
416 .then_some(frame.ip as usize)
417 .and_then(|pc| debug_info.span_for_pc(pc).cloned())
418 });
419 GcClassifiedRuntimeError {
420 kind,
421 message: message.into(),
422 span,
423 }
424 }
425
426 pub fn run(&mut self) {
427 self.run_with_budget(usize::MAX)
428 .expect("GC VM execution failed");
429 }
430
431 pub fn run_with_budget(&mut self, instruction_budget: usize) -> Result<(), GcRuntimeError> {
432 self.run_with_budget_classified(instruction_budget)
433 .map_err(Into::into)
434 }
435
436 pub fn run_with_budget_classified(
438 &mut self,
439 instruction_budget: usize,
440 ) -> Result<(), GcClassifiedRuntimeError> {
441 let mut executed = 0;
442 while self.current_frame().ip < self.current_frame().instructions.len() as i32 - 1 {
443 self.current_frame().ip += 1;
444 let ip = self.current_frame().ip as usize;
445 if executed >= instruction_budget {
446 return Err(self.runtime_error(
447 GcRuntimeErrorKind::ExecutionLimit,
448 format!("instruction limit exceeded (budget: {})", instruction_budget),
449 ));
450 }
451 executed += 1;
452 let ins = self.current_frame().instructions.clone();
453 let op = *ins.get(ip).unwrap();
454 let opcode = Opcode::from_repr(op).ok_or_else(|| {
455 self.runtime_error(
456 GcRuntimeErrorKind::InvalidBytecode,
457 format!("unknown opcode 0x{:02x}", op),
458 )
459 })?;
460
461 match opcode {
462 Opcode::OpConst => {
463 let const_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
464 self.current_frame().ip += 2;
465 let constant = self.constant(const_index)?;
466 self.dup_and_push(constant)?;
467 }
468 Opcode::OpAdd | Opcode::OpSub | Opcode::OpMul | Opcode::OpDiv => {
469 self.execute_binary_operation(opcode)?;
470 }
471 Opcode::OpPop => {
472 self.pop_discard()?;
473 }
474 Opcode::OpTrue => {
475 self.alloc_and_push(Value::Boolean(true))?;
476 }
477 Opcode::OpFalse => {
478 self.alloc_and_push(Value::Boolean(false))?;
479 }
480 Opcode::OpEqual
481 | Opcode::OpNotEqual
482 | Opcode::OpGreaterThan
483 | Opcode::OpLessThan => {
484 self.execute_comparison(opcode)?;
485 }
486 Opcode::OpMinus => {
487 self.execute_minus_operation()?;
488 }
489 Opcode::OpBang => {
490 self.execute_bang_operation()?;
491 }
492 Opcode::OpJump => {
493 let pos = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
494 self.current_frame().ip = pos as i32 - 1;
495 }
496 Opcode::OpJumpNotTruthy => {
497 let pos = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
498 self.current_frame().ip += 2;
499 let condition = self.pop_owned()?;
500 if !is_truthy(&self.heap, condition) {
501 self.current_frame().ip = pos as i32 - 1;
502 }
503 self.heap.free(condition);
504 }
505 Opcode::OpNull => {
506 self.dup_and_push(self.null)?;
507 }
508 Opcode::OpGetGlobal => {
509 let global_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
510 self.current_frame().ip += 2;
511 self.dup_and_push(self.globals[global_index])?;
512 }
513 Opcode::OpSetGlobal => {
514 let global_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
515 self.current_frame().ip += 2;
516 let value = self.pop_owned()?;
517 self.heap.free(self.globals[global_index]);
518 self.globals[global_index] = value;
519 self.globals_initialized[global_index] = true;
520 }
521 Opcode::OpArray => {
522 let count = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
523 self.current_frame().ip += 2;
524 let start = self.stack_base_for(count)?;
525 let elements = self.build_array(start, self.sp);
526 let array = alloc_value(&mut self.heap, Value::Array(elements));
527 self.clear_stack_range(start, self.sp);
528 self.sp = start;
529 self.push_raw(array)?;
530 }
531 Opcode::OpHash => {
532 let count = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
533 self.current_frame().ip += 2;
534 let start = self.stack_base_for(count)?;
535 let elements = self.build_hash(start, self.sp)?;
536 let hash = alloc_value(&mut self.heap, Value::Hash(elements));
537 self.clear_stack_range(start, self.sp);
538 self.sp = start;
539 self.push_raw(hash)?;
540 }
541 Opcode::OpIndex => {
542 let (index, left) = self.pop_owned_pair()?;
543 let result = self.execute_index_operation(left, index);
544 self.heap.free(index);
545 self.heap.free(left);
546 result?;
547 }
548 Opcode::OpReturnValue => {
549 let return_value = self.pop_owned()?;
550 if self.frame_index == 1 {
551 self.clear_stack_range(0, self.sp);
554 self.sp = 0;
555 self.heap.free(self.last_popped);
556 self.last_popped = return_value;
557 break;
558 }
559 let frame = self.pop_frame();
560 let new_sp = frame.base_pointer - 1;
561 self.clear_stack_range(new_sp, self.sp);
562 self.sp = new_sp;
563 self.push_raw(return_value)?;
564 }
565 Opcode::OpReturn => {
566 if self.frame_index == 1 {
567 self.clear_stack_range(0, self.sp);
568 self.sp = 0;
569 self.heap.free(self.last_popped);
570 self.last_popped = self.heap.dup(self.null);
571 break;
572 }
573 let frame = self.pop_frame();
574 let new_sp = frame.base_pointer - 1;
575 self.clear_stack_range(new_sp, self.sp);
576 self.sp = new_sp;
577 self.dup_and_push(self.null)?;
578 }
579 Opcode::OpCall => {
580 let num_args = ins[ip + 1] as usize;
581 self.current_frame().ip += 1;
582 self.execute_call(num_args)?;
583 }
584 Opcode::OpSetLocal => {
585 let local_index = ins[ip + 1] as usize;
586 self.current_frame().ip += 1;
587 let base = self.current_frame().base_pointer;
588 let slot = self.local_slot(base, local_index)?;
589 let value = self.pop_owned()?;
590 self.heap.free(self.stack[slot]);
591 self.stack[slot] = value;
592 if let Some(flag) = self.current_frame().initialized.get_mut(local_index) {
595 *flag = true;
596 }
597 }
598 Opcode::OpGetLocal => {
599 let local_index = ins[ip + 1] as usize;
600 self.current_frame().ip += 1;
601 let base = self.current_frame().base_pointer;
602 let slot = self.local_slot(base, local_index)?;
603 self.dup_and_push(self.stack[slot])?;
604 }
605 Opcode::OpGetBuiltin => {
606 let built_index = ins[ip + 1] as usize;
607 self.current_frame().ip += 1;
608 let definition = BuiltIns.get(built_index).ok_or_else(|| {
609 self.runtime_error(
610 GcRuntimeErrorKind::InvalidBytecode,
611 format!("builtin index {} out of range", built_index),
612 )
613 })?;
614 self.alloc_and_push(Value::Builtin(definition.id))?;
615 }
616 Opcode::OpClosure => {
617 let const_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
618 let num_free = ins[ip + 3] as usize;
619 self.current_frame().ip += 3;
620 self.push_closure(const_index, num_free)?;
621 }
622 Opcode::OpGetFree => {
623 let free_index = ins[ip + 1] as usize;
624 self.current_frame().ip += 1;
625 let free_var = self.current_frame().cl.free.get(free_index).copied();
626 let free_var = free_var.ok_or_else(|| {
627 self.runtime_error(
628 GcRuntimeErrorKind::InvalidBytecode,
629 format!("free variable index {} out of range", free_index),
630 )
631 })?;
632 self.dup_and_push(free_var)?;
633 }
634 Opcode::OpCurrentClosure => {
635 let current = self.current_frame().cl.clone();
636 self.alloc_and_push(Value::Closure(current))?;
637 }
638 Opcode::OpClass => {
639 let name_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
640 self.current_frame().ip += 2;
641 let name = self.constant_string(name_index)?;
642 self.alloc_and_push(Value::Class(GcClass {
643 name,
644 constructor: None,
645 methods: HashMap::new(),
646 }))?;
647 }
648 Opcode::OpMethod => {
649 let name_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
650 let kind = ins[ip + 3];
651 self.current_frame().ip += 3;
652 let name = self.constant_string(name_index)?;
653 let method = self.pop_owned()?;
654 if self.sp == 0 {
655 self.heap.free(method);
656 return Err(
657 self.runtime_error(GcRuntimeErrorKind::Stack, "stack underflow")
658 );
659 }
660 let class = self.stack[self.sp - 1];
661 let result = self.install_method(class, name, method, kind == 1);
662 self.heap.free(method);
663 result?;
664 }
665 Opcode::OpGetProperty => {
666 let name_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
667 self.current_frame().ip += 2;
668 let name = self.constant_string(name_index)?;
669 let receiver = self.pop_owned()?;
670 let value = self.get_property(receiver, &name);
671 self.heap.free(receiver);
672 self.push_raw(value?)?;
673 }
674 Opcode::OpSetProperty => {
675 let name_index = BigEndian::read_u16(&ins[ip + 1..ip + 3]) as usize;
676 self.current_frame().ip += 2;
677 let name = self.constant_string(name_index)?;
678 let (value, receiver) = self.pop_owned_pair()?;
679 let result = self.set_property(receiver, name, value);
680 self.heap.free(value);
681 self.heap.free(receiver);
682 result?;
683 }
684 Opcode::OpNew => {
685 let num_args = ins[ip + 1] as usize;
686 self.current_frame().ip += 1;
687 self.execute_new(num_args)?;
688 }
689 Opcode::OpDebugger => {
690 self.record_debugger_hit();
692 }
693 }
694 }
695 Ok(())
696 }
697
698 pub fn last_popped_stack_elm(&self) -> Option<GcRef> {
699 Some(self.last_popped)
700 }
701
702 pub fn export_last_result(&self) -> Option<Object> {
703 self.last_popped_stack_elm()
704 .map(|reference| export_object(&self.heap, reference))
705 }
706
707 pub fn try_export_last_result(&self) -> Result<Object, String> {
708 try_export_object(&self.heap, self.last_popped)
709 }
710
711 pub fn last_result_string(&self) -> String {
712 value_to_string(&self.heap, self.last_popped)
713 }
714
715 fn alloc_and_push(&mut self, value: Value) -> Result<(), GcClassifiedRuntimeError> {
716 let reference = alloc_value(&mut self.heap, value);
717 self.push_raw(reference)
718 }
719
720 fn dup_and_push(&mut self, reference: GcRef) -> Result<(), GcClassifiedRuntimeError> {
721 let duplicated = self.heap.dup(reference);
722 self.push_raw(duplicated)
723 }
724
725 fn push_raw(&mut self, value: GcRef) -> Result<(), GcClassifiedRuntimeError> {
726 if self.sp >= STACK_SIZE {
727 let error = self.runtime_error(GcRuntimeErrorKind::Stack, "stack limit exceeded");
728 self.heap.free(value);
729 return Err(error);
730 }
731 let old = self.stack[self.sp];
732 self.stack[self.sp] = value;
733 self.heap.free(old);
734 self.sp += 1;
735 Ok(())
736 }
737
738 fn pop_owned(&mut self) -> Result<GcRef, GcClassifiedRuntimeError> {
743 if self.sp == 0 {
744 return Err(self.runtime_error(GcRuntimeErrorKind::Stack, "stack underflow"));
745 }
746 self.sp -= 1;
747 let value = self.stack[self.sp];
748 self.stack[self.sp] = self.heap.dup(self.null);
749 Ok(value)
750 }
751
752 fn pop_discard(&mut self) -> Result<(), GcClassifiedRuntimeError> {
753 let value = self.pop_owned()?;
754 self.heap.free(self.last_popped);
755 self.last_popped = value;
756 Ok(())
757 }
758
759 fn pop_owned_pair(&mut self) -> Result<(GcRef, GcRef), GcClassifiedRuntimeError> {
762 let top = self.pop_owned()?;
763 match self.pop_owned() {
764 Ok(below) => Ok((top, below)),
765 Err(error) => {
766 self.heap.free(top);
767 Err(error)
768 }
769 }
770 }
771
772 fn stack_base_for(&self, count: usize) -> Result<usize, GcClassifiedRuntimeError> {
775 self.sp
776 .checked_sub(count)
777 .ok_or_else(|| self.runtime_error(GcRuntimeErrorKind::Stack, "stack underflow"))
778 }
779
780 fn local_slot(
781 &self,
782 base: usize,
783 local_index: usize,
784 ) -> Result<usize, GcClassifiedRuntimeError> {
785 let slot = base + local_index;
786 if slot >= STACK_SIZE {
787 return Err(self.runtime_error(
788 GcRuntimeErrorKind::InvalidBytecode,
789 format!("local index {} out of range", local_index),
790 ));
791 }
792 Ok(slot)
793 }
794
795 fn clear_stack_range(&mut self, start: usize, end: usize) {
796 for index in start..end {
797 let old = self.stack[index];
798 self.stack[index] = self.heap.dup(self.null);
799 self.heap.free(old);
800 }
801 }
802
803 fn execute_binary_operation(&mut self, opcode: Opcode) -> Result<(), GcClassifiedRuntimeError> {
804 let (right, left) = self.pop_owned_pair()?;
805 let left_value = get_value(&self.heap, left).clone();
806 let right_value = get_value(&self.heap, right).clone();
807 let result = match (&left_value, &right_value) {
808 (Value::Integer(l), Value::Integer(r)) => match opcode {
809 Opcode::OpAdd => Ok(Value::Integer(l.wrapping_add(*r))),
810 Opcode::OpSub => Ok(Value::Integer(l.wrapping_sub(*r))),
811 Opcode::OpMul => Ok(Value::Integer(l.wrapping_mul(*r))),
812 Opcode::OpDiv if *r != 0 => {
813 l.checked_div(*r).map(Value::Integer).ok_or_else(|| {
814 (GcRuntimeErrorKind::Arithmetic, "integer overflow in division".to_string())
815 })
816 }
817 Opcode::OpDiv => {
818 Err((GcRuntimeErrorKind::Arithmetic, "division by zero".to_string()))
819 }
820 _ => unreachable!(),
821 },
822 (Value::String(l), Value::String(r)) if opcode == Opcode::OpAdd => {
823 Ok(Value::String(l.to_string() + r))
824 }
825 _ => Err((
826 GcRuntimeErrorKind::Type,
827 format!(
828 "unsupported binary operation for {} and {}",
829 value_to_string(&self.heap, left),
830 value_to_string(&self.heap, right)
831 ),
832 )),
833 };
834 self.heap.free(left);
835 self.heap.free(right);
836 match result {
837 Ok(value) => self.alloc_and_push(value),
838 Err((kind, message)) => Err(self.runtime_error(kind, message)),
839 }
840 }
841
842 fn execute_comparison(&mut self, opcode: Opcode) -> Result<(), GcClassifiedRuntimeError> {
843 let (right, left) = self.pop_owned_pair()?;
844 let result = match (get_value(&self.heap, left), get_value(&self.heap, right)) {
845 (Value::Integer(l), Value::Integer(r)) => match opcode {
846 Opcode::OpEqual => Some(l == r),
847 Opcode::OpNotEqual => Some(l != r),
848 Opcode::OpGreaterThan => Some(l > r),
849 Opcode::OpLessThan => Some(l < r),
850 _ => unreachable!(),
851 },
852 (Value::Boolean(l), Value::Boolean(r)) => match opcode {
853 Opcode::OpEqual => Some(l == r),
854 Opcode::OpNotEqual => Some(l != r),
855 _ => None,
856 },
857 (Value::String(l), Value::String(r)) => match opcode {
858 Opcode::OpEqual => Some(l == r),
859 Opcode::OpNotEqual => Some(l != r),
860 _ => None,
861 },
862 (Value::Null, Value::Null) => match opcode {
863 Opcode::OpEqual => Some(true),
864 Opcode::OpNotEqual => Some(false),
865 _ => None,
866 },
867 (Value::Class(_), Value::Class(_))
868 | (Value::Instance(_), Value::Instance(_))
869 | (Value::BoundMethod(_), Value::BoundMethod(_)) => match opcode {
870 Opcode::OpEqual => Some(left == right),
871 Opcode::OpNotEqual => Some(left != right),
872 _ => None,
873 },
874 _ => None,
875 };
876 let message = if result.is_none() {
877 Some(format!(
878 "unsupported comparison for {} and {}",
879 value_to_string(&self.heap, left),
880 value_to_string(&self.heap, right)
881 ))
882 } else {
883 None
884 };
885 self.heap.free(left);
886 self.heap.free(right);
887 if let Some(result) = result {
888 self.alloc_and_push(Value::Boolean(result))
889 } else {
890 Err(self.runtime_error(GcRuntimeErrorKind::Type, message.unwrap()))
891 }
892 }
893
894 fn execute_minus_operation(&mut self) -> Result<(), GcClassifiedRuntimeError> {
895 let operand = self.pop_owned()?;
896 let negated = match get_value(&self.heap, operand) {
897 Value::Integer(value) => Some(value.wrapping_neg()),
898 _ => None,
899 };
900 let message = negated.is_none().then(|| {
901 format!("unsupported type for negation: {}", value_to_string(&self.heap, operand))
902 });
903 self.heap.free(operand);
904 if let Some(negated) = negated {
905 self.alloc_and_push(Value::Integer(negated))
906 } else {
907 Err(self.runtime_error(GcRuntimeErrorKind::Type, message.unwrap()))
908 }
909 }
910
911 fn execute_bang_operation(&mut self) -> Result<(), GcClassifiedRuntimeError> {
912 let operand = self.pop_owned()?;
913 let result = match get_value(&self.heap, operand) {
914 Value::Boolean(l) => !l,
915 _ => false,
916 };
917 self.heap.free(operand);
918 self.alloc_and_push(Value::Boolean(result))
919 }
920
921 fn build_array(&mut self, start: usize, end: usize) -> Vec<GcRef> {
922 let mut elements = Vec::with_capacity(end - start);
923 for i in start..end {
924 elements.push(self.stack[i]);
925 }
926 elements
927 }
928
929 fn build_hash(
930 &mut self,
931 start: usize,
932 end: usize,
933 ) -> Result<HashMap<HashKey, GcRef>, GcClassifiedRuntimeError> {
934 let mut elements = HashMap::new();
935 for i in (start..end).step_by(2) {
936 let key_ref = self.stack[i];
937 let key = HashKey::from_value(get_value(&self.heap, key_ref)).ok_or_else(|| {
938 self.runtime_error(
939 GcRuntimeErrorKind::Index,
940 format!(
941 "hash key must be hashable, got {}",
942 value_to_string(&self.heap, key_ref)
943 ),
944 )
945 })?;
946 elements.insert(key, self.stack[i + 1]);
947 }
948 Ok(elements)
949 }
950
951 fn execute_index_operation(
952 &mut self,
953 left: GcRef,
954 index: GcRef,
955 ) -> Result<(), GcClassifiedRuntimeError> {
956 let left_value = get_value(&self.heap, left).clone();
957 let index_value = get_value(&self.heap, index).clone();
958 match (&left_value, &index_value) {
959 (Value::Array(array), Value::Integer(i)) => self.execute_array_index(array, *i),
960 (Value::Hash(hash), _) => self.execute_hash_index(hash, &index_value),
961 _ => Err(self.runtime_error(
962 GcRuntimeErrorKind::Index,
963 format!(
964 "unsupported index operation for {} and {}",
965 value_to_string(&self.heap, left),
966 value_to_string(&self.heap, index)
967 ),
968 )),
969 }
970 }
971
972 fn execute_array_index(
973 &mut self,
974 array: &[GcRef],
975 index: i64,
976 ) -> Result<(), GcClassifiedRuntimeError> {
977 if index < array.len() as i64 && index >= 0 {
978 self.dup_and_push(array[index as usize])
979 } else {
980 self.dup_and_push(self.null)
981 }
982 }
983
984 fn execute_hash_index(
985 &mut self,
986 hash: &HashMap<HashKey, GcRef>,
987 index: &Value,
988 ) -> Result<(), GcClassifiedRuntimeError> {
989 let key = HashKey::from_value(index).ok_or_else(|| {
990 self.runtime_error(GcRuntimeErrorKind::Index, "unsupported hash index key")
991 })?;
992 match hash.get(&key) {
993 Some(value) => self.dup_and_push(*value),
994 None => self.dup_and_push(self.null),
995 }
996 }
997
998 fn current_frame(&mut self) -> &mut Frame {
999 &mut self.frames[self.frame_index - 1]
1000 }
1001
1002 fn push_frame(&mut self, frame: Frame) -> Result<(), GcClassifiedRuntimeError> {
1003 if self.frame_index >= MAX_FRAMES {
1004 return Err(self.runtime_error(GcRuntimeErrorKind::Stack, "frame limit exceeded"));
1005 }
1006 self.frames[self.frame_index] = frame;
1007 self.frame_index += 1;
1008 Ok(())
1009 }
1010
1011 fn pop_frame(&mut self) -> Frame {
1012 self.frame_index -= 1;
1013 self.frames[self.frame_index].clone()
1014 }
1015
1016 fn execute_call(&mut self, num_args: usize) -> Result<(), GcClassifiedRuntimeError> {
1017 let callee_slot = self.stack_base_for(num_args + 1)?;
1018 let callee = self.stack[callee_slot];
1019 match callee_kind(&self.heap, callee) {
1020 CalleeKind::Closure(closure) => self.call_closure(closure, num_args),
1021 CalleeKind::Builtin(builtin) => self.call_builtin(builtin, num_args),
1022 CalleeKind::BoundMethod(bound) => self.call_bound_method(bound, num_args),
1023 CalleeKind::Class(name) => Err(self.runtime_error(
1024 GcRuntimeErrorKind::Call,
1025 format!("class {} must be constructed with new", name),
1026 )),
1027 CalleeKind::Other(value) => {
1028 Err(self.runtime_error(GcRuntimeErrorKind::Call, format!("cannot call {}", value)))
1029 }
1030 }
1031 }
1032
1033 fn call_closure(
1034 &mut self,
1035 closure: GcClosure,
1036 num_args: usize,
1037 ) -> Result<(), GcClassifiedRuntimeError> {
1038 let compiled = match get_value(&self.heap, closure.func) {
1039 Value::CompiledFunction(f) => f.clone(),
1040 _ => {
1041 return Err(self.runtime_error(
1042 GcRuntimeErrorKind::InvalidBytecode,
1043 "closure without compiled function",
1044 ))
1045 }
1046 };
1047 if compiled.num_parameters != num_args {
1048 return Err(self.runtime_error(
1049 GcRuntimeErrorKind::Call,
1050 format!(
1051 "wrong number of arguments: want={}, got={}",
1052 compiled.num_parameters, num_args
1053 ),
1054 ));
1055 }
1056
1057 let base_pointer = self.sp - num_args;
1061 let next_sp = base_pointer
1062 .checked_add(compiled.num_locals)
1063 .filter(|next_sp| *next_sp <= STACK_SIZE)
1064 .ok_or_else(|| self.runtime_error(GcRuntimeErrorKind::Stack, "stack limit exceeded"))?;
1065 let frame = Frame::new(
1066 closure,
1067 compiled.instructions,
1068 base_pointer,
1069 compiled.num_locals,
1070 compiled.num_parameters,
1071 );
1072 self.sp = next_sp;
1073 self.push_frame(frame)
1074 }
1075
1076 fn call_builtin(
1077 &mut self,
1078 builtin: BuiltinId,
1079 num_args: usize,
1080 ) -> Result<(), GcClassifiedRuntimeError> {
1081 let base = self.sp - num_args - 1;
1082 let args = self.stack[self.sp - num_args..self.sp].to_vec();
1083 let result = call_builtin_with_output(
1084 &mut self.heap,
1085 builtin,
1086 &args,
1087 self.null,
1088 self.output.as_mut(),
1089 );
1090 self.clear_stack_range(base, self.sp);
1091 self.sp = base;
1092 self.push_raw(result)
1093 }
1094
1095 fn push_closure(
1096 &mut self,
1097 const_index: usize,
1098 num_free: usize,
1099 ) -> Result<(), GcClassifiedRuntimeError> {
1100 let func = self.constant(const_index)?;
1101 if !matches!(get_value(&self.heap, func), Value::CompiledFunction(_)) {
1102 return Err(self.runtime_error(
1103 GcRuntimeErrorKind::InvalidBytecode,
1104 format!("cannot build closure over {}", value_to_string(&self.heap, func)),
1105 ));
1106 }
1107 let start = self.stack_base_for(num_free)?;
1108 let mut free = Vec::with_capacity(num_free);
1109 for i in 0..num_free {
1110 free.push(self.stack[start + i]);
1111 }
1112 let closure = alloc_value(
1113 &mut self.heap,
1114 Value::Closure(GcClosure {
1115 func,
1116 free,
1117 }),
1118 );
1119 self.clear_stack_range(start, self.sp);
1120 self.sp = start;
1121 self.push_raw(closure)
1122 }
1123
1124 fn constant(&self, index: usize) -> Result<GcRef, GcClassifiedRuntimeError> {
1125 self.constants.get(index).copied().ok_or_else(|| {
1126 self.runtime_error(
1127 GcRuntimeErrorKind::InvalidBytecode,
1128 format!("constant index {} out of range", index),
1129 )
1130 })
1131 }
1132
1133 fn constant_string(&self, index: usize) -> Result<String, GcClassifiedRuntimeError> {
1134 let constant = self.constant(index)?;
1135 match get_value(&self.heap, constant) {
1136 Value::String(value) => Ok(value.clone()),
1137 value => Err(self.runtime_error(
1138 GcRuntimeErrorKind::InvalidBytecode,
1139 format!("expected string constant, got {}", value),
1140 )),
1141 }
1142 }
1143
1144 fn install_method(
1145 &mut self,
1146 class: GcRef,
1147 name: String,
1148 method: GcRef,
1149 constructor: bool,
1150 ) -> Result<(), GcClassifiedRuntimeError> {
1151 if !matches!(get_value(&self.heap, class), Value::Class(_)) {
1152 return Err(self.runtime_error(
1153 GcRuntimeErrorKind::InvalidBytecode,
1154 format!("cannot install method on {}", value_to_string(&self.heap, class)),
1155 ));
1156 }
1157 let owned_method = self.heap.dup(method);
1158 let old_method = match get_value_mut(&mut self.heap, class) {
1159 Value::Class(class) => {
1160 if constructor {
1161 class.constructor.replace(owned_method)
1162 } else {
1163 class.methods.insert(name, owned_method)
1164 }
1165 }
1166 _ => unreachable!(),
1167 };
1168 if let Some(old_method) = old_method {
1169 self.heap.free(old_method);
1170 }
1171 Ok(())
1172 }
1173
1174 fn get_property(
1175 &mut self,
1176 receiver: GcRef,
1177 name: &str,
1178 ) -> Result<GcRef, GcClassifiedRuntimeError> {
1179 let (class, field) = match get_value(&self.heap, receiver) {
1180 Value::Instance(instance) => (instance.class, instance.fields.get(name).copied()),
1181 _ => {
1182 return Err(self.runtime_error(
1183 GcRuntimeErrorKind::Property,
1184 format!(
1185 "cannot read property '{}' of {}",
1186 name,
1187 value_to_string(&self.heap, receiver)
1188 ),
1189 ))
1190 }
1191 };
1192 if let Some(field) = field {
1193 return Ok(self.heap.dup(field));
1194 }
1195
1196 let (class_name, method) = match get_value(&self.heap, class) {
1197 Value::Class(class) => (class.name.clone(), class.methods.get(name).copied()),
1198 _ => {
1199 return Err(self.runtime_error(
1200 GcRuntimeErrorKind::InvalidBytecode,
1201 "instance has invalid class",
1202 ))
1203 }
1204 };
1205 match method {
1206 Some(method) => Ok(alloc_value(
1207 &mut self.heap,
1208 Value::BoundMethod(GcBoundMethod {
1209 receiver,
1210 method,
1211 name: name.to_string(),
1212 }),
1213 )),
1214 None => Err(self.runtime_error(
1215 GcRuntimeErrorKind::Property,
1216 format!("property '{}' does not exist on {}", name, class_name),
1217 )),
1218 }
1219 }
1220
1221 fn set_property(
1222 &mut self,
1223 receiver: GcRef,
1224 name: String,
1225 value: GcRef,
1226 ) -> Result<(), GcClassifiedRuntimeError> {
1227 if !matches!(get_value(&self.heap, receiver), Value::Instance(_)) {
1228 return Err(self.runtime_error(
1229 GcRuntimeErrorKind::Property,
1230 format!(
1231 "cannot set property '{}' of {}",
1232 name,
1233 value_to_string(&self.heap, receiver)
1234 ),
1235 ));
1236 }
1237 let owned_value = self.heap.dup(value);
1238 let old_value = match get_value_mut(&mut self.heap, receiver) {
1239 Value::Instance(instance) => instance.fields.insert(name, owned_value),
1240 _ => unreachable!(),
1241 };
1242 if let Some(old_value) = old_value {
1243 self.heap.free(old_value);
1244 }
1245 Ok(())
1246 }
1247
1248 fn execute_new(&mut self, num_args: usize) -> Result<(), GcClassifiedRuntimeError> {
1249 let base = self.stack_base_for(num_args + 1)?;
1250 let class_reference = self.stack[base];
1251 let (class_name, constructor) = match get_value(&self.heap, class_reference) {
1252 Value::Class(class) => (class.name.clone(), class.constructor),
1253 _ => {
1254 return Err(self.runtime_error(
1255 GcRuntimeErrorKind::Call,
1256 format!("cannot construct {}", value_to_string(&self.heap, class_reference)),
1257 ))
1258 }
1259 };
1260
1261 let Some(constructor) = constructor else {
1262 if num_args != 0 {
1263 return Err(self.runtime_error(
1264 GcRuntimeErrorKind::Call,
1265 format!(
1266 "wrong number of arguments for {}.constructor: want=0, got={}",
1267 class_name, num_args
1268 ),
1269 ));
1270 }
1271 let instance = alloc_value(
1272 &mut self.heap,
1273 Value::Instance(GcInstance {
1274 class: class_reference,
1275 fields: HashMap::new(),
1276 }),
1277 );
1278 self.clear_stack_range(base, self.sp);
1279 self.sp = base;
1280 return self.push_raw(instance);
1281 };
1282
1283 let closure = match get_value(&self.heap, constructor) {
1284 Value::Closure(closure) => closure.clone(),
1285 _ => {
1286 return Err(self.runtime_error(
1287 GcRuntimeErrorKind::InvalidBytecode,
1288 "constructor is not a closure",
1289 ))
1290 }
1291 };
1292 let compiled = match get_value(&self.heap, closure.func) {
1293 Value::CompiledFunction(function) => function.clone(),
1294 _ => {
1295 return Err(self.runtime_error(
1296 GcRuntimeErrorKind::InvalidBytecode,
1297 "constructor closure has invalid function",
1298 ))
1299 }
1300 };
1301 let expected = compiled.num_parameters.saturating_sub(1);
1302 if expected != num_args {
1303 return Err(self.runtime_error(
1304 GcRuntimeErrorKind::Call,
1305 format!(
1306 "wrong number of arguments for {}.constructor: want={}, got={}",
1307 class_name, expected, num_args
1308 ),
1309 ));
1310 }
1311
1312 let instance = alloc_value(
1313 &mut self.heap,
1314 Value::Instance(GcInstance {
1315 class: class_reference,
1316 fields: HashMap::new(),
1317 }),
1318 );
1319 self.rewrite_receiver_call(constructor, instance, num_args)?;
1320 self.call_closure(closure, num_args + 1)
1321 }
1322
1323 fn call_bound_method(
1324 &mut self,
1325 bound: GcBoundMethod,
1326 num_args: usize,
1327 ) -> Result<(), GcClassifiedRuntimeError> {
1328 let closure = match get_value(&self.heap, bound.method) {
1329 Value::Closure(closure) => closure.clone(),
1330 _ => {
1331 return Err(self.runtime_error(
1332 GcRuntimeErrorKind::InvalidBytecode,
1333 "bound method is not a closure",
1334 ))
1335 }
1336 };
1337 let compiled = match get_value(&self.heap, closure.func) {
1338 Value::CompiledFunction(function) => function.clone(),
1339 _ => {
1340 return Err(self.runtime_error(
1341 GcRuntimeErrorKind::InvalidBytecode,
1342 "method closure has invalid function",
1343 ))
1344 }
1345 };
1346 let expected = compiled.num_parameters.saturating_sub(1);
1347 if expected != num_args {
1348 let class_name = match get_value(&self.heap, bound.receiver) {
1349 Value::Instance(instance) => match get_value(&self.heap, instance.class) {
1350 Value::Class(class) => class.name.clone(),
1351 _ => "<invalid class>".to_string(),
1352 },
1353 _ => "<invalid receiver>".to_string(),
1354 };
1355 return Err(self.runtime_error(
1356 GcRuntimeErrorKind::Call,
1357 format!(
1358 "wrong number of arguments for {}.{}: want={}, got={}",
1359 class_name, bound.name, expected, num_args
1360 ),
1361 ));
1362 }
1363 let receiver = self.heap.dup(bound.receiver);
1364 self.rewrite_receiver_call(bound.method, receiver, num_args)?;
1365 self.call_closure(closure, num_args + 1)
1366 }
1367
1368 fn rewrite_receiver_call(
1371 &mut self,
1372 callable: GcRef,
1373 receiver: GcRef,
1374 num_args: usize,
1375 ) -> Result<(), GcClassifiedRuntimeError> {
1376 let base = self.sp - num_args - 1;
1377 if base + num_args + 2 > STACK_SIZE {
1378 let error = self.runtime_error(GcRuntimeErrorKind::Stack, "stack limit exceeded");
1379 self.heap.free(receiver);
1380 return Err(error);
1381 }
1382 let callable = self.heap.dup(callable);
1383 let borrowed_arguments = self.stack[self.sp - num_args..self.sp].to_vec();
1384 let arguments = borrowed_arguments
1385 .into_iter()
1386 .map(|argument| self.heap.dup(argument))
1387 .collect::<Vec<_>>();
1388 self.clear_stack_range(base, self.sp);
1389 self.sp = base;
1390 self.push_raw(callable)?;
1391 self.push_raw(receiver)?;
1392 for argument in arguments {
1393 self.push_raw(argument)?;
1394 }
1395 Ok(())
1396 }
1397}
1398
1399fn is_truthy(heap: &GcHeap, condition: GcRef) -> bool {
1400 match get_value(heap, condition) {
1401 Value::Boolean(b) => *b,
1402 Value::Null => false,
1403 _ => true,
1404 }
1405}
1406
1407fn callee_kind(heap: &GcHeap, reference: GcRef) -> CalleeKind {
1408 match get_value(heap, reference) {
1409 Value::Closure(closure) => CalleeKind::Closure(closure.clone()),
1410 Value::Builtin(builtin) => CalleeKind::Builtin(*builtin),
1411 Value::BoundMethod(bound) => CalleeKind::BoundMethod(bound.clone()),
1412 Value::Class(class) => CalleeKind::Class(class.name.clone()),
1413 _ => CalleeKind::Other(value_to_string(heap, reference)),
1414 }
1415}
1416
1417fn compiled_instructions(heap: &GcHeap, func: GcRef) -> Vec<u8> {
1418 match get_value(heap, func) {
1419 Value::CompiledFunction(f) => f.instructions.clone(),
1420 _ => panic!("expected compiled function"),
1421 }
1422}