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#[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#[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 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 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 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 pub fn set_capture_output(&mut self, capture: bool) {
269 self.output = capture.then(String::new);
270 }
271
272 pub fn take_output(&mut self) -> String {
274 self.output.as_mut().map(std::mem::take).unwrap_or_default()
275 }
276
277 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 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 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 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 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 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 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 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 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 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 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}