1use std::collections::HashMap;
11
12use object::builtins::{BuiltIns, BuiltinId};
13
14use crate::runtime_backend::{CodeHandle, ValueStore};
15
16pub type Value = u64;
18
19pub const FALSE_VALUE: Value = 0b0011;
20pub const TRUE_VALUE: Value = 0b0111;
21pub const NULL_VALUE: Value = 0b1011;
22
23pub const PTR_TAG_MASK: u64 = 0b111;
24pub const HEAP_TAG: u64 = 0b001;
25pub const BUILTIN_TAG: u64 = 0b101;
26
27pub const SMI_MIN: i64 = -(1 << 62);
29pub const SMI_MAX: i64 = (1 << 62) - 1;
30
31pub const MAX_INVOKE_ARGS: usize = 7;
34
35pub fn is_smi(value: Value) -> bool {
36 value & 1 == 0
37}
38
39pub fn smi_to_i64(value: Value) -> i64 {
40 (value as i64) >> 1
41}
42
43pub fn i64_fits_smi(raw: i64) -> bool {
44 (SMI_MIN..=SMI_MAX).contains(&raw)
45}
46
47pub fn smi_from_i64(raw: i64) -> Value {
48 debug_assert!(i64_fits_smi(raw));
49 (raw << 1) as u64
50}
51
52pub fn is_heap(value: Value) -> bool {
53 value & PTR_TAG_MASK == HEAP_TAG
54}
55
56pub fn is_builtin(value: Value) -> bool {
57 value & PTR_TAG_MASK == BUILTIN_TAG
58}
59
60pub fn bool_value(value: bool) -> Value {
61 if value {
62 TRUE_VALUE
63 } else {
64 FALSE_VALUE
65 }
66}
67
68pub fn builtin_ordinal(id: BuiltinId) -> u64 {
72 match id {
73 BuiltinId::Len => 0,
74 BuiltinId::Puts => 1,
75 BuiltinId::First => 2,
76 BuiltinId::Last => 3,
77 BuiltinId::Rest => 4,
78 BuiltinId::Push => 5,
79 }
80}
81
82pub fn builtin_from_ordinal(ordinal: u64) -> Option<BuiltinId> {
83 match ordinal {
84 0 => Some(BuiltinId::Len),
85 1 => Some(BuiltinId::Puts),
86 2 => Some(BuiltinId::First),
87 3 => Some(BuiltinId::Last),
88 4 => Some(BuiltinId::Rest),
89 5 => Some(BuiltinId::Push),
90 _ => None,
91 }
92}
93
94pub fn builtin_value(id: BuiltinId) -> Value {
95 (builtin_ordinal(id) << 3) | BUILTIN_TAG
96}
97
98pub fn builtin_canonical_name(id: BuiltinId) -> &'static str {
101 match id {
102 BuiltinId::Len => "len",
103 BuiltinId::Puts => "puts",
104 BuiltinId::First => "first",
105 BuiltinId::Last => "last",
106 BuiltinId::Rest => "rest",
107 BuiltinId::Push => "push",
108 }
109}
110
111pub fn builtin_id_for_symbol_index(index: usize) -> Option<BuiltinId> {
114 BuiltIns.get(index).map(|definition| definition.id)
115}
116
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
120#[repr(u64)]
121pub enum RuntimeErrorKind {
122 InternalError = 0,
123 TypeError = 1,
124 ArityError = 2,
125 NotCallable = 3,
126 NotConstructable = 4,
127 MissingProperty = 5,
128 InvalidHashKey = 6,
129 DivisionByZero = 7,
130 IntegerOverflow = 8,
131 ResourceLimit = 9,
132}
133
134impl RuntimeErrorKind {
135 pub fn name(self) -> &'static str {
136 match self {
137 RuntimeErrorKind::InternalError => "InternalError",
138 RuntimeErrorKind::TypeError => "TypeError",
139 RuntimeErrorKind::ArityError => "ArityError",
140 RuntimeErrorKind::NotCallable => "NotCallable",
141 RuntimeErrorKind::NotConstructable => "NotConstructable",
142 RuntimeErrorKind::MissingProperty => "MissingProperty",
143 RuntimeErrorKind::InvalidHashKey => "InvalidHashKey",
144 RuntimeErrorKind::DivisionByZero => "DivisionByZero",
145 RuntimeErrorKind::IntegerOverflow => "IntegerOverflow",
146 RuntimeErrorKind::ResourceLimit => "ResourceLimit",
147 }
148 }
149
150 pub fn from_u64(kind: u64) -> Option<RuntimeErrorKind> {
151 match kind {
152 0 => Some(RuntimeErrorKind::InternalError),
153 1 => Some(RuntimeErrorKind::TypeError),
154 2 => Some(RuntimeErrorKind::ArityError),
155 3 => Some(RuntimeErrorKind::NotCallable),
156 4 => Some(RuntimeErrorKind::NotConstructable),
157 5 => Some(RuntimeErrorKind::MissingProperty),
158 6 => Some(RuntimeErrorKind::InvalidHashKey),
159 7 => Some(RuntimeErrorKind::DivisionByZero),
160 8 => Some(RuntimeErrorKind::IntegerOverflow),
161 9 => Some(RuntimeErrorKind::ResourceLimit),
162 _ => None,
163 }
164 }
165}
166
167#[derive(Clone, Debug, Eq, PartialEq)]
168pub struct RuntimeFailure {
169 pub kind: RuntimeErrorKind,
170 pub message: String,
171}
172
173pub type RuntimeResult<T> = Result<T, RuntimeFailure>;
174
175fn fail<T>(kind: RuntimeErrorKind, message: impl Into<String>) -> RuntimeResult<T> {
176 Err(RuntimeFailure {
177 kind,
178 message: message.into(),
179 })
180}
181
182#[derive(Clone, Debug, Eq, PartialEq, Hash)]
183pub enum HashKey {
184 Integer(i64),
185 Boolean(bool),
186 Str(String),
187}
188
189impl HashKey {
190 fn rank(&self) -> u8 {
193 match self {
194 HashKey::Integer(_) => 0,
195 HashKey::Boolean(_) => 1,
196 HashKey::Str(_) => 2,
197 }
198 }
199
200 fn canonical_bytes(&self) -> Vec<u8> {
201 match self {
202 HashKey::Integer(raw) => raw.to_string().into_bytes(),
203 HashKey::Boolean(raw) => raw.to_string().into_bytes(),
204 HashKey::Str(raw) => raw.clone().into_bytes(),
205 }
206 }
207}
208
209#[derive(Clone, Debug)]
210pub struct ClosureData {
211 pub code: CodeHandle,
212 pub num_parameters: u64,
213 pub free: Vec<Value>,
214}
215
216#[derive(Clone, Debug)]
217pub struct ClassData {
218 pub name: String,
219 pub methods: HashMap<String, Value>,
220 pub constructor: Option<Value>,
221}
222
223#[derive(Clone, Debug)]
224pub struct InstanceData {
225 pub class: Value,
226 pub fields: HashMap<String, Value>,
227}
228
229#[derive(Clone, Debug)]
230pub struct BoundMethodData {
231 pub receiver: Value,
232 pub method: Value,
233 pub name: String,
234}
235
236#[derive(Clone, Debug)]
239pub enum HeapObject {
240 BoxedInt(i64),
241 Str(String),
242 Array(Vec<Value>),
243 Hash(HashMap<HashKey, Value>),
244 Closure(ClosureData),
245 Class(ClassData),
246 Instance(InstanceData),
247 BoundMethod(BoundMethodData),
248}
249
250fn get_obj<S: ValueStore>(store: &S, value: Value) -> RuntimeResult<&HeapObject> {
251 match store.try_get(value) {
252 Some(object) => Ok(object),
253 None => fail(RuntimeErrorKind::InternalError, "invalid heap reference"),
254 }
255}
256
257pub fn int_value<S: ValueStore>(store: &S, value: Value) -> Option<i64> {
259 if is_smi(value) {
260 return Some(smi_to_i64(value));
261 }
262 match store.try_get(value) {
263 Some(HeapObject::BoxedInt(raw)) => Some(*raw),
264 _ => None,
265 }
266}
267
268pub fn make_int<S: ValueStore>(store: &mut S, raw: i64) -> Value {
271 if i64_fits_smi(raw) {
272 smi_from_i64(raw)
273 } else {
274 store.alloc(HeapObject::BoxedInt(raw))
275 }
276}
277
278pub fn truthy(value: Value) -> bool {
280 value != FALSE_VALUE && value != NULL_VALUE
281}
282
283pub fn string_from_utf8<S: ValueStore>(store: &mut S, bytes: &[u8]) -> RuntimeResult<Value> {
284 match std::str::from_utf8(bytes) {
285 Ok(text) => Ok(store.alloc(HeapObject::Str(text.to_string()))),
286 Err(_) => fail(RuntimeErrorKind::InternalError, "string literal is not valid UTF-8"),
287 }
288}
289
290pub fn array_from_values<S: ValueStore>(store: &mut S, values: &[Value]) -> Value {
291 store.alloc(HeapObject::Array(values.to_vec()))
292}
293
294pub fn hash_key<S: ValueStore>(store: &S, value: Value) -> Option<HashKey> {
295 if let Some(raw) = int_value(store, value) {
296 return Some(HashKey::Integer(raw));
297 }
298 match value {
299 TRUE_VALUE => Some(HashKey::Boolean(true)),
300 FALSE_VALUE => Some(HashKey::Boolean(false)),
301 _ => match store.try_get(value) {
302 Some(HeapObject::Str(text)) => Some(HashKey::Str(text.clone())),
303 _ => None,
304 },
305 }
306}
307
308pub fn hash_from_pairs<S: ValueStore>(store: &mut S, pairs: &[Value]) -> RuntimeResult<Value> {
310 debug_assert_eq!(pairs.len() % 2, 0);
311 let mut entries = HashMap::new();
312 for &[key, value] in pairs.as_chunks::<2>().0 {
313 let key = match hash_key(store, key) {
314 Some(key) => key,
315 None => {
316 let shown = display(store, key)?;
317 return fail(
318 RuntimeErrorKind::InvalidHashKey,
319 format!("hash key must be hashable, got {}", shown),
320 );
321 }
322 };
323 entries.insert(key, value);
324 }
325 Ok(store.alloc(HeapObject::Hash(entries)))
326}
327
328pub fn closure_new<S: ValueStore>(
329 store: &mut S,
330 code: CodeHandle,
331 num_parameters: u64,
332 free: &[Value],
333) -> RuntimeResult<Value> {
334 if num_parameters as usize > MAX_INVOKE_ARGS {
335 return fail(
336 RuntimeErrorKind::ResourceLimit,
337 format!("closure cannot take more than {} parameters", MAX_INVOKE_ARGS),
338 );
339 }
340 Ok(store.alloc(HeapObject::Closure(ClosureData {
341 code,
342 num_parameters,
343 free: free.to_vec(),
344 })))
345}
346
347pub fn get_free<S: ValueStore>(store: &S, closure: Value, index: u64) -> RuntimeResult<Value> {
349 match get_obj(store, closure)? {
350 HeapObject::Closure(data) => match data.free.get(index as usize) {
351 Some(value) => Ok(*value),
352 None => fail(RuntimeErrorKind::InternalError, "free variable index out of range"),
353 },
354 _ => fail(RuntimeErrorKind::InternalError, "rt_get_free on a non-closure"),
355 }
356}
357
358pub fn class_new<S: ValueStore>(store: &mut S, name: &str) -> Value {
359 store.alloc(HeapObject::Class(ClassData {
360 name: name.to_string(),
361 methods: HashMap::new(),
362 constructor: None,
363 }))
364}
365
366pub fn class_add_method<S: ValueStore>(
367 store: &mut S,
368 class: Value,
369 name: &str,
370 method: Value,
371 is_constructor: bool,
372) -> RuntimeResult<()> {
373 if !matches!(get_obj(store, method)?, HeapObject::Closure(_)) {
374 return fail(RuntimeErrorKind::InternalError, "class method must be a closure");
375 }
376 match store.try_get_mut(class) {
377 Some(HeapObject::Class(data)) => {
378 if is_constructor {
379 data.constructor = Some(method);
380 } else {
381 data.methods.insert(name.to_string(), method);
382 }
383 Ok(())
384 }
385 _ => fail(RuntimeErrorKind::InternalError, "cannot install a method on a non-class"),
386 }
387}
388
389pub fn get_property<S: ValueStore>(
392 store: &mut S,
393 object: Value,
394 name: &str,
395) -> RuntimeResult<Value> {
396 let (class, field) = match store.try_get(object) {
397 Some(HeapObject::Instance(instance)) => {
398 (instance.class, instance.fields.get(name).copied())
399 }
400 _ => {
401 let shown = display(store, object)?;
402 return fail(
403 RuntimeErrorKind::TypeError,
404 format!("cannot read property '{}' of {}", name, shown),
405 );
406 }
407 };
408 if let Some(field) = field {
409 return Ok(field);
410 }
411 let (class_name, method) = match get_obj(store, class)? {
412 HeapObject::Class(data) => (data.name.clone(), data.methods.get(name).copied()),
413 _ => return fail(RuntimeErrorKind::InternalError, "instance has an invalid class"),
414 };
415 match method {
416 Some(method) => Ok(store.alloc(HeapObject::BoundMethod(BoundMethodData {
417 receiver: object,
418 method,
419 name: name.to_string(),
420 }))),
421 None => fail(
422 RuntimeErrorKind::MissingProperty,
423 format!("property '{}' does not exist on {}", name, class_name),
424 ),
425 }
426}
427
428pub fn set_property<S: ValueStore>(
429 store: &mut S,
430 object: Value,
431 name: &str,
432 value: Value,
433) -> RuntimeResult<()> {
434 match store.try_get_mut(object) {
435 Some(HeapObject::Instance(instance)) => {
436 instance.fields.insert(name.to_string(), value);
437 Ok(())
438 }
439 _ => {
440 let shown = display(store, object)?;
441 fail(
442 RuntimeErrorKind::TypeError,
443 format!("cannot set property '{}' of {}", name, shown),
444 )
445 }
446 }
447}
448
449pub fn index<S: ValueStore>(store: &S, object: Value, index: Value) -> RuntimeResult<Value> {
453 if is_heap(object) {
454 match get_obj(store, object)? {
455 HeapObject::Array(elements) => {
456 if let Some(position) = int_value(store, index) {
457 if position >= 0 && (position as usize) < elements.len() {
458 return Ok(elements[position as usize]);
459 }
460 return Ok(NULL_VALUE);
461 }
462 }
463 HeapObject::Hash(entries) => {
464 let key = match hash_key(store, index) {
465 Some(key) => key,
466 None => {
467 return fail(RuntimeErrorKind::InvalidHashKey, "unsupported hash index key")
468 }
469 };
470 return Ok(entries.get(&key).copied().unwrap_or(NULL_VALUE));
471 }
472 _ => {}
473 }
474 }
475 let object_shown = display(store, object)?;
476 let index_shown = display(store, index)?;
477 fail(
478 RuntimeErrorKind::TypeError,
479 format!("unsupported index operation for {} and {}", object_shown, index_shown),
480 )
481}
482
483pub fn eq_values<S: ValueStore>(store: &S, left: Value, right: Value) -> RuntimeResult<bool> {
487 let left_int = int_value(store, left);
488 let right_int = int_value(store, right);
489 if let (Some(l), Some(r)) = (left_int, right_int) {
490 return Ok(l == r);
491 }
492 if left_int.is_some() != right_int.is_some() {
493 return Ok(false);
494 }
495 if !is_heap(left) || !is_heap(right) {
496 return Ok(left == right);
499 }
500 match (get_obj(store, left)?, get_obj(store, right)?) {
501 (HeapObject::Str(l), HeapObject::Str(r)) => Ok(l == r),
502 (HeapObject::Array(l), HeapObject::Array(r)) => {
503 if l.len() != r.len() {
504 return Ok(false);
505 }
506 for (l_element, r_element) in l.iter().zip(r.iter()) {
507 if !eq_values(store, *l_element, *r_element)? {
508 return Ok(false);
509 }
510 }
511 Ok(true)
512 }
513 (HeapObject::Hash(l), HeapObject::Hash(r)) => {
514 if l.len() != r.len() {
515 return Ok(false);
516 }
517 for (key, l_value) in l.iter() {
518 match r.get(key) {
519 Some(r_value) => {
520 if !eq_values(store, *l_value, *r_value)? {
521 return Ok(false);
522 }
523 }
524 None => return Ok(false),
525 }
526 }
527 Ok(true)
528 }
529 (HeapObject::Closure(_), HeapObject::Closure(_))
530 | (HeapObject::Class(_), HeapObject::Class(_))
531 | (HeapObject::Instance(_), HeapObject::Instance(_))
532 | (HeapObject::BoundMethod(_), HeapObject::BoundMethod(_)) => Ok(left == right),
533 _ => Ok(false),
534 }
535}
536
537pub fn gt<S: ValueStore>(store: &S, left: Value, right: Value) -> RuntimeResult<Value> {
539 if let (Some(l), Some(r)) = (int_value(store, left), int_value(store, right)) {
540 return Ok(bool_value(l > r));
541 }
542 let left_shown = display(store, left)?;
543 let right_shown = display(store, right)?;
544 fail(
545 RuntimeErrorKind::TypeError,
546 format!("unsupported comparison for {} and {}", left_shown, right_shown),
547 )
548}
549
550fn checked_arith<S: ValueStore>(
551 store: &mut S,
552 left: Value,
553 right: Value,
554 operation: &str,
555 apply: impl Fn(i64, i64) -> Option<i64>,
556) -> RuntimeResult<Value> {
557 if let (Some(l), Some(r)) = (int_value(store, left), int_value(store, right)) {
558 return match apply(l, r) {
559 Some(raw) => Ok(make_int(store, raw)),
560 None => fail(
561 RuntimeErrorKind::IntegerOverflow,
562 format!("integer overflow in {}", operation),
563 ),
564 };
565 }
566 let left_shown = display(store, left)?;
567 let right_shown = display(store, right)?;
568 fail(
569 RuntimeErrorKind::TypeError,
570 format!("unsupported binary operation for {} and {}", left_shown, right_shown),
571 )
572}
573
574pub fn add<S: ValueStore>(store: &mut S, left: Value, right: Value) -> RuntimeResult<Value> {
576 if int_value(store, left).is_none() {
577 if let (Some(HeapObject::Str(l)), Some(HeapObject::Str(r))) =
578 (store.try_get(left), store.try_get(right))
579 {
580 let combined = format!("{}{}", l, r);
581 return Ok(store.alloc(HeapObject::Str(combined)));
582 }
583 }
584 checked_arith(store, left, right, "addition", i64::checked_add)
585}
586
587pub fn sub<S: ValueStore>(store: &mut S, left: Value, right: Value) -> RuntimeResult<Value> {
588 checked_arith(store, left, right, "subtraction", i64::checked_sub)
589}
590
591pub fn mul<S: ValueStore>(store: &mut S, left: Value, right: Value) -> RuntimeResult<Value> {
592 checked_arith(store, left, right, "multiplication", i64::checked_mul)
593}
594
595pub fn div<S: ValueStore>(store: &mut S, left: Value, right: Value) -> RuntimeResult<Value> {
598 if let (Some(_), Some(0)) = (int_value(store, left), int_value(store, right)) {
599 return fail(RuntimeErrorKind::DivisionByZero, "division by zero");
600 }
601 checked_arith(store, left, right, "division", i64::checked_div)
602}
603
604pub fn minus<S: ValueStore>(store: &mut S, value: Value) -> RuntimeResult<Value> {
605 if let Some(raw) = int_value(store, value) {
606 return match raw.checked_neg() {
607 Some(negated) => Ok(make_int(store, negated)),
608 None => fail(RuntimeErrorKind::IntegerOverflow, "integer overflow in negation"),
609 };
610 }
611 let shown = display(store, value)?;
612 fail(RuntimeErrorKind::TypeError, format!("unsupported type for negation: {}", shown))
613}
614
615pub fn bang(value: Value) -> Value {
617 bool_value(!truthy(value))
618}
619
620fn sorted_hash_entries(entries: &HashMap<HashKey, Value>) -> Vec<(&HashKey, Value)> {
622 let mut sorted: Vec<(&HashKey, Value)> =
623 entries.iter().map(|(key, value)| (key, *value)).collect();
624 sorted.sort_by(|(a, _), (b, _)| {
625 (a.rank(), a.canonical_bytes()).cmp(&(b.rank(), b.canonical_bytes()))
626 });
627 sorted
628}
629
630fn key_display(key: &HashKey) -> String {
631 match key {
632 HashKey::Integer(raw) => raw.to_string(),
633 HashKey::Boolean(raw) => raw.to_string(),
634 HashKey::Str(raw) => raw.clone(),
635 }
636}
637
638fn instance_class_name<S: ValueStore>(store: &S, instance: Value) -> RuntimeResult<String> {
639 match get_obj(store, instance)? {
640 HeapObject::Instance(data) => match get_obj(store, data.class)? {
641 HeapObject::Class(class) => Ok(class.name.clone()),
642 _ => fail(RuntimeErrorKind::InternalError, "instance has an invalid class"),
643 },
644 _ => fail(RuntimeErrorKind::InternalError, "expected an instance"),
645 }
646}
647
648pub fn display<S: ValueStore>(store: &S, value: Value) -> RuntimeResult<String> {
650 if let Some(raw) = int_value(store, value) {
651 return Ok(raw.to_string());
652 }
653 match value {
654 TRUE_VALUE => return Ok("true".to_string()),
655 FALSE_VALUE => return Ok("false".to_string()),
656 NULL_VALUE => return Ok("null".to_string()),
657 _ => {}
658 }
659 if is_builtin(value) {
660 return Ok("[builtin function]".to_string());
661 }
662 match get_obj(store, value)? {
663 HeapObject::BoxedInt(_) => unreachable!("handled by int_value"),
664 HeapObject::Str(text) => Ok(text.clone()),
665 HeapObject::Array(elements) => {
666 let mut rendered = Vec::with_capacity(elements.len());
667 for element in elements {
668 rendered.push(display(store, *element)?);
669 }
670 Ok(format!("[{}]", rendered.join(", ")))
671 }
672 HeapObject::Hash(entries) => {
673 let mut rendered = Vec::with_capacity(entries.len());
674 for (key, entry_value) in sorted_hash_entries(entries) {
675 rendered.push(format!("{}: {}", key_display(key), display(store, entry_value)?));
676 }
677 Ok(format!("{{{}}}", rendered.join(", ")))
678 }
679 HeapObject::Closure(_) => Ok("[function]".to_string()),
680 HeapObject::Class(data) => Ok(format!("[class {}]", data.name)),
681 HeapObject::Instance(_) => Ok(format!("[object {}]", instance_class_name(store, value)?)),
682 HeapObject::BoundMethod(data) => Ok(format!(
683 "[bound method {}.{}]",
684 instance_class_name(store, data.receiver)?,
685 data.name
686 )),
687 }
688}
689
690fn json_escape(text: &str) -> String {
691 let mut escaped = String::with_capacity(text.len() + 2);
692 for character in text.chars() {
693 match character {
694 '"' => escaped.push_str("\\\""),
695 '\\' => escaped.push_str("\\\\"),
696 '\n' => escaped.push_str("\\n"),
697 '\r' => escaped.push_str("\\r"),
698 '\t' => escaped.push_str("\\t"),
699 c if (c as u32) < 0x20 => {
700 escaped.push_str(&format!("\\u{:04x}", c as u32));
701 }
702 c => escaped.push(c),
703 }
704 }
705 escaped
706}
707
708pub fn canonical_value<S: ValueStore>(store: &S, value: Value) -> RuntimeResult<String> {
710 if let Some(raw) = int_value(store, value) {
711 return Ok(format!("{{\"type\":\"integer\",\"value\":\"{}\"}}", raw));
712 }
713 match value {
714 TRUE_VALUE => return Ok("{\"type\":\"boolean\",\"value\":true}".to_string()),
715 FALSE_VALUE => return Ok("{\"type\":\"boolean\",\"value\":false}".to_string()),
716 NULL_VALUE => return Ok("{\"type\":\"null\"}".to_string()),
717 _ => {}
718 }
719 if is_builtin(value) {
720 let id = match builtin_from_ordinal(value >> 3) {
721 Some(id) => id,
722 None => return fail(RuntimeErrorKind::InternalError, "invalid builtin encoding"),
723 };
724 return Ok(format!("{{\"type\":\"builtin\",\"id\":\"{}\"}}", builtin_canonical_name(id)));
725 }
726 match get_obj(store, value)? {
727 HeapObject::BoxedInt(_) => unreachable!("handled by int_value"),
728 HeapObject::Str(text) => {
729 Ok(format!("{{\"type\":\"string\",\"value\":\"{}\"}}", json_escape(text)))
730 }
731 HeapObject::Array(elements) => {
732 let mut rendered = Vec::with_capacity(elements.len());
733 for element in elements {
734 rendered.push(canonical_value(store, *element)?);
735 }
736 Ok(format!("{{\"type\":\"array\",\"elements\":[{}]}}", rendered.join(",")))
737 }
738 HeapObject::Hash(entries) => {
739 let mut rendered = Vec::with_capacity(entries.len());
740 for (key, entry_value) in sorted_hash_entries(entries) {
741 let key_json = match key {
742 HashKey::Integer(raw) => {
743 format!("{{\"type\":\"integer\",\"value\":\"{}\"}}", raw)
744 }
745 HashKey::Boolean(raw) => {
746 format!("{{\"type\":\"boolean\",\"value\":{}}}", raw)
747 }
748 HashKey::Str(raw) => {
749 format!("{{\"type\":\"string\",\"value\":\"{}\"}}", json_escape(raw))
750 }
751 };
752 rendered.push(format!(
753 "{{\"key\":{},\"value\":{}}}",
754 key_json,
755 canonical_value(store, entry_value)?
756 ));
757 }
758 Ok(format!("{{\"type\":\"hash\",\"entries\":[{}]}}", rendered.join(",")))
759 }
760 HeapObject::Closure(_) => Ok("{\"type\":\"function\"}".to_string()),
761 HeapObject::Class(data) => {
762 Ok(format!("{{\"type\":\"class\",\"name\":\"{}\"}}", json_escape(&data.name)))
763 }
764 HeapObject::Instance(_) => Ok(format!(
765 "{{\"type\":\"instance\",\"class\":\"{}\"}}",
766 json_escape(&instance_class_name(store, value)?)
767 )),
768 HeapObject::BoundMethod(data) => Ok(format!(
769 "{{\"type\":\"bound_method\",\"class\":\"{}\",\"method\":\"{}\"}}",
770 json_escape(&instance_class_name(store, data.receiver)?),
771 json_escape(&data.name)
772 )),
773 }
774}
775
776pub trait OutputSink {
779 fn write_line(&mut self, line: &str);
780}
781
782pub struct BufferSink {
784 pub bytes: Vec<u8>,
785}
786
787impl Default for BufferSink {
788 fn default() -> Self {
789 Self::new()
790 }
791}
792
793impl BufferSink {
794 pub fn new() -> BufferSink {
795 BufferSink {
796 bytes: vec![],
797 }
798 }
799}
800
801impl OutputSink for BufferSink {
802 fn write_line(&mut self, line: &str) {
803 self.bytes.extend_from_slice(line.as_bytes());
804 self.bytes.push(b'\n');
805 }
806}
807
808pub fn call_builtin<S: ValueStore>(
811 store: &mut S,
812 sink: &mut dyn OutputSink,
813 id: BuiltinId,
814 args: &[Value],
815) -> RuntimeResult<Value> {
816 let expect_arity = |count: usize| -> RuntimeResult<()> {
817 if args.len() != count {
818 return fail(
819 RuntimeErrorKind::ArityError,
820 format!(
821 "builtin {} expected {} argument{}, got {}",
822 builtin_canonical_name(id),
823 count,
824 if count == 1 { "" } else { "s" },
825 args.len()
826 ),
827 );
828 }
829 Ok(())
830 };
831 match id {
832 BuiltinId::Len => {
833 expect_arity(1)?;
834 let length = match store.try_get(args[0]) {
835 Some(HeapObject::Str(text)) => text.len() as i64,
836 Some(HeapObject::Array(elements)) => elements.len() as i64,
837 _ => {
838 let shown = display(store, args[0])?;
839 return fail(
840 RuntimeErrorKind::TypeError,
841 format!("builtin len not supported for type {}", shown),
842 );
843 }
844 };
845 Ok(make_int(store, length))
846 }
847 BuiltinId::Puts => {
848 for argument in args {
849 let line = display(store, *argument)?;
850 sink.write_line(&line);
851 }
852 Ok(NULL_VALUE)
853 }
854 BuiltinId::First | BuiltinId::Last => {
855 expect_arity(1)?;
856 match store.try_get(args[0]) {
857 Some(HeapObject::Array(elements)) => {
858 let element =
859 if id == BuiltinId::First { elements.first() } else { elements.last() };
860 Ok(element.copied().unwrap_or(NULL_VALUE))
861 }
862 _ => {
863 let shown = display(store, args[0])?;
864 fail(
865 RuntimeErrorKind::TypeError,
866 format!(
867 "builtin {} not supported for type {}",
868 builtin_canonical_name(id),
869 shown
870 ),
871 )
872 }
873 }
874 }
875 BuiltinId::Rest => {
876 expect_arity(1)?;
877 let rest = match store.try_get(args[0]) {
878 Some(HeapObject::Array(elements)) => {
879 if elements.is_empty() {
880 return Ok(NULL_VALUE);
881 }
882 elements[1..].to_vec()
883 }
884 _ => {
885 let shown = display(store, args[0])?;
886 return fail(
887 RuntimeErrorKind::TypeError,
888 format!("builtin rest not supported for type {}", shown),
889 );
890 }
891 };
892 Ok(store.alloc(HeapObject::Array(rest)))
893 }
894 BuiltinId::Push => {
895 expect_arity(2)?;
896 let pushed = match store.try_get(args[0]) {
897 Some(HeapObject::Array(elements)) => {
898 let mut extended = elements.clone();
899 extended.push(args[1]);
900 extended
901 }
902 _ => {
903 let shown = display(store, args[0])?;
904 return fail(
905 RuntimeErrorKind::TypeError,
906 format!("builtin push not supported for type {}", shown),
907 );
908 }
909 };
910 Ok(store.alloc(HeapObject::Array(pushed)))
911 }
912 }
913}
914
915#[derive(Clone, Debug, PartialEq)]
918pub enum ReturnPolicy {
919 Direct,
920 ConstructorInstance(Value),
921}
922
923#[derive(Clone, Debug)]
927pub enum CallDispatch {
928 Return(Value),
929 Invoke { code: CodeHandle, closure: Value, args: Vec<Value>, return_policy: ReturnPolicy },
930}
931
932fn closure_signature<S: ValueStore>(store: &S, closure: Value) -> Option<(CodeHandle, u64)> {
933 match store.try_get(closure) {
934 Some(HeapObject::Closure(data)) => Some((data.code, data.num_parameters)),
935 _ => None,
936 }
937}
938
939pub fn dispatch_call<S: ValueStore>(
942 store: &mut S,
943 sink: &mut dyn OutputSink,
944 callee: Value,
945 args: &[Value],
946) -> RuntimeResult<CallDispatch> {
947 if is_builtin(callee) {
948 let id = match builtin_from_ordinal(callee >> 3) {
949 Some(id) => id,
950 None => return fail(RuntimeErrorKind::InternalError, "invalid builtin encoding"),
951 };
952 return Ok(CallDispatch::Return(call_builtin(store, sink, id, args)?));
953 }
954 if is_heap(callee) {
955 match get_obj(store, callee)? {
956 HeapObject::Closure(data) => {
957 if data.num_parameters != args.len() as u64 {
958 return fail(
959 RuntimeErrorKind::ArityError,
960 format!(
961 "wrong number of arguments: want={}, got={}",
962 data.num_parameters,
963 args.len()
964 ),
965 );
966 }
967 return Ok(CallDispatch::Invoke {
968 code: data.code,
969 closure: callee,
970 args: args.to_vec(),
971 return_policy: ReturnPolicy::Direct,
972 });
973 }
974 HeapObject::BoundMethod(bound) => {
975 let (receiver, method, name) = (bound.receiver, bound.method, bound.name.clone());
976 let (code, num_parameters) = match closure_signature(store, method) {
977 Some(signature) => signature,
978 None => {
979 return fail(
980 RuntimeErrorKind::InternalError,
981 "bound method is not a closure",
982 )
983 }
984 };
985 let expected = num_parameters.saturating_sub(1);
986 if expected != args.len() as u64 {
987 let class_name = instance_class_name(store, receiver)?;
988 return fail(
989 RuntimeErrorKind::ArityError,
990 format!(
991 "wrong number of arguments for {}.{}: want={}, got={}",
992 class_name,
993 name,
994 expected,
995 args.len()
996 ),
997 );
998 }
999 let mut invoke_args = Vec::with_capacity(args.len() + 1);
1000 invoke_args.push(receiver);
1001 invoke_args.extend_from_slice(args);
1002 return Ok(CallDispatch::Invoke {
1003 code,
1004 closure: method,
1005 args: invoke_args,
1006 return_policy: ReturnPolicy::Direct,
1007 });
1008 }
1009 HeapObject::Class(data) => {
1010 return fail(
1011 RuntimeErrorKind::NotCallable,
1012 format!("class {} must be constructed with new", data.name),
1013 );
1014 }
1015 _ => {}
1016 }
1017 }
1018 let shown = display(store, callee)?;
1019 fail(RuntimeErrorKind::NotCallable, format!("cannot call {}", shown))
1020}
1021
1022pub fn dispatch_construct<S: ValueStore>(
1026 store: &mut S,
1027 callee: Value,
1028 args: &[Value],
1029) -> RuntimeResult<CallDispatch> {
1030 let (class_name, constructor) = match store.try_get(callee) {
1031 Some(HeapObject::Class(data)) => (data.name.clone(), data.constructor),
1032 _ => {
1033 let shown = display(store, callee)?;
1034 return fail(RuntimeErrorKind::NotConstructable, format!("cannot construct {}", shown));
1035 }
1036 };
1037 let constructor = match constructor {
1038 None => {
1039 if !args.is_empty() {
1040 return fail(
1041 RuntimeErrorKind::ArityError,
1042 format!(
1043 "wrong number of arguments for {}.constructor: want=0, got={}",
1044 class_name,
1045 args.len()
1046 ),
1047 );
1048 }
1049 let instance = store.alloc(HeapObject::Instance(InstanceData {
1050 class: callee,
1051 fields: HashMap::new(),
1052 }));
1053 return Ok(CallDispatch::Return(instance));
1054 }
1055 Some(constructor) => constructor,
1056 };
1057 let (code, num_parameters) = match closure_signature(store, constructor) {
1058 Some(signature) => signature,
1059 None => return fail(RuntimeErrorKind::InternalError, "constructor is not a closure"),
1060 };
1061 let expected = num_parameters.saturating_sub(1);
1062 if expected != args.len() as u64 {
1063 return fail(
1064 RuntimeErrorKind::ArityError,
1065 format!(
1066 "wrong number of arguments for {}.constructor: want={}, got={}",
1067 class_name,
1068 expected,
1069 args.len()
1070 ),
1071 );
1072 }
1073 let instance = store.alloc(HeapObject::Instance(InstanceData {
1074 class: callee,
1075 fields: HashMap::new(),
1076 }));
1077 let mut invoke_args = Vec::with_capacity(args.len() + 1);
1078 invoke_args.push(instance);
1079 invoke_args.extend_from_slice(args);
1080 Ok(CallDispatch::Invoke {
1081 code,
1082 closure: constructor,
1083 args: invoke_args,
1084 return_policy: ReturnPolicy::ConstructorInstance(instance),
1085 })
1086}