1use std::cell::OnceCell;
21use std::fmt;
22
23use crate::heap::arena::Record;
24use crate::heap::atom::{KeyKind, PropertyKey};
25use crate::heap::hash::FxHashMap;
26use crate::heap::object::{NONE, ObjectKind, flags};
27use crate::heap::{Heap, HeapError, Need};
28use crate::value::{ObjectId, ShapeId, ValidityCellId, Value};
29
30pub const TABLE_THRESHOLD: u32 = 8;
32pub const DICTIONARY_THRESHOLD: u32 = 64;
34pub const STATIC_CELL: ValidityCellId = ValidityCellId(0);
36
37#[derive(Clone, Copy, PartialEq, Eq, Hash)]
38pub struct PropertyFlags(u8);
39
40impl PropertyFlags {
41 pub const WRITABLE: u8 = 1 << 0;
42 pub const ENUMERABLE: u8 = 1 << 1;
43 pub const CONFIGURABLE: u8 = 1 << 2;
44 pub const ACCESSOR: u8 = 1 << 3;
45 pub const AUTOINIT: u8 = 1 << 4;
50 pub const DEFAULT: PropertyFlags = PropertyFlags::data(true, true, true);
52
53 pub const fn data(writable: bool, enumerable: bool, configurable: bool) -> Self {
54 PropertyFlags(
55 (writable as u8 * Self::WRITABLE)
56 | (enumerable as u8 * Self::ENUMERABLE)
57 | (configurable as u8 * Self::CONFIGURABLE),
58 )
59 }
60
61 pub const fn accessor(enumerable: bool, configurable: bool) -> Self {
62 PropertyFlags(
63 Self::ACCESSOR
64 | (enumerable as u8 * Self::ENUMERABLE)
65 | (configurable as u8 * Self::CONFIGURABLE),
66 )
67 }
68
69 pub const fn bits(self) -> u8 {
70 self.0
71 }
72
73 pub const fn has(self, flag: u8) -> bool {
74 self.0 & flag != 0
75 }
76
77 pub const fn with(self, flag: u8) -> Self {
78 PropertyFlags(self.0 | flag)
79 }
80
81 pub const fn width(self) -> u32 {
84 if self.has(Self::ACCESSOR) { 2 } else { 1 }
85 }
86}
87
88impl fmt::Debug for PropertyFlags {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 let flag = |bit, name| if self.has(bit) { name } else { "-" };
91 write!(
92 f,
93 "{}{}{}{}{}",
94 flag(Self::AUTOINIT, "i"),
95 flag(Self::ACCESSOR, "a"),
96 flag(Self::WRITABLE, "w"),
97 flag(Self::ENUMERABLE, "e"),
98 flag(Self::CONFIGURABLE, "c")
99 )
100 }
101}
102
103#[derive(Default)]
106pub enum Transitions {
107 #[default]
108 None,
109 Small(Vec<(PropertyKey, PropertyFlags, ShapeId)>),
110 Map(Box<FxHashMap<(PropertyKey, PropertyFlags), ShapeId>>),
111}
112
113const SMALL_TRANSITIONS: usize = 4;
114
115impl Transitions {
116 #[inline]
117 pub fn find(&self, key: PropertyKey, flags: PropertyFlags) -> Option<ShapeId> {
118 match self {
119 Transitions::None => None,
120 Transitions::Small(entries) => entries
121 .iter()
122 .find(|(entry_key, entry_flags, _)| *entry_key == key && *entry_flags == flags)
123 .map(|entry| entry.2),
124 Transitions::Map(map) => map.get(&(key, flags)).copied(),
125 }
126 }
127
128 fn insert(&mut self, key: PropertyKey, flags: PropertyFlags, child: ShapeId) {
129 match self {
130 Transitions::None => *self = Transitions::Small(vec![(key, flags, child)]),
131 Transitions::Small(entries) if entries.len() < SMALL_TRANSITIONS => {
132 entries.push((key, flags, child))
133 }
134 Transitions::Small(entries) => {
135 let mut map: FxHashMap<_, _> =
136 entries.drain(..).map(|(k, f, c)| ((k, f), c)).collect();
137 map.insert((key, flags), child);
138 *self = Transitions::Map(Box::new(map));
139 }
140 Transitions::Map(map) => {
141 map.insert((key, flags), child);
142 }
143 }
144 }
145
146 pub fn retain(&mut self, mut keep: impl FnMut(ShapeId) -> bool) {
147 match self {
148 Transitions::None => {}
149 Transitions::Small(entries) => entries.retain(|entry| keep(entry.2)),
150 Transitions::Map(map) => map.retain(|_, child| keep(*child)),
151 }
152 }
153
154 pub fn len(&self) -> usize {
155 match self {
156 Transitions::None => 0,
157 Transitions::Small(entries) => entries.len(),
158 Transitions::Map(map) => map.len(),
159 }
160 }
161
162 pub fn is_empty(&self) -> bool {
163 self.len() == 0
164 }
165}
166
167#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168pub struct DictEntry {
169 pub key: PropertyKey,
170 pub flags: PropertyFlags,
171 pub slot: u32,
173}
174
175#[derive(Default)]
177pub struct Dictionary {
178 map: FxHashMap<PropertyKey, u32>,
179 entries: Vec<DictEntry>,
180 free_slots: Vec<u32>,
181 next_slot: u32,
182}
183
184impl Dictionary {
185 fn from_entries(entries: impl IntoIterator<Item = (PropertyKey, PropertyFlags, u32)>) -> Self {
186 let mut dictionary = Dictionary::default();
187 for (key, flags, slot) in entries {
188 dictionary.map.insert(key, dictionary.entries.len() as u32);
189 dictionary.entries.push(DictEntry { key, flags, slot });
190 dictionary.next_slot = dictionary.next_slot.max(slot + flags.width());
191 }
192 dictionary
193 }
194
195 #[inline]
196 pub fn find(&self, key: PropertyKey) -> Option<(u32, PropertyFlags)> {
197 let entry = &self.entries[*self.map.get(&key)? as usize];
198 Some((entry.slot, entry.flags))
199 }
200
201 fn next_slot(&self, flags: PropertyFlags) -> u32 {
204 match self.free_slots.last() {
205 Some(&slot) if flags.width() == 1 => slot,
206 _ => self.next_slot,
207 }
208 }
209
210 fn insert(&mut self, key: PropertyKey, flags: PropertyFlags) -> u32 {
211 let slot = match self.free_slots.pop() {
212 Some(slot) if flags.width() == 1 => slot,
213 free => {
214 self.free_slots.extend(free);
215 self.next_slot += flags.width();
216 self.next_slot - flags.width()
217 }
218 };
219 self.map.insert(key, self.entries.len() as u32);
220 self.entries.push(DictEntry { key, flags, slot });
221 slot
222 }
223
224 fn remove(&mut self, key: PropertyKey) -> Option<u32> {
225 let index = self.map.remove(&key)?;
226 let entry = &mut self.entries[index as usize];
227 let slot = entry.slot;
228 entry.slot = NONE;
229 self.free_slots.extend(slot..slot + entry.flags.width());
230 if self.entries.len() > 16 && self.entries.len() > 2 * self.map.len() {
231 self.entries.retain(|entry| entry.slot != NONE);
232 for (index, entry) in self.entries.iter().enumerate() {
233 self.map.insert(entry.key, index as u32);
234 }
235 }
236 Some(slot)
237 }
238
239 fn reslot(&mut self, key: PropertyKey, flags: PropertyFlags) -> Option<u32> {
243 let index = *self.map.get(&key)? as usize;
244 let old = self.entries[index];
245 self.free_slots
246 .extend(old.slot..old.slot + old.flags.width());
247 let slot = match self.free_slots.pop() {
248 Some(slot) if flags.width() == 1 => slot,
249 free => {
250 self.free_slots.extend(free);
251 self.next_slot += flags.width();
252 self.next_slot - flags.width()
253 }
254 };
255 self.entries[index] = DictEntry { key, flags, slot };
256 Some(slot)
257 }
258
259 fn set_flags(&mut self, key: PropertyKey, flags: PropertyFlags) -> bool {
260 match self.map.get(&key) {
261 Some(&index) => {
262 self.entries[index as usize].flags = flags;
263 true
264 }
265 None => false,
266 }
267 }
268
269 pub fn entries(&self) -> impl Iterator<Item = &DictEntry> {
271 self.entries.iter().filter(|entry| entry.slot != NONE)
272 }
273
274 pub fn len(&self) -> usize {
275 self.map.len()
276 }
277
278 pub fn is_empty(&self) -> bool {
279 self.map.is_empty()
280 }
281
282 fn reorder(&mut self, front: &[PropertyKey]) {
286 let mut entries = Vec::with_capacity(self.map.len());
287 for &key in front {
288 if let Some(&index) = self.map.get(&key) {
289 entries.push(self.entries[index as usize]);
290 }
291 }
292 for entry in &self.entries {
293 if entry.slot != NONE && !front.contains(&entry.key) {
294 entries.push(*entry);
295 }
296 }
297 self.map.clear();
298 for (index, entry) in entries.iter().enumerate() {
299 self.map.insert(entry.key, index as u32);
300 }
301 self.entries = entries;
302 }
303}
304
305pub struct Shape {
306 pub parent: Option<ShapeId>,
308 pub key: PropertyKey,
310 pub flags: PropertyFlags,
311 pub slot: u32,
312 pub count: u32,
314 pub width: u32,
317 pub prototype: Option<ObjectId>,
318 pub kind: ObjectKind,
319 pub transitions: Transitions,
320 pub table: OnceCell<Box<FxHashMap<PropertyKey, (u32, PropertyFlags)>>>,
323 pub validity: ValidityCellId,
325 pub dictionary: Option<Box<Dictionary>>,
326}
327
328impl Shape {
329 fn root(prototype: Option<ObjectId>, kind: ObjectKind) -> Self {
330 Shape {
331 parent: None,
332 key: PropertyKey::index(0).unwrap(),
333 flags: PropertyFlags::DEFAULT,
334 slot: NONE,
335 count: 0,
336 width: 0,
337 prototype,
338 kind,
339 transitions: Transitions::None,
340 table: OnceCell::new(),
341 validity: ValidityCellId(NONE),
342 dictionary: None,
343 }
344 }
345
346 pub fn is_dictionary(&self) -> bool {
347 self.dictionary.is_some()
348 }
349
350 pub fn has_table(&self) -> bool {
352 self.table.get().is_some()
353 }
354}
355
356impl Record for Shape {
357 fn vacant() -> Self {
358 Shape::root(None, ObjectKind::Free)
359 }
360}
361
362#[derive(Clone, Copy, Debug)]
363pub struct ValidityCell {
364 pub valid: bool,
365}
366
367impl Record for ValidityCell {
368 fn vacant() -> Self {
369 ValidityCell { valid: false }
370 }
371}
372
373#[derive(Default, Debug)]
376pub struct PrototypeInfo {
377 pub cell: Option<ValidityCellId>,
378 pub dependents: Vec<ObjectId>,
380}
381
382impl Heap {
383 #[inline]
386 pub(crate) fn root_shape(&mut self, prototype: Option<ObjectId>, kind: ObjectKind) -> ShapeId {
387 let key = (prototype.map_or(NONE, |p| p.0), kind);
388 if let Some((cached, shape)) = self.last_root
389 && cached == key
390 {
391 return shape;
392 }
393 self.root_shape_slow(prototype, kind, key)
394 }
395
396 #[inline(never)]
397 fn root_shape_slow(
398 &mut self,
399 prototype: Option<ObjectId>,
400 kind: ObjectKind,
401 key: (u32, ObjectKind),
402 ) -> ShapeId {
403 if let Some(&shape) = self.shape_roots.get(&key) {
404 self.last_root = Some((key, shape));
405 return shape;
406 }
407 if let Some(prototype) = prototype {
408 self.ensure_prototype(prototype);
409 }
410 let shape = ShapeId(
411 self.shapes
412 .alloc(Shape::root(prototype, kind), &mut self.gc.room),
413 );
414 self.shape_roots.insert(key, shape);
415 self.last_root = Some((key, shape));
416 shape
417 }
418
419 pub(crate) fn dictionary_shape(
420 &mut self,
421 prototype: Option<ObjectId>,
422 kind: ObjectKind,
423 dictionary: Dictionary,
424 ) -> ShapeId {
425 if let Some(prototype) = prototype {
426 self.ensure_prototype(prototype);
427 }
428 let mut shape = Shape::root(prototype, kind);
429 shape.dictionary = Some(Box::new(dictionary));
430 ShapeId(self.shapes.alloc(shape, &mut self.gc.room))
431 }
432
433 fn transition(&mut self, shape: ShapeId, key: PropertyKey, flags: PropertyFlags) -> ShapeId {
434 let parent = self.shapes.get(shape.0);
435 if let Some(child) = parent.transitions.find(key, flags) {
436 return child;
437 }
438 let mut child = Shape::root(parent.prototype, parent.kind);
439 child.parent = Some(shape);
440 child.key = key;
441 child.flags = flags;
442 child.slot = parent.width;
443 child.count = parent.count + 1;
444 child.width = parent.width + flags.width();
445 let child = ShapeId(self.shapes.alloc(child, &mut self.gc.room));
446 self.shapes
447 .get_mut(shape.0)
448 .transitions
449 .insert(key, flags, child);
450 child
451 }
452
453 fn build_table(&self, shape: ShapeId) -> FxHashMap<PropertyKey, (u32, PropertyFlags)> {
454 let mut current = self.shapes.get(shape.0);
455 let mut table = FxHashMap::default();
456 table.reserve(current.count as usize);
457 while current.count > 0 {
458 table.insert(current.key, (current.slot, current.flags));
459 current = self
460 .shapes
461 .get(current.parent.expect("non-root shape has a parent").0);
462 }
463 table
464 }
465
466 #[inline]
468 pub fn shape_lookup(&self, shape: ShapeId, key: PropertyKey) -> Option<(u32, PropertyFlags)> {
469 let record = self.shapes.get(shape.0);
470 if let Some(dictionary) = &record.dictionary {
471 return dictionary.find(key);
472 }
473 if record.count > TABLE_THRESHOLD {
474 return record
475 .table
476 .get_or_init(|| Box::new(self.build_table(shape)))
477 .get(&key)
478 .copied();
479 }
480 let mut current = record;
481 while current.count > 0 {
482 if current.key == key {
483 return Some((current.slot, current.flags));
484 }
485 current = self
486 .shapes
487 .get(current.parent.expect("non-root shape has a parent").0);
488 }
489 None
490 }
491
492 pub fn shape_entries(&self, shape: ShapeId) -> Vec<(PropertyKey, PropertyFlags, u32)> {
494 let record = self.shapes.get(shape.0);
495 if let Some(dictionary) = &record.dictionary {
496 return dictionary
497 .entries()
498 .map(|entry| (entry.key, entry.flags, entry.slot))
499 .collect();
500 }
501 let mut entries = Vec::with_capacity(record.count as usize);
502 let mut current = record;
503 while current.count > 0 {
504 entries.push((current.key, current.flags, current.slot));
505 current = self
506 .shapes
507 .get(current.parent.expect("non-root shape has a parent").0);
508 }
509 entries.reverse();
510 entries
511 }
512
513 pub fn shape(&self, shape: ShapeId) -> &Shape {
514 self.shapes.get(shape.0)
515 }
516
517 pub(crate) fn prototype_of_shape(&self, shape: ShapeId) -> Option<ObjectId> {
518 self.shapes.get(shape.0).prototype
519 }
520
521 pub fn shape_of(&self, object: ObjectId) -> ShapeId {
522 self.objects.get(object.0).shape
523 }
524
525 pub fn is_dictionary(&self, object: ObjectId) -> bool {
526 self.objects.get(object.0).has(flags::DICTIONARY)
527 }
528
529 pub fn prototype_of(&self, object: ObjectId) -> Option<ObjectId> {
530 self.shapes
531 .get(self.objects.get(object.0).shape.0)
532 .prototype
533 }
534
535 #[inline]
539 pub fn lookup_own(&self, object: ObjectId, key: PropertyKey) -> Option<(u32, PropertyFlags)> {
540 self.shape_lookup(self.objects.get(object.0).shape, key)
541 }
542
543 #[inline(always)]
547 pub fn is_pending_closure(&self, object: ObjectId) -> bool {
548 let header = self.objects.get(object.0);
549 header.kind == ObjectKind::Function
550 && header.slots().len < self.shapes.get(header.shape.0).width
551 }
552
553 pub(crate) fn pending_closure_shape(
557 &mut self,
558 function: ObjectId,
559 entries: &[(PropertyKey, PropertyFlags)],
560 ) -> Result<ShapeId, HeapError> {
561 debug_assert_eq!(self.objects.get(function.0).slots().len, 0);
562 let need = Need {
563 shapes: entries.len() + 1,
564 ..Need::default()
565 };
566 self.safepoint(&need, &[Value::object(function)])?;
567 let mut shape = self.root_shape(self.prototype_of(function), ObjectKind::Function);
568 for &(key, flags) in entries {
569 shape = self.transition(shape, key, flags.with(PropertyFlags::AUTOINIT));
570 }
571 self.objects.get_mut(function.0).shape = shape;
572 Ok(shape)
573 }
574
575 pub(crate) fn unpend_closure(
579 &mut self,
580 function: ObjectId,
581 count: u32,
582 ) -> Result<(), HeapError> {
583 debug_assert!(self.is_pending_closure(function));
584 let need = Need {
585 shapes: 1,
586 values: [count, 0],
587 ..Need::default()
588 };
589 self.safepoint(&need, &[Value::object(function)])?;
590 let shape = self.root_shape(self.prototype_of(function), ObjectKind::Function);
591 let slots = self.values.alloc_slots(count, &mut self.gc.room);
592 let header = self.objects.get_mut(function.0);
593 header.shape = shape;
594 header.set_slots(slots);
595 if header.has(flags::USED_AS_PROTOTYPE) {
596 self.invalidate(function);
597 }
598 Ok(())
599 }
600
601 #[inline]
603 #[track_caller]
604 pub fn get_slot(&self, object: ObjectId, slot: u32) -> Value {
605 let header = self.objects.get(object.0);
606 debug_assert!(
607 slot < header.slots().len,
608 "slot {slot} outside the object's range"
609 );
610 self.values.get_at(header.slots_start + slot)
611 }
612
613 #[inline]
614 #[track_caller]
615 pub fn set_slot(&mut self, object: ObjectId, slot: u32, value: Value) {
616 let header = self.objects.get(object.0);
617 debug_assert!(
618 slot < header.slots().len,
619 "slot {slot} outside the object's range"
620 );
621 self.values.set_at(header.slots_start + slot, value);
622 }
623
624 pub fn get_property(&self, object: ObjectId, key: PropertyKey) -> Option<Value> {
626 let mut current = object;
627 loop {
628 if let Some((slot, _)) = self.lookup_own(current, key) {
629 return Some(self.get_slot(current, slot));
630 }
631 current = self.prototype_of(current)?;
632 }
633 }
634
635 #[inline]
637 pub fn add_property(
638 &mut self,
639 object: ObjectId,
640 key: PropertyKey,
641 flags: PropertyFlags,
642 value: Value,
643 ) -> Result<(), HeapError> {
644 debug_assert!(!flags.has(PropertyFlags::ACCESSOR), "add_accessor");
645 self.add_slots(object, key, flags, [value, Value::UNDEFINED])
646 }
647
648 pub fn add_accessor(
652 &mut self,
653 object: ObjectId,
654 key: PropertyKey,
655 flags: PropertyFlags,
656 getter: Value,
657 setter: Value,
658 ) -> Result<(), HeapError> {
659 debug_assert!(flags.has(PropertyFlags::ACCESSOR));
660 self.add_slots(object, key, flags, [getter, setter])
661 }
662
663 #[inline]
664 fn add_slots(
665 &mut self,
666 object: ObjectId,
667 key: PropertyKey,
668 flags: PropertyFlags,
669 values: [Value; 2],
670 ) -> Result<(), HeapError> {
671 if let KeyKind::Index(_) = key.kind() {
672 self.note_prototype_index(object);
673 }
674 let header = self.objects.get(object.0);
678 if !header.has(flags::DICTIONARY) {
679 let slots = header.slots();
680 let shape = self.shapes.get(header.shape.0);
681 let width = shape.width;
682 if width + flags.width() <= slots.len
683 && let Some(next) = shape.transitions.find(key, flags)
684 {
685 let header = self.objects.get_mut(object.0);
686 header.shape = next;
687 let prototype = header.has(flags::USED_AS_PROTOTYPE);
688 self.values.set_at(slots.start + width, values[0]);
689 if flags.width() == 2 {
690 self.values.set_at(slots.start + width + 1, values[1]);
691 }
692 if prototype {
693 self.invalidate(object);
694 }
695 return Ok(());
696 }
697 }
698 self.add_property_slow(object, key, flags, values)
699 }
700
701 #[inline(never)]
702 fn add_property_slow(
703 &mut self,
704 object: ObjectId,
705 key: PropertyKey,
706 flags: PropertyFlags,
707 values: [Value; 2],
708 ) -> Result<(), HeapError> {
709 debug_assert!(!self.is_pending_closure(object), "a pending closure");
710 let header = *self.objects.get(object.0);
711 if header.has(flags::DICTIONARY) {
712 return self.dictionary_add(object, key, flags, values);
713 }
714 let shape = self.shapes.get(header.shape.0);
715 let (count, width) = (shape.count, shape.width);
716 let known = shape.transitions.find(key, flags);
717 if known.is_none() && self.shape_lookup(header.shape, key).is_some() {
719 return Err(HeapError::DuplicateKey);
720 }
721 let keep = [Value::object(object), key.to_value(), values[0], values[1]];
722 if count >= DICTIONARY_THRESHOLD {
723 self.enter_dictionary(object, &keep[1..])?;
724 return self.dictionary_add(object, key, flags, values);
725 }
726 let slots = header.slots();
727 let needed = width + flags.width();
728 let grow = needed > slots.len;
729 let new_len = (slots.len * 2).max(needed).max(4);
730 let collections = self.gc.stats.collections;
731 if grow || known.is_none() {
732 let need = Need {
733 shapes: 1,
734 values: [if grow { new_len } else { 0 }, 0],
735 ..Need::default()
736 };
737 self.safepoint(&need, &keep)?;
738 }
739 let next = match known {
743 Some(child) if self.gc.stats.collections == collections => child,
744 _ => self.transition(header.shape, key, flags),
745 };
746 let mut slots = slots;
747 if grow {
748 slots = self.values.grow_slots(slots, new_len, &mut self.gc.room);
749 }
750 let header = self.objects.get_mut(object.0);
751 header.shape = next;
752 header.set_slots(slots);
753 let prototype = header.has(flags::USED_AS_PROTOTYPE);
754 self.values.set(slots, width, values[0]);
755 if flags.width() == 2 {
756 self.values.set(slots, width + 1, values[1]);
757 }
758 if prototype {
759 self.invalidate(object);
760 }
761 Ok(())
762 }
763
764 fn dictionary_add(
765 &mut self,
766 object: ObjectId,
767 key: PropertyKey,
768 flags: PropertyFlags,
769 values: [Value; 2],
770 ) -> Result<(), HeapError> {
771 let header = *self.objects.get(object.0);
772 let dictionary = self
773 .shapes
774 .get(header.shape.0)
775 .dictionary
776 .as_deref()
777 .expect("dictionary shape");
778 if dictionary.find(key).is_some() {
779 return Err(HeapError::DuplicateKey);
780 }
781 let slot = dictionary.next_slot(flags);
782 let end = slot + flags.width();
783 let mut slots = header.slots();
784 if end > slots.len {
785 let new_len = (slots.len * 2).max(end).max(4);
786 let need = Need {
787 values: [new_len, 0],
788 ..Need::default()
789 };
790 let keep = [Value::object(object), key.to_value(), values[0], values[1]];
791 self.safepoint(&need, &keep)?;
792 slots = self.values.grow_slots(slots, new_len, &mut self.gc.room);
793 self.objects.get_mut(object.0).set_slots(slots);
794 }
795 let dictionary = self
796 .shapes
797 .get_mut(header.shape.0)
798 .dictionary
799 .as_deref_mut()
800 .expect("dictionary shape");
801 let used = dictionary.insert(key, flags);
802 debug_assert_eq!(used, slot);
803 self.values.set(slots, slot, values[0]);
804 if flags.width() == 2 {
805 self.values.set(slots, slot + 1, values[1]);
806 }
807 self.dictionary_changed(object);
808 if header.has(flags::USED_AS_PROTOTYPE) {
809 self.invalidate(object);
810 }
811 Ok(())
812 }
813
814 #[inline]
818 fn dictionary_changed(&mut self, object: ObjectId) {
819 if self.objects.get(object.0).kind == ObjectKind::GlobalObject {
820 self.bump_global_generation();
821 }
822 }
823
824 pub(crate) fn enter_dictionary(
827 &mut self,
828 object: ObjectId,
829 keep: &[Value],
830 ) -> Result<(), HeapError> {
831 debug_assert!(!self.is_pending_closure(object), "a pending closure");
832 let mut roots = vec![Value::object(object)];
833 roots.extend_from_slice(keep);
834 self.safepoint(
835 &Need {
836 shapes: 1,
837 ..Need::default()
838 },
839 &roots,
840 )?;
841 let header = *self.objects.get(object.0);
842 let shape = self.shapes.get(header.shape.0);
843 let (prototype, kind) = (shape.prototype, shape.kind);
844 let dictionary = Dictionary::from_entries(self.shape_entries(header.shape));
845 let shape = self.dictionary_shape(prototype, kind, dictionary);
846 let header = self.objects.get_mut(object.0);
847 header.shape = shape;
848 header.flags |= flags::DICTIONARY;
849 Ok(())
850 }
851
852 pub fn reorder_dictionary(&mut self, object: ObjectId, front: &[PropertyKey]) {
856 let header = *self.objects.get(object.0);
857 debug_assert!(header.has(flags::DICTIONARY), "a dictionary object");
858 if let Some(dictionary) = self
859 .shapes
860 .get_mut(header.shape.0)
861 .dictionary
862 .as_deref_mut()
863 {
864 dictionary.reorder(front);
865 self.dictionary_changed(object);
866 }
867 }
868
869 pub fn delete_property(
872 &mut self,
873 object: ObjectId,
874 key: PropertyKey,
875 ) -> Result<bool, HeapError> {
876 if self.lookup_own(object, key).is_none() {
877 return Ok(false);
878 }
879 if !self.is_dictionary(object) {
880 self.enter_dictionary(object, &[])?;
881 }
882 let header = *self.objects.get(object.0);
883 let dictionary = self
884 .shapes
885 .get_mut(header.shape.0)
886 .dictionary
887 .as_deref_mut()
888 .expect("dictionary shape");
889 let (_, flags) = dictionary.find(key).expect("present key");
890 let slot = dictionary.remove(key).expect("present key");
891 for slot in slot..slot + flags.width() {
892 self.values.set(header.slots(), slot, Value::EMPTY);
893 }
894 self.dictionary_changed(object);
895 if header.has(flags::USED_AS_PROTOTYPE) {
896 self.invalidate(object);
897 }
898 Ok(true)
899 }
900
901 pub fn set_property_flags(
904 &mut self,
905 object: ObjectId,
906 key: PropertyKey,
907 flags: PropertyFlags,
908 ) -> Result<bool, HeapError> {
909 let Some((_, old)) = self.lookup_own(object, key) else {
910 return Ok(false);
911 };
912 if old == flags {
913 return Ok(true);
914 }
915 debug_assert!(!self.is_pending_closure(object), "a pending closure");
916 debug_assert_eq!(old.width(), flags.width(), "a data/accessor change");
917 let header = *self.objects.get(object.0);
918 if header.has(flags::DICTIONARY) {
919 let dictionary = self
920 .shapes
921 .get_mut(header.shape.0)
922 .dictionary
923 .as_deref_mut()
924 .expect("dictionary shape");
925 dictionary.set_flags(key, flags);
926 self.dictionary_changed(object);
927 } else {
928 let mut entries = self.shape_entries(header.shape);
929 for entry in &mut entries {
930 if entry.0 == key {
931 entry.1 = flags;
932 }
933 }
934 let prototype = self.prototype_of(object);
935 self.replay_shape(object, prototype, &entries)?;
936 }
937 if header.has(flags::USED_AS_PROTOTYPE) {
938 self.invalidate(object);
939 }
940 Ok(true)
941 }
942
943 pub fn reshape_property(
947 &mut self,
948 object: ObjectId,
949 key: PropertyKey,
950 flags: PropertyFlags,
951 values: [Value; 2],
952 ) -> Result<(), HeapError> {
953 let (_, old) = self.lookup_own(object, key).expect("an own property");
954 let mark = self.roots.stack.mark();
955 self.roots.stack.push(values[0]);
956 self.roots.stack.push(values[1]);
957 let result = (|| {
958 if old.width() == flags.width() {
959 self.set_property_flags(object, key, flags)?;
960 } else {
961 if !self.is_dictionary(object) {
962 self.enter_dictionary(object, &[])?;
963 }
964 let header = *self.objects.get(object.0);
965 let dictionary = self
966 .shapes
967 .get_mut(header.shape.0)
968 .dictionary
969 .as_deref_mut()
970 .expect("dictionary shape");
971 let slot = dictionary.reslot(key, flags).expect("present key");
972 let end = slot + flags.width();
973 let slots = header.slots();
974 if end > slots.len {
975 let new_len = (slots.len * 2).max(end).max(4);
976 self.safepoint(
977 &Need {
978 values: [new_len, 0],
979 ..Need::default()
980 },
981 &[Value::object(object)],
982 )?;
983 let slots = self.values.grow_slots(slots, new_len, &mut self.gc.room);
984 self.objects.get_mut(object.0).set_slots(slots);
985 }
986 self.dictionary_changed(object);
987 if header.has(flags::USED_AS_PROTOTYPE) {
988 self.invalidate(object);
989 }
990 }
991 let (slot, _) = self.lookup_own(object, key).expect("an own property");
992 self.set_slot(object, slot, values[0]);
993 if flags.width() == 2 {
994 self.set_slot(object, slot + 1, values[1]);
995 }
996 Ok(())
997 })();
998 self.roots.stack.truncate(mark);
999 result
1000 }
1001
1002 fn replay_shape(
1006 &mut self,
1007 object: ObjectId,
1008 prototype: Option<ObjectId>,
1009 entries: &[(PropertyKey, PropertyFlags, u32)],
1010 ) -> Result<(), HeapError> {
1011 let need = Need {
1012 shapes: entries.len() + 1,
1013 ..Need::default()
1014 };
1015 let proto_value = prototype.map_or(Value::UNDEFINED, Value::object);
1016 self.safepoint(&need, &[Value::object(object), proto_value])?;
1017 let kind = self.shapes.get(self.objects.get(object.0).shape.0).kind;
1018 let mut shape = self.root_shape(prototype, kind);
1019 for &(key, flags, slot) in entries {
1020 debug_assert_eq!(slot, self.shapes.get(shape.0).width);
1021 shape = self.transition(shape, key, flags);
1022 }
1023 self.objects.get_mut(object.0).shape = shape;
1024 Ok(())
1025 }
1026
1027 pub fn set_prototype(
1030 &mut self,
1031 object: ObjectId,
1032 prototype: Option<ObjectId>,
1033 ) -> Result<(), HeapError> {
1034 let old = self.prototype_of(object);
1035 if old == prototype {
1036 return Ok(());
1037 }
1038 let mut walk = prototype;
1039 while let Some(current) = walk {
1040 if current == object {
1041 return Err(HeapError::PrototypeCycle);
1042 }
1043 walk = self.prototype_of(current);
1044 }
1045 let header = *self.objects.get(object.0);
1046 if header.has(flags::DICTIONARY) {
1047 if let Some(prototype) = prototype {
1048 self.ensure_prototype(prototype);
1049 }
1050 let shape = self.shapes.get_mut(header.shape.0);
1051 shape.prototype = prototype;
1052 shape.validity = ValidityCellId(NONE);
1053 } else {
1054 let entries = self.shape_entries(header.shape);
1055 self.replay_shape(object, prototype, &entries)?;
1056 }
1057 if header.has(flags::USED_AS_PROTOTYPE) {
1058 if let Some(old) = old
1059 && let Some(info) = self.prototypes.get_mut(&old)
1060 {
1061 info.dependents.retain(|&dependent| dependent != object);
1062 }
1063 if let Some(new) = prototype {
1064 self.prototypes
1065 .entry(new)
1066 .or_default()
1067 .dependents
1068 .push(object);
1069 }
1070 self.invalidate(object);
1071 }
1072 Ok(())
1073 }
1074
1075 pub fn own_keys(&self, object: ObjectId) -> Vec<PropertyKey> {
1079 let header = self.objects.get(object.0);
1080 let mut indices = Vec::new();
1081 let elements = header.elements();
1082 let element_count = if header.kind == ObjectKind::Array {
1083 header.payload.min(elements.len)
1084 } else {
1085 elements.len
1086 };
1087 for index in 0..element_count {
1088 if !self.values.get(elements, index).is_empty()
1089 && let Some(key) = PropertyKey::index(index)
1090 {
1091 indices.push((index, key));
1092 }
1093 }
1094 let mut strings = Vec::new();
1095 let mut symbols = Vec::new();
1096 for (key, _, _) in self.shape_entries(header.shape) {
1097 match key.kind() {
1098 KeyKind::Index(index) => indices.push((index, key)),
1099 KeyKind::String(id) => match self.strings.get(id.0).array_index {
1100 NONE => strings.push(key),
1101 index => indices.push((index, key)),
1102 },
1103 KeyKind::Symbol(id) => {
1104 if !self.symbols.get(id.0).private {
1105 symbols.push(key);
1106 }
1107 }
1108 }
1109 }
1110 indices.sort_by_key(|&(index, _)| index);
1111 indices
1112 .into_iter()
1113 .map(|(_, key)| key)
1114 .chain(strings)
1115 .chain(symbols)
1116 .collect()
1117 }
1118
1119 fn has_index_property(&self, object: ObjectId) -> bool {
1122 let header = self.objects.get(object.0);
1123 let elements = header.elements();
1124 if self
1125 .values
1126 .slice(elements)
1127 .iter()
1128 .any(|value| !value.is_empty())
1129 {
1130 return true;
1131 }
1132 self.shape_entries(header.shape)
1133 .iter()
1134 .any(|(key, _, _)| match key.kind() {
1135 KeyKind::Index(_) => true,
1136 KeyKind::String(id) => self.strings.get(id.0).array_index != NONE,
1137 KeyKind::Symbol(_) => false,
1138 })
1139 }
1140
1141 pub(crate) fn ensure_prototype(&mut self, prototype: ObjectId) {
1146 let header = self.objects.get_mut(prototype.0);
1147 if header.has(flags::USED_AS_PROTOTYPE) {
1148 return;
1149 }
1150 header.flags |= flags::USED_AS_PROTOTYPE;
1151 let kind = header.kind;
1152 if self.holes_writable
1153 && (kind.is_exotic()
1154 || matches!(kind, ObjectKind::Arguments)
1155 || self.has_index_property(prototype))
1156 {
1157 self.holes_writable = false;
1158 }
1159 self.prototypes.entry(prototype).or_default();
1160 if let Some(parent) = self.prototype_of(prototype) {
1161 self.ensure_prototype(parent);
1162 self.prototypes
1163 .entry(parent)
1164 .or_default()
1165 .dependents
1166 .push(prototype);
1167 }
1168 }
1169
1170 pub(crate) fn invalidate(&mut self, object: ObjectId) {
1173 let mut work = vec![object];
1174 while let Some(current) = work.pop() {
1175 if let Some(info) = self.prototypes.get_mut(¤t) {
1176 if let Some(cell) = info.cell.take() {
1177 self.cells.get_mut(cell.0).valid = false;
1178 }
1179 work.extend_from_slice(&info.dependents);
1180 }
1181 }
1182 }
1183
1184 pub fn validity_cell(&mut self, object: ObjectId) -> Result<ValidityCellId, HeapError> {
1187 let shape_id = self.objects.get(object.0).shape;
1188 let shape = self.shapes.get(shape_id.0);
1189 let Some(prototype) = shape.prototype else {
1190 return Ok(STATIC_CELL);
1191 };
1192 if shape.validity.0 != NONE && self.cells.get(shape.validity.0).valid {
1193 return Ok(shape.validity);
1194 }
1195 let cell = self.chain_cell_keeping(prototype, Value::object(object))?;
1196 self.shapes.get_mut(shape_id.0).validity = cell;
1198 Ok(cell)
1199 }
1200
1201 pub fn chain_cell(&mut self, prototype: ObjectId) -> Result<ValidityCellId, HeapError> {
1207 self.chain_cell_keeping(prototype, Value::UNDEFINED)
1208 }
1209
1210 fn chain_cell_keeping(
1211 &mut self,
1212 prototype: ObjectId,
1213 keep: Value,
1214 ) -> Result<ValidityCellId, HeapError> {
1215 self.ensure_prototype(prototype);
1216 if let Some(cell) = self.prototypes.get(&prototype).and_then(|info| info.cell) {
1217 return Ok(cell);
1218 }
1219 self.safepoint(
1220 &Need {
1221 cells: 1,
1222 ..Need::default()
1223 },
1224 &[Value::object(prototype), keep],
1225 )?;
1226 let cell = ValidityCellId(
1227 self.cells
1228 .alloc(ValidityCell { valid: true }, &mut self.gc.room),
1229 );
1230 self.prototypes.entry(prototype).or_default().cell = Some(cell);
1231 Ok(cell)
1232 }
1233
1234 #[inline]
1235 pub fn cell_is_valid(&self, cell: ValidityCellId) -> bool {
1236 self.cells.get(cell.0).valid
1237 }
1238}