1pub mod arena;
10pub mod atom;
11pub mod bigint;
12pub mod buffer;
13pub mod code;
14pub mod gc;
15pub(crate) mod hash;
16pub mod object;
17pub mod report;
18pub mod string;
19pub mod symbol;
20pub mod table;
21pub mod values;
22
23use std::fmt;
24
25use self::arena::Arena;
26use self::atom::{AtomTable, KEY_INDEX_LIMIT, PropertyKey};
27use self::bigint::BigIntRecord;
28use self::gc::{GcState, Handles, HeapLimits, RootStack, Roots};
29use self::hash::FxHashMap;
30use self::object::{
31 FunctionBytecode, FunctionRecord, Internal, InternalData, InternalTag, NONE, ObjectHeader,
32 ObjectKind, PayloadArenas, flags,
33};
34use self::string::{AtomKeep, StringRecord, StringRepr};
35use self::symbol::SymbolRecord;
36use self::values::{Range, ValueHeap};
37use crate::bytecode::CodeUnitId;
38use crate::shape::{Dictionary, PrototypeInfo, STATIC_CELL, Shape, ValidityCell};
39use crate::value::{BigIntId, BytecodeId, ObjectId, ScopeId, ShapeId, StringId, SymbolId, Value};
40use crate::vm::VmStack;
41use crate::vm::ic::IcStore;
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum HeapError {
45 OutOfMemory,
48 DuplicateKey,
50 PrototypeCycle,
52}
53
54impl fmt::Display for HeapError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.write_str(match self {
57 HeapError::OutOfMemory => "out of memory",
58 HeapError::DuplicateKey => "duplicate property key",
59 HeapError::PrototypeCycle => "cyclic prototype chain",
60 })
61 }
62}
63
64impl std::error::Error for HeapError {}
65
66#[derive(Clone, Copy, Debug)]
68pub struct BytecodeTemplate<'a> {
69 pub unit: CodeUnitId,
70 pub constants: &'a [Value],
71 pub children: &'a [u32],
73}
74
75#[derive(Clone, Copy, Debug, Default)]
77pub(crate) struct Need {
78 pub objects: usize,
79 pub shapes: usize,
80 pub strings: usize,
81 pub symbols: usize,
82 pub bigints: usize,
83 pub cells: usize,
84 pub functions: usize,
85 pub bytecodes: usize,
86 pub internals: usize,
87 pub values: [u32; 2],
89 pub extra: usize,
91}
92
93impl Need {
94 pub fn nominal_bytes(&self) -> usize {
99 self.objects * size_of::<ObjectHeader>()
100 + self.strings * size_of::<StringRecord>()
101 + self.symbols * size_of::<SymbolRecord>()
102 + self.bigints * size_of::<BigIntRecord>()
103 + self.functions * size_of::<FunctionRecord>()
104 + self.bytecodes * size_of::<FunctionBytecode>()
105 + self.internals * size_of::<Internal>()
106 + (values::class_len(self.values[0]) + values::class_len(self.values[1])) as usize * 8
107 + self.extra
108 }
109}
110
111pub struct Heap {
112 pub(crate) objects: Arena<ObjectHeader>,
113 pub(crate) shapes: Arena<Shape>,
114 pub(crate) strings: Arena<StringRecord>,
115 pub(crate) kept: Vec<Value>,
117 pub(crate) rejections: Vec<Value>,
120 pub(crate) holey: crate::heap::hash::FxHashSet<u32>,
123 pub(crate) modules: Vec<Value>,
125 pub(crate) rope_bytes: std::cell::Cell<usize>,
127 pub(crate) ropes: FxHashMap<u32, (StringId, StringId)>,
130 pub(crate) html_dda: bool,
133 pub(crate) symbols: Arena<SymbolRecord>,
134 pub(crate) bigints: Arena<BigIntRecord>,
135 pub(crate) cells: Arena<ValidityCell>,
136 pub(crate) values: ValueHeap,
137 pub(crate) payloads: PayloadArenas,
138 pub(crate) atoms: AtomTable,
139 pub(crate) atom_mode: AtomMode,
141 pub(crate) pinned_atoms: Vec<StringId>,
144 pub(crate) held_atoms: Vec<StringId>,
147 pub(crate) code: code::DynamicUnits,
149 pub(crate) regexps: crate::vm::regexp::RegExpStore,
151 pub(crate) shape_roots: FxHashMap<(u32, ObjectKind), ShapeId>,
153 pub(crate) last_root: Option<((u32, ObjectKind), ShapeId)>,
155 pub(crate) prototypes: FxHashMap<ObjectId, PrototypeInfo>,
157 pub(crate) roots: Roots,
158 pub(crate) gc: GcState,
159 pub(crate) vm: VmStack,
162 pub(crate) ics: IcStore,
165 pub(crate) global_generation: u32,
169 pub(crate) holes_writable: bool,
174 pub(crate) closure_shapes: [Option<ShapeId>; CLOSURE_SHAPES],
178 pub(crate) pending_closure_shapes: [Option<ShapeId>; CLOSURE_SHAPES],
182 pub(crate) arguments_shapes: [Option<ShapeId>; 2],
186 pub(crate) jobs: std::collections::VecDeque<crate::vm::jobs::Job>,
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq)]
194pub enum AtomMode {
195 Pin,
199 Hold,
203 Weak,
205}
206
207pub(crate) const CLOSURE_SHAPES: usize = 5;
210
211impl Default for Heap {
212 fn default() -> Self {
213 Self::new(HeapLimits::default())
214 }
215}
216
217impl Heap {
218 pub fn new(limits: HeapLimits) -> Self {
219 let mut heap = Heap {
220 objects: Arena::new(),
221 shapes: Arena::new(),
222 strings: Arena::new(),
223 kept: Vec::new(),
224 rejections: Vec::new(),
225 holey: Default::default(),
226 modules: Vec::new(),
227 rope_bytes: std::cell::Cell::new(0),
228 ropes: FxHashMap::default(),
229 html_dda: false,
230 symbols: Arena::new(),
231 bigints: Arena::new(),
232 cells: Arena::new(),
233 values: ValueHeap::new(),
234 payloads: PayloadArenas::default(),
235 atoms: AtomTable::default(),
236 atom_mode: AtomMode::Pin,
237 pinned_atoms: Vec::new(),
238 held_atoms: Vec::new(),
239 code: code::DynamicUnits::default(),
240 regexps: Default::default(),
241 shape_roots: FxHashMap::default(),
242 last_root: None,
243 prototypes: FxHashMap::default(),
244 roots: Roots::default(),
245 gc: GcState::new(limits),
246 vm: VmStack::default(),
247 ics: IcStore::default(),
248 global_generation: 0,
249 holes_writable: true,
250 closure_shapes: [None; CLOSURE_SHAPES],
251 pending_closure_shapes: [None; CLOSURE_SHAPES],
252 arguments_shapes: [None; 2],
253 jobs: std::collections::VecDeque::new(),
254 };
255 let cell = heap
256 .cells
257 .alloc(ValidityCell { valid: true }, &mut heap.gc.room);
258 debug_assert_eq!(cell, STATIC_CELL.0);
259 heap
260 }
261
262 pub fn roots(&mut self) -> &mut RootStack {
265 &mut self.roots.stack
266 }
267
268 pub fn handles(&mut self) -> &mut Handles {
269 &mut self.roots.handles
270 }
271
272 pub fn handles_ref(&self) -> &Handles {
273 &self.roots.handles
274 }
275
276 pub fn add_fixed_root(&mut self, value: Value) -> usize {
278 self.roots.fixed.push(value);
279 self.roots.fixed.len() - 1
280 }
281
282 pub fn set_fixed_root(&mut self, slot: usize, value: Value) {
283 self.roots.fixed[slot] = value;
284 }
285
286 pub fn fixed_root(&self, slot: usize) -> Value {
287 self.roots.fixed[slot]
288 }
289
290 pub fn gc(&self) -> &GcState {
293 &self.gc
294 }
295
296 pub fn gc_mut(&mut self) -> &mut GcState {
297 &mut self.gc
298 }
299
300 pub fn bytes(&self) -> usize {
304 self.objects.bytes()
305 + self.shapes.bytes()
306 + self.strings.bytes()
307 + self.rope_bytes.get()
308 + self.symbols.bytes()
309 + self.bigints.bytes()
310 + self.cells.bytes()
311 + self.payloads.functions.bytes()
312 + self.payloads.bytecode.bytes()
313 + self.payloads.internals.bytes()
314 + self.values.bytes()
315 + self.code.live_bytes()
316 + self.regexps.bytes()
317 + self.table_bytes()
318 }
319
320 pub fn table_bytes(&self) -> usize {
323 self.ropes.capacity() * (size_of::<(u32, (StringId, StringId))>() + 1)
324 + self.atoms.bytes()
325 + self.ics.bytes()
326 }
327
328 pub fn used_bytes(&self) -> usize {
330 self.objects.used_bytes()
331 + self.shapes.used_bytes()
332 + self.strings.used_bytes()
333 + self.rope_bytes.get()
334 + self.symbols.used_bytes()
335 + self.bigints.used_bytes()
336 + self.cells.used_bytes()
337 + self.payloads.functions.used_bytes()
338 + self.payloads.bytecode.used_bytes()
339 + self.payloads.internals.used_bytes()
340 + self.values.used_slots() * 8
341 + self.code.live_bytes()
342 + self.regexps.bytes()
343 }
344
345 pub fn ics(&self) -> &IcStore {
346 &self.ics
347 }
348
349 pub fn ics_mut(&mut self) -> &mut IcStore {
350 &mut self.ics
351 }
352
353 pub fn global_generation(&self) -> u32 {
354 self.global_generation
355 }
356
357 pub(crate) fn bump_global_generation(&mut self) {
361 self.global_generation = self.global_generation.wrapping_add(1);
362 if self.global_generation == crate::vm::ic::EMPTY_TAG {
363 self.global_generation = 0;
364 self.ics.clear_kind(crate::vm::ic::IcKind::Global);
365 }
366 }
367
368 pub fn value_heap(&self) -> &ValueHeap {
369 &self.values
370 }
371
372 pub fn regexps(&self) -> &crate::vm::regexp::RegExpStore {
374 &self.regexps
375 }
376
377 pub fn atom_table(&self) -> &AtomTable {
378 &self.atoms
379 }
380
381 pub fn live_objects(&self) -> usize {
382 self.objects.live_count()
383 }
384
385 pub fn live_shapes(&self) -> usize {
386 self.shapes.live_count()
387 }
388
389 pub fn live_strings(&self) -> usize {
390 self.strings.live_count()
391 }
392
393 pub fn live_cells(&self) -> usize {
394 self.cells.live_count()
395 }
396
397 pub fn is_live(&self, value: Value) -> bool {
398 if let Some(id) = value.as_object() {
399 self.objects.is_live(id.0)
400 } else if let Some(id) = value.as_string() {
401 self.strings.is_live(id.0)
402 } else if let Some(id) = value.as_symbol() {
403 self.symbols.is_live(id.0)
404 } else if let Some(id) = value.as_bigint() {
405 self.bigints.is_live(id.0)
406 } else {
407 true
408 }
409 }
410
411 pub fn alloc_object(
417 &mut self,
418 kind: ObjectKind,
419 prototype: Option<ObjectId>,
420 slot_capacity: u32,
421 ) -> Result<ObjectId, HeapError> {
422 match kind {
423 ObjectKind::Ordinary
424 | ObjectKind::Array
425 | ObjectKind::Native
426 | ObjectKind::Arguments
427 | ObjectKind::ForInIterator => self.alloc_plain(kind, prototype, slot_capacity, 0),
428 ObjectKind::RegExp => {
430 self.alloc_object_with(kind, prototype, slot_capacity, 0, NONE, Need::default())
431 }
432 ObjectKind::Scope | ObjectKind::GlobalObject => {
433 self.alloc_object_with(kind, prototype, slot_capacity, 0, NONE, Need::default())
434 }
435 ObjectKind::Function => panic!("function objects are created by alloc_function"),
436 ObjectKind::Free => panic!("ObjectKind::Free is the poison value"),
437 _ => panic!("{kind:?} objects are created by alloc_internal"),
438 }
439 }
440
441 #[allow(clippy::too_many_arguments)]
446 pub fn alloc_internal(
447 &mut self,
448 kind: ObjectKind,
449 tag: InternalTag,
450 prototype: Option<ObjectId>,
451 slot_capacity: u32,
452 slots: &[Value],
453 data: InternalData,
454 extra_flags: u8,
455 ) -> Result<ObjectId, HeapError> {
456 debug_assert!(kind.has_internal());
457 let mut keep = Vec::with_capacity(slots.len() + 1);
458 keep.push(prototype.map_or(Value::UNDEFINED, Value::object));
459 keep.extend_from_slice(slots);
460 let need = Need {
461 objects: 1,
462 shapes: 1,
463 internals: 1,
464 values: [slot_capacity, 0],
465 extra: slots.len() * 8,
466 ..Need::default()
467 };
468 self.safepoint(&need, &keep)?;
469 let shape = self.root_shape(prototype, kind);
470 let range = self.values.alloc_slots(slot_capacity, &mut self.gc.room);
471 let payload = self.payloads.internals.alloc(
472 Internal {
473 tag,
474 slots: slots.to_vec(),
475 data,
476 },
477 &mut self.gc.room,
478 );
479 let header = ObjectHeader::new(
480 kind,
481 shape,
482 range,
483 Range::EMPTY,
484 payload,
485 flags::EXTENSIBLE | extra_flags,
486 );
487 Ok(ObjectId(self.objects.alloc(header, &mut self.gc.room)))
488 }
489
490 #[inline]
492 pub fn internal(&self, object: ObjectId) -> &Internal {
493 let header = self.objects.get(object.0);
494 debug_assert!(header.kind.has_internal(), "{:?}", header.kind);
495 self.payloads.internals.get(header.payload)
496 }
497
498 #[inline]
499 pub fn internal_mut(&mut self, object: ObjectId) -> &mut Internal {
500 let header = self.objects.get(object.0);
501 debug_assert!(header.kind.has_internal(), "{:?}", header.kind);
502 self.payloads.internals.get_mut(header.payload)
503 }
504
505 #[inline]
507 pub fn internal_tag(&self, object: ObjectId) -> InternalTag {
508 let header = self.objects.get(object.0);
509 if header.kind.has_internal() {
510 self.payloads.internals.get(header.payload).tag
511 } else {
512 InternalTag::None
513 }
514 }
515
516 #[inline]
517 pub fn internal_slot(&self, object: ObjectId, slot: usize) -> Value {
518 self.internal(object).slots[slot]
519 }
520
521 #[inline]
522 pub fn set_internal_slot(&mut self, object: ObjectId, slot: usize, value: Value) {
523 self.internal_mut(object).slots[slot] = value;
524 }
525
526 pub fn push_internal_slot(&mut self, object: ObjectId, value: Value) -> Result<(), HeapError> {
529 let internal = self.internal(object);
530 if internal.slots.len() == internal.slots.capacity() {
531 let extra = internal.slots.capacity().max(4) * 8;
532 self.safepoint(
533 &Need {
534 extra,
535 ..Need::default()
536 },
537 &[Value::object(object), value],
538 )?;
539 }
540 self.internal_mut(object).slots.push(value);
541 Ok(())
542 }
543
544 #[inline]
546 fn alloc_plain(
547 &mut self,
548 kind: ObjectKind,
549 prototype: Option<ObjectId>,
550 slot_capacity: u32,
551 element_capacity: u32,
552 ) -> Result<ObjectId, HeapError> {
553 let need = Need {
554 objects: 1,
555 shapes: 1,
556 values: [slot_capacity, element_capacity],
557 ..Need::default()
558 };
559 self.safepoint(&need, &[prototype.map_or(Value::UNDEFINED, Value::object)])?;
560 let shape = self.root_shape(prototype, kind);
561 let slots = self.values.alloc_slots(slot_capacity, &mut self.gc.room);
562 let elements = self.values.alloc(element_capacity, &mut self.gc.room);
563 let header = ObjectHeader::new(kind, shape, slots, elements, 0, flags::EXTENSIBLE);
564 Ok(ObjectId(self.objects.alloc(header, &mut self.gc.room)))
565 }
566
567 #[inline]
568 fn alloc_object_with(
569 &mut self,
570 kind: ObjectKind,
571 prototype: Option<ObjectId>,
572 slot_capacity: u32,
573 element_capacity: u32,
574 payload: u32,
575 extra: Need,
576 ) -> Result<ObjectId, HeapError> {
577 debug_assert!(kind != ObjectKind::Free);
578 let need = Need {
579 objects: 1,
580 shapes: 1,
581 values: [slot_capacity, element_capacity],
582 ..extra
583 };
584 let proto_value = prototype.map_or(Value::UNDEFINED, Value::object);
585 let payload_value = if kind == ObjectKind::Scope && payload != NONE {
586 Value::object(ObjectId(payload))
587 } else {
588 Value::UNDEFINED
589 };
590 self.safepoint(&need, &[proto_value, payload_value])?;
591 let mut object_flags = flags::EXTENSIBLE;
592 let shape = if kind == ObjectKind::GlobalObject {
593 object_flags |= flags::DICTIONARY;
594 self.dictionary_shape(prototype, kind, Dictionary::default())
595 } else {
596 self.root_shape(prototype, kind)
597 };
598 let slots = self.values.alloc_slots(slot_capacity, &mut self.gc.room);
599 let elements = self.values.alloc(element_capacity, &mut self.gc.room);
600 if kind == ObjectKind::Function {
601 object_flags |= flags::CALLABLE;
602 }
603 let header = ObjectHeader::new(kind, shape, slots, elements, payload, object_flags);
604 Ok(ObjectId(self.objects.alloc(header, &mut self.gc.room)))
605 }
606
607 pub fn object_kind(&self, object: ObjectId) -> ObjectKind {
608 self.objects.get(object.0).kind
609 }
610
611 pub fn header(&self, object: ObjectId) -> &ObjectHeader {
612 self.objects.get(object.0)
613 }
614
615 pub fn alloc_object_with_elements(
618 &mut self,
619 kind: ObjectKind,
620 prototype: Option<ObjectId>,
621 slot_capacity: u32,
622 element_capacity: u32,
623 ) -> Result<ObjectId, HeapError> {
624 debug_assert!(matches!(
625 kind,
626 ObjectKind::Ordinary | ObjectKind::Arguments | ObjectKind::ForInIterator
627 ));
628 self.alloc_plain(kind, prototype, slot_capacity, element_capacity)
629 }
630
631 pub(crate) fn set_payload(&mut self, object: ObjectId, payload: u32) {
635 self.objects.get_mut(object.0).payload = payload;
636 }
637
638 pub(crate) fn set_raw_slot(&mut self, object: ObjectId, slot: u32, value: Value) {
641 let slots = self.objects.get(object.0).slots();
642 self.values.set(slots, slot, value);
643 }
644
645 pub(crate) fn raw_slot(&self, object: ObjectId, slot: u32) -> Value {
646 self.values.get(self.objects.get(object.0).slots(), slot)
647 }
648
649 pub fn alloc_array(
653 &mut self,
654 prototype: Option<ObjectId>,
655 capacity: u32,
656 ) -> Result<ObjectId, HeapError> {
657 self.alloc_object_with(
658 ObjectKind::Array,
659 prototype,
660 0,
661 capacity,
662 0,
663 Need::default(),
664 )
665 }
666
667 pub fn array_length(&self, array: ObjectId) -> u32 {
668 let header = self.objects.get(array.0);
669 debug_assert_eq!(header.kind, ObjectKind::Array);
670 header.payload
671 }
672
673 #[inline]
675 pub fn get_element(&self, object: ObjectId, index: u32) -> Value {
676 let header = self.objects.get(object.0);
677 let elements = header.elements();
678 if index < elements.len && (header.kind != ObjectKind::Array || index < header.payload) {
679 self.values.get(elements, index)
680 } else {
681 Value::EMPTY
682 }
683 }
684
685 pub fn set_element(
690 &mut self,
691 object: ObjectId,
692 index: u32,
693 value: Value,
694 ) -> Result<(), HeapError> {
695 let header = *self.objects.get(object.0);
696 let mut elements = header.elements();
697 if index >= elements.len {
698 let grown = if elements.len < 1024 {
701 elements.len * 2
702 } else {
703 values::next_class_len(elements.len)
704 };
705 let new_len = grown.max(index + 1).max(4);
706 self.safepoint(
707 &Need {
708 values: [new_len, 0],
709 ..Need::default()
710 },
711 &[Value::object(object), value],
712 )?;
713 elements = self.values.grow(elements, new_len, &mut self.gc.room);
714 self.objects.get_mut(object.0).set_elements(elements);
715 }
716 if header.kind == ObjectKind::Array
719 && (value.is_empty()
720 || index > header.payload
721 || (index > 0 && self.values.get(elements, index - 1).is_empty()))
722 {
723 self.holey.insert(object.0);
724 }
725 self.values.set(elements, index, value);
726 let header = self.objects.get_mut(object.0);
727 if header.kind == ObjectKind::Array && index >= header.payload {
728 header.payload = index + 1;
729 }
730 if header.has(flags::USED_AS_PROTOTYPE) {
731 self.holes_writable = false;
732 }
733 Ok(())
734 }
735
736 pub fn make_sparse(&mut self, object: ObjectId) -> Result<(), HeapError> {
740 let header = *self.objects.get(object.0);
741 if header.has(flags::SPARSE) {
742 return Ok(());
743 }
744 let elements = header.elements();
745 let count = if header.kind == ObjectKind::Array {
746 header.payload.min(elements.len)
747 } else {
748 elements.len
749 };
750 let mut index = 0;
751 while index < count {
752 let value = self
753 .values
754 .get(self.objects.get(object.0).elements(), index);
755 if !value.is_empty() {
756 let key = PropertyKey::index(index).ok_or(HeapError::OutOfMemory)?;
757 self.add_property(object, key, crate::shape::PropertyFlags::DEFAULT, value)?;
760 }
761 index += 1;
762 }
763 let header = self.objects.get_mut(object.0);
764 let elements = header.elements();
765 header.set_elements(Range::EMPTY);
766 header.flags |= flags::SPARSE;
767 self.values.free(elements);
768 Ok(())
769 }
770
771 #[inline]
774 pub(crate) fn note_prototype_index(&mut self, object: ObjectId) {
775 if self.holes_writable && self.objects.get(object.0).has(flags::USED_AS_PROTOTYPE) {
776 self.holes_writable = false;
777 }
778 }
779
780 #[inline]
782 pub fn holes_writable(&self) -> bool {
783 self.holes_writable
784 }
785
786 pub fn push_element(&mut self, array: ObjectId, value: Value) -> Result<(), HeapError> {
787 let length = self.array_length(array);
788 self.set_element(array, length, value)
789 }
790
791 pub fn set_array_length(&mut self, array: ObjectId, length: u32) {
794 let header = *self.objects.get(array.0);
795 debug_assert_eq!(header.kind, ObjectKind::Array);
796 let elements = header.elements();
797 let cut = length.min(elements.len)..header.payload.min(elements.len);
798 if !cut.is_empty() {
799 self.values.slice_mut(elements)[cut.start as usize..cut.end as usize]
800 .fill(Value::EMPTY);
801 }
802 self.objects.get_mut(array.0).payload = length;
803 }
804
805 pub fn alloc_scope(
810 &mut self,
811 parent: Option<ScopeId>,
812 slots: u32,
813 ) -> Result<ScopeId, HeapError> {
814 let payload = parent.map_or(NONE, |scope| scope.0);
815 let object =
816 self.alloc_object_with(ObjectKind::Scope, None, slots, 0, payload, Need::default())?;
817 Ok(ScopeId(object.0))
818 }
819
820 pub fn scope_parent(&self, scope: ScopeId) -> Option<ScopeId> {
821 let header = self.objects.get(scope.0);
822 debug_assert_eq!(header.kind, ObjectKind::Scope);
823 (header.payload != NONE).then_some(ScopeId(header.payload))
824 }
825
826 #[inline]
827 fn scope_at(&self, mut scope: ScopeId, depth: u32) -> &ObjectHeader {
828 for _ in 0..depth {
829 scope = ScopeId(self.objects.get(scope.0).payload);
830 }
831 self.objects.get(scope.0)
832 }
833
834 #[inline]
836 pub fn scope_get(&self, scope: ScopeId, depth: u32, index: u32) -> Value {
837 self.values.get(self.scope_at(scope, depth).slots(), index)
838 }
839
840 #[inline]
841 pub fn scope_set(&mut self, scope: ScopeId, depth: u32, index: u32, value: Value) {
842 let slots = self.scope_at(scope, depth).slots();
843 self.values.set(slots, index, value);
844 }
845
846 pub fn alloc_function(
848 &mut self,
849 prototype: Option<ObjectId>,
850 scope: Option<ScopeId>,
851 ) -> Result<ObjectId, HeapError> {
852 self.alloc_function_with(prototype, scope, 2)
853 }
854
855 pub fn alloc_function_with(
858 &mut self,
859 prototype: Option<ObjectId>,
860 scope: Option<ScopeId>,
861 slot_capacity: u32,
862 ) -> Result<ObjectId, HeapError> {
863 let scope_value = scope.map_or(Value::UNDEFINED, |scope| Value::object(scope.object()));
864 let mark = self.roots.stack.mark();
865 self.roots.stack.push(scope_value);
866 let need = Need {
867 functions: 1,
868 ..Need::default()
869 };
870 let result = self.alloc_object_with(
871 ObjectKind::Function,
872 prototype,
873 slot_capacity,
874 0,
875 NONE,
876 need,
877 );
878 self.roots.stack.truncate(mark);
879 let object = result?;
880 let record = FunctionRecord {
881 scope: scope.map_or(NONE, |scope| scope.0),
882 bytecode: NONE,
883 native: NONE,
884 home_object: NONE,
885 fields: NONE,
886 };
887 let payload = self.payloads.functions.alloc(record, &mut self.gc.room);
888 self.objects.get_mut(object.0).payload = payload;
889 Ok(object)
890 }
891
892 pub(crate) fn function_record_mut(&mut self, function: ObjectId) -> &mut FunctionRecord {
893 let header = self.objects.get(function.0);
894 debug_assert_eq!(header.kind, ObjectKind::Function);
895 self.payloads.functions.get_mut(header.payload)
896 }
897
898 pub(crate) fn set_function_scope(&mut self, function: ObjectId, scope: ScopeId) {
901 let header = self.objects.get(function.0);
902 debug_assert_eq!(header.kind, ObjectKind::Function);
903 let payload = header.payload;
904 self.payloads.functions.get_mut(payload).scope = scope.0;
905 }
906
907 pub fn function_scope(&self, function: ObjectId) -> Option<ScopeId> {
908 let header = self.objects.get(function.0);
909 debug_assert_eq!(header.kind, ObjectKind::Function);
910 let scope = self.payloads.functions.get(header.payload).scope;
911 (scope != NONE).then_some(ScopeId(scope))
912 }
913
914 pub fn alloc_bytecode_function(
919 &mut self,
920 prototype: Option<ObjectId>,
921 scope: Option<ScopeId>,
922 tree: &[BytecodeTemplate<'_>],
923 ) -> Result<ObjectId, HeapError> {
924 assert!(!tree.is_empty(), "a bytecode tree has a root");
925 let mark = self.roots.stack.mark();
926 for template in tree {
927 for &constant in template.constants {
928 self.roots.stack.push(constant);
929 }
930 }
931 let result = self.alloc_bytecode_function_rooted(prototype, scope, tree);
932 self.roots.stack.truncate(mark);
933 result
934 }
935
936 fn alloc_bytecode_function_rooted(
937 &mut self,
938 prototype: Option<ObjectId>,
939 scope: Option<ScopeId>,
940 tree: &[BytecodeTemplate<'_>],
941 ) -> Result<ObjectId, HeapError> {
942 let function = self.alloc_function(prototype, scope)?;
943 let count = |len: usize| u32::try_from(len).map_err(|_| HeapError::OutOfMemory);
944 let mut constant_slots = 0u32;
945 let mut child_slots = 0u32;
946 for template in tree {
947 let constants = values::class_len(count(template.constants.len())?);
948 let children = values::class_len(count(template.children.len())?);
949 constant_slots = constant_slots
950 .checked_add(constants)
951 .ok_or(HeapError::OutOfMemory)?;
952 child_slots = child_slots
953 .checked_add(children)
954 .ok_or(HeapError::OutOfMemory)?;
955 }
956 self.safepoint(
959 &Need {
960 bytecodes: tree.len(),
961 values: [constant_slots, child_slots],
962 ..Need::default()
963 },
964 &[Value::object(function)],
965 )?;
966 let mut ids = Vec::with_capacity(tree.len());
967 for template in tree {
968 let constant_count = template.constants.len() as u32;
969 let constants = self.values.alloc(constant_count, &mut self.gc.room);
970 self.values.slice_mut(constants)[..template.constants.len()]
971 .copy_from_slice(template.constants);
972 let record = FunctionBytecode {
973 unit: template.unit.0,
974 constants,
975 constant_count,
976 children: Range::EMPTY,
977 child_count: 0,
978 };
979 ids.push(self.payloads.bytecode.alloc(record, &mut self.gc.room));
980 }
981 for (template, &id) in tree.iter().zip(&ids) {
982 let child_count = template.children.len() as u32;
983 let children = self.values.alloc(child_count, &mut self.gc.room);
984 for (slot, &child) in template.children.iter().enumerate() {
985 let child = Value::int32(ids[child as usize] as i32);
986 self.values.set(children, slot as u32, child);
987 }
988 let record = self.payloads.bytecode.get_mut(id);
989 record.children = children;
990 record.child_count = child_count;
991 }
992 let payload = self.objects.get(function.0).payload;
993 self.payloads.functions.get_mut(payload).bytecode = ids[0];
994 Ok(function)
995 }
996
997 pub fn alloc_closure(
1002 &mut self,
1003 prototype: Option<ObjectId>,
1004 scope: Option<ScopeId>,
1005 bytecode: BytecodeId,
1006 ) -> Result<ObjectId, HeapError> {
1007 let function = self.alloc_function_with(prototype, scope, 0)?;
1008 debug_assert!(self.payloads.bytecode.is_live(bytecode.0));
1009 let payload = self.objects.get(function.0).payload;
1010 self.payloads.functions.get_mut(payload).bytecode = bytecode.0;
1011 Ok(function)
1012 }
1013
1014 pub fn alloc_native_function(
1016 &mut self,
1017 prototype: Option<ObjectId>,
1018 native: u32,
1019 ) -> Result<ObjectId, HeapError> {
1020 self.alloc_native_closure(prototype, native, None)
1021 }
1022
1023 pub fn alloc_native_closure(
1027 &mut self,
1028 prototype: Option<ObjectId>,
1029 native: u32,
1030 data: Option<ObjectId>,
1031 ) -> Result<ObjectId, HeapError> {
1032 let function = self.alloc_function(prototype, data.map(|data| ScopeId(data.0)))?;
1033 let payload = self.objects.get(function.0).payload;
1034 self.payloads.functions.get_mut(payload).native = native;
1035 Ok(function)
1036 }
1037
1038 pub(crate) fn apply_closure_shape(
1041 &mut self,
1042 function: ObjectId,
1043 shape: ShapeId,
1044 values: &[Value],
1045 ) {
1046 let header = self.objects.get_mut(function.0);
1047 debug_assert_eq!(self.shapes.get(shape.0).width as usize, values.len());
1048 debug_assert!(header.slots().len as usize >= values.len());
1049 header.shape = shape;
1050 let slots = header.slots();
1051 for (slot, &value) in values.iter().enumerate() {
1052 self.values.set(slots, slot as u32, value);
1053 }
1054 }
1055
1056 pub(crate) fn apply_pending_shape(&mut self, function: ObjectId, shape: ShapeId) {
1059 let header = self.objects.get_mut(function.0);
1060 debug_assert_eq!(header.slots().len, 0);
1061 header.shape = shape;
1062 }
1063
1064 #[inline]
1066 pub fn function_record(&self, function: ObjectId) -> FunctionRecord {
1067 let header = self.objects.get(function.0);
1068 debug_assert_eq!(header.kind, ObjectKind::Function);
1069 *self.payloads.functions.get(header.payload)
1070 }
1071
1072 pub fn function_bytecode(&self, function: ObjectId) -> Option<BytecodeId> {
1073 let header = self.objects.get(function.0);
1074 debug_assert_eq!(header.kind, ObjectKind::Function);
1075 let bytecode = self.payloads.functions.get(header.payload).bytecode;
1076 (bytecode != NONE).then_some(BytecodeId(bytecode))
1077 }
1078
1079 #[inline]
1080 pub fn bytecode_unit(&self, bytecode: BytecodeId) -> CodeUnitId {
1081 CodeUnitId(self.payloads.bytecode.get(bytecode.0).unit)
1082 }
1083
1084 pub fn bytecode_constants(&self, bytecode: BytecodeId) -> &[Value] {
1085 let record = self.payloads.bytecode.get(bytecode.0);
1086 &self.values.slice(record.constants)[..record.constant_count as usize]
1087 }
1088
1089 #[inline]
1090 pub fn bytecode_constant(&self, bytecode: BytecodeId, index: u32) -> Value {
1091 let record = self.payloads.bytecode.get(bytecode.0);
1092 debug_assert!(index < record.constant_count);
1093 self.values.get(record.constants, index)
1094 }
1095
1096 #[inline]
1098 pub fn bytecode_child(&self, bytecode: BytecodeId, index: u32) -> BytecodeId {
1099 let record = self.payloads.bytecode.get(bytecode.0);
1100 debug_assert!(index < record.child_count);
1101 let child = self.values.get(record.children, index);
1102 BytecodeId(child.as_int32().expect("child link") as u32)
1103 }
1104
1105 pub fn live_bytecodes(&self) -> usize {
1106 self.payloads.bytecode.live_count()
1107 }
1108
1109 fn alloc_string_record(&mut self, record: StringRecord) -> Result<StringId, HeapError> {
1112 let need = Need {
1113 strings: 1,
1114 extra: record.flat().map_or(0, StringRepr::content_bytes),
1115 ..Need::default()
1116 };
1117 self.safepoint(&need, &[])?;
1118 Ok(StringId(self.strings.alloc(record, &mut self.gc.room)))
1119 }
1120
1121 pub fn alloc_string_repr(&mut self, repr: StringRepr) -> Result<StringId, HeapError> {
1123 self.alloc_string_record(StringRecord::new(repr))
1124 }
1125
1126 pub(crate) fn set_object_flag(&mut self, object: ObjectId, flag: u8) {
1128 self.objects.get_mut(object.0).flags |= flag;
1129 }
1130
1131 pub(crate) fn clear_object_flag(&mut self, object: ObjectId, flag: u8) {
1133 self.objects.get_mut(object.0).flags &= !flag;
1134 }
1135
1136 pub fn alloc_string(&mut self, text: &str) -> Result<StringId, HeapError> {
1138 self.alloc_string_record(StringRecord::new(StringRepr::from_text(text)))
1139 }
1140
1141 pub fn alloc_string_units(&mut self, units: &[u16]) -> Result<StringId, HeapError> {
1142 self.alloc_string_record(StringRecord::new(StringRepr::from_units(units)))
1143 }
1144
1145 #[inline]
1147 pub fn string(&self, id: StringId) -> &StringRecord {
1148 let record = self.strings.get(id.0);
1149 if record.flat().is_none() {
1150 self.flatten(id, record);
1151 }
1152 record
1153 }
1154
1155 #[inline]
1157 pub fn string_len(&self, id: StringId) -> usize {
1158 self.strings.get(id.0).len()
1159 }
1160
1161 #[inline]
1163 pub fn string_is_one_byte(&self, id: StringId) -> bool {
1164 self.strings.get(id.0).is_one_byte()
1165 }
1166
1167 #[cold]
1170 #[inline(never)]
1171 fn flatten(&self, id: StringId, record: &StringRecord) {
1172 let halves = |id: StringId| self.ropes.get(&id.0).copied();
1173 let (left, right) = halves(id).expect("a rope");
1174 let mut stack = vec![right, left];
1175 let repr = if record.is_one_byte() {
1176 let mut bytes = Vec::with_capacity(record.len());
1177 while let Some(id) = stack.pop() {
1178 let part = self.strings.get(id.0);
1179 match (part.flat(), halves(id)) {
1180 (Some(StringRepr::OneByte(flat)), _) => bytes.extend_from_slice(flat),
1181 (Some(StringRepr::TwoByte(_)), _) => unreachable!("a one-byte rope"),
1182 (None, Some((left, right))) => {
1183 stack.push(right);
1184 stack.push(left);
1185 }
1186 (None, None) => unreachable!("a string with neither units nor halves"),
1187 }
1188 }
1189 StringRepr::OneByte(bytes.into())
1190 } else {
1191 let mut units = Vec::with_capacity(record.len());
1192 while let Some(id) = stack.pop() {
1193 let part = self.strings.get(id.0);
1194 match (part.flat(), halves(id)) {
1195 (Some(flat), _) => units.extend(flat.units()),
1196 (None, Some((left, right))) => {
1197 stack.push(right);
1198 stack.push(left);
1199 }
1200 (None, None) => unreachable!("a string with neither units nor halves"),
1201 }
1202 }
1203 StringRepr::TwoByte(units.into())
1204 };
1205 self.rope_bytes
1206 .set(self.rope_bytes.get() + repr.content_bytes());
1207 record.set_flat(repr);
1208 }
1209
1210 pub fn alloc_rope(&mut self, left: StringId, right: StringId) -> Result<StringId, HeapError> {
1212 let len = self.string_len(left) + self.string_len(right);
1213 let one_byte = self.string_is_one_byte(left) && self.string_is_one_byte(right);
1214 let need = Need {
1215 strings: 1,
1216 ..Need::default()
1217 };
1218 self.safepoint(&need, &[Value::string(left), Value::string(right)])?;
1219 let record = StringRecord::rope(len as u32, one_byte);
1220 let id = self.strings.alloc(record, &mut self.gc.room);
1221 self.ropes.insert(id, (left, right));
1222 Ok(StringId(id))
1223 }
1224
1225 pub fn string_text(&self, id: StringId) -> String {
1226 self.string(id).repr().to_rust_string()
1227 }
1228
1229 fn intern_repr(&mut self, repr: StringRepr) -> Result<StringId, HeapError> {
1230 let hash = repr.content_hash();
1231 if let Some(id) = self.atoms.find(&self.strings, &repr, hash) {
1232 self.retain_atom(id);
1233 return Ok(id);
1234 }
1235 self.insert_atom(repr, hash)
1236 }
1237
1238 fn insert_atom(&mut self, repr: StringRepr, hash: u32) -> Result<StringId, HeapError> {
1240 let array_index = repr.array_index().unwrap_or(NONE);
1241 let mut record = StringRecord::new(repr);
1242 record.is_atom = true;
1243 record.array_index = array_index;
1244 let id = self.alloc_string_record(record)?;
1245 if id.0 >= KEY_INDEX_LIMIT {
1246 self.strings.free(id.0);
1247 return Err(HeapError::OutOfMemory);
1248 }
1249 self.atoms.insert(hash, id);
1250 self.retain_atom(id);
1251 Ok(id)
1252 }
1253
1254 #[inline]
1256 fn retain_atom(&mut self, id: StringId) {
1257 match self.atom_mode {
1258 AtomMode::Weak => {}
1259 AtomMode::Hold => self.hold_atom(id),
1260 AtomMode::Pin => self.pin_atom(id),
1261 }
1262 }
1263
1264 fn hold_atom(&mut self, id: StringId) {
1267 let record = self.strings.get_mut(id.0);
1268 if record.keep == AtomKeep::Weak {
1269 record.keep = AtomKeep::Held;
1270 self.held_atoms.push(id);
1271 }
1272 }
1273
1274 pub fn pin_atom(&mut self, id: StringId) {
1276 let record = self.strings.get_mut(id.0);
1277 debug_assert!(record.is_atom, "pinning a string that is not an atom");
1278 if record.keep != AtomKeep::Pinned {
1279 record.keep = AtomKeep::Pinned;
1280 self.pinned_atoms.push(id);
1281 }
1282 }
1283
1284 pub fn set_atom_mode(&mut self, mode: AtomMode) -> AtomMode {
1286 std::mem::replace(&mut self.atom_mode, mode)
1287 }
1288
1289 pub fn atom_mode(&self) -> AtomMode {
1290 self.atom_mode
1291 }
1292
1293 pub fn intern_pinned(&mut self, text: &str) -> Result<StringId, HeapError> {
1296 let mode = self.set_atom_mode(AtomMode::Pin);
1297 let result = self.intern(text);
1298 self.atom_mode = mode;
1299 result
1300 }
1301
1302 pub fn property_key_pinned(&mut self, text: &str) -> Result<PropertyKey, HeapError> {
1305 let mode = self.set_atom_mode(AtomMode::Pin);
1306 let result = self.property_key(text);
1307 self.atom_mode = mode;
1308 result
1309 }
1310
1311 pub(crate) fn held_atoms_mark(&self) -> usize {
1313 self.held_atoms.len()
1314 }
1315
1316 pub(crate) fn release_held_atoms(&mut self, mark: usize) {
1317 for position in mark..self.held_atoms.len() {
1318 let id = self.held_atoms[position];
1319 let record = self.strings.get_mut(id.0);
1320 if record.keep == AtomKeep::Held {
1321 record.keep = AtomKeep::Weak;
1322 }
1323 }
1324 self.held_atoms.truncate(mark);
1325 }
1326
1327 pub fn intern(&mut self, text: &str) -> Result<StringId, HeapError> {
1329 self.intern_repr(StringRepr::from_text(text))
1330 }
1331
1332 pub fn intern_units(&mut self, units: &[u16]) -> Result<StringId, HeapError> {
1333 self.intern_repr(StringRepr::from_units(units))
1334 }
1335
1336 pub fn intern_latin1(&mut self, bytes: &[u8]) -> Result<StringId, HeapError> {
1339 let hash = string::content_hash(bytes.iter().map(|&byte| u16::from(byte)));
1340 if let Some(id) = self.atoms.find_one_byte(&self.strings, bytes, hash) {
1341 self.retain_atom(id);
1342 return Ok(id);
1343 }
1344 self.insert_atom(StringRepr::OneByte(bytes.into()), hash)
1345 }
1346
1347 pub fn intern_string(&mut self, string: StringId) -> Result<StringId, HeapError> {
1349 let record = self.string(string);
1350 if record.is_atom {
1351 self.retain_atom(string);
1352 return Ok(string);
1353 }
1354 let repr = record.repr().clone();
1355 self.intern_repr(repr)
1356 }
1357
1358 fn key_for_repr(&mut self, repr: StringRepr) -> Result<PropertyKey, HeapError> {
1359 if let Some(key) = repr.array_index().and_then(PropertyKey::index) {
1360 return Ok(key);
1361 }
1362 Ok(PropertyKey::atom(self.intern_repr(repr)?))
1363 }
1364
1365 pub fn property_key(&mut self, text: &str) -> Result<PropertyKey, HeapError> {
1369 self.key_for_repr(StringRepr::from_text(text))
1370 }
1371
1372 pub fn property_key_units(&mut self, units: &[u16]) -> Result<PropertyKey, HeapError> {
1373 self.key_for_repr(StringRepr::from_units(units))
1374 }
1375
1376 pub fn property_key_latin1(&mut self, bytes: &[u8]) -> Result<PropertyKey, HeapError> {
1378 let units = || bytes.iter().map(|&byte| u16::from(byte));
1379 if let Some(key) = string::array_index(bytes.len(), units()).and_then(PropertyKey::index) {
1380 return Ok(key);
1381 }
1382 Ok(PropertyKey::atom(self.intern_latin1(bytes)?))
1383 }
1384
1385 pub fn alloc_symbol(
1386 &mut self,
1387 description: Option<StringId>,
1388 private: bool,
1389 ) -> Result<SymbolId, HeapError> {
1390 let keep = description.map_or(Value::UNDEFINED, Value::string);
1391 self.safepoint(
1392 &Need {
1393 symbols: 1,
1394 ..Need::default()
1395 },
1396 &[keep],
1397 )?;
1398 let record = SymbolRecord {
1399 description: description.map_or(NONE, |id| id.0),
1400 private,
1401 };
1402 let id = self.symbols.alloc(record, &mut self.gc.room);
1403 if id >= KEY_INDEX_LIMIT {
1404 self.symbols.free(id);
1405 return Err(HeapError::OutOfMemory);
1406 }
1407 Ok(SymbolId(id))
1408 }
1409
1410 pub fn symbol(&self, id: SymbolId) -> &SymbolRecord {
1411 self.symbols.get(id.0)
1412 }
1413
1414 pub fn symbol_description(&self, id: SymbolId) -> Option<StringId> {
1415 let description = self.symbols.get(id.0).description;
1416 (description != NONE).then_some(StringId(description))
1417 }
1418
1419 pub fn alloc_bigint(&mut self, negative: bool, limbs: &[u64]) -> Result<BigIntId, HeapError> {
1420 let used = limbs.len() - limbs.iter().rev().take_while(|&&limb| limb == 0).count();
1421 let limbs: Box<[u64]> = limbs[..used].into();
1422 let need = Need {
1423 bigints: 1,
1424 extra: limbs.len() * 8,
1425 ..Need::default()
1426 };
1427 self.safepoint(&need, &[])?;
1428 let record = BigIntRecord {
1429 negative: negative && !limbs.is_empty(),
1430 limbs,
1431 };
1432 Ok(BigIntId(self.bigints.alloc(record, &mut self.gc.room)))
1433 }
1434
1435 pub fn bigint(&self, id: BigIntId) -> &BigIntRecord {
1436 self.bigints.get(id.0)
1437 }
1438}