Skip to main content

gc/
vm.rs

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