use std::{
cmp::Ordering, collections::TryReserveError, marker::PhantomData, num::NonZeroU32, ptr::NonNull,
};
use ahash::AHashMap;
use hashbrown::{HashTable, hash_table::Entry};
use crate::{
ecmascript::{
Agent, InternalMethods, Object, Primitive, PrivateField, PropertyKey, Realm, Symbol,
TryGetResult, Value,
},
engine::{Bindable, GcToken, NoGcScope, bindable_handle},
heap::{
ArenaAccess, ArenaAccessMut, CompactionLists, CreateHeapData, DirectArenaAccess,
DirectArenaAccessMut, Heap, HeapMarkAndSweep, HeapSweepWeakReference,
IntrinsicObjectShapes, PropertyKeyHeap, WeakReference, WorkQueues,
{ElementArrayKey, ElementArrays}, {HeapIndexHandle, PropertyKeyIndex},
},
};
use super::caches::PropertyLookupCache;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ObjectShape<'a>(
NonZeroU32,
PhantomData<&'a GcToken>,
);
bindable_handle!(ObjectShape);
impl<'a> ObjectShape<'a> {
pub(crate) const NULL: Self = Self::from_non_zero(NonZeroU32::new(1).unwrap());
pub(crate) fn is_intrinsic(self, agent: &Agent) -> bool {
self.get(agent).keys_cap != ElementArrayKey::Empty
&& self.get_transitions(agent).parent.is_none()
}
fn get_transitions(self, agent: &Agent) -> &ObjectShapeTransitionMap<'a> {
self.get_transitions_direct(&agent.heap.object_shape_transitions)
}
fn get_transitions_direct<'r>(
self,
transitions: &'r [ObjectShapeTransitionMap<'a>],
) -> &'r ObjectShapeTransitionMap<'a> {
&transitions[self.get_index()]
}
pub(crate) fn get_transitions_direct_mut<'r>(
self,
transitions: &'r mut [ObjectShapeTransitionMap<'static>],
) -> &'r mut ObjectShapeTransitionMap<'a> {
unsafe {
core::mem::transmute::<
&'r mut ObjectShapeTransitionMap<'static>,
&'r mut ObjectShapeTransitionMap<'a>,
>(&mut transitions[self.get_index()])
}
}
#[inline(always)]
pub(crate) const fn get_index_u32_const(self) -> u32 {
let raw_value = self.0.get();
(raw_value & 0x7FFF_FFFF) - 1
}
pub(crate) fn keys<'e>(
self,
object_shapes: &[ObjectShapeRecord<'static>],
elements: &'e ElementArrays,
) -> &'e [PropertyKey<'a>] {
let data = &object_shapes[self.get_index()];
debug_assert_eq!(data.values_cap, data.len.into());
elements.get_keys_raw(data.keys_cap, data.keys, data.len)
}
pub(crate) fn keys_index(
self,
agent: &impl AsRef<Vec<ObjectShapeRecord<'static>>>,
) -> PropertyKeyIndex<'a> {
self.get_direct(agent.as_ref()).keys
}
pub(crate) fn keys_capacity(
self,
agent: &impl AsRef<Vec<ObjectShapeRecord<'static>>>,
) -> ElementArrayKey {
self.get_direct(agent.as_ref()).keys_cap
}
pub(crate) fn values_capacity(
self,
agent: &impl AsRef<Vec<ObjectShapeRecord<'static>>>,
) -> ElementArrayKey {
self.get_direct(agent.as_ref()).values_cap
}
pub(crate) fn len(self, agent: &impl AsRef<Vec<ObjectShapeRecord<'static>>>) -> u32 {
self.get_direct(agent.as_ref()).len
}
pub(crate) fn extensible(self) -> bool {
(self.0.get() & 0x8000_0000) == 0
}
pub(crate) fn set_extensible(&mut self, extensible: bool) {
if !extensible {
self.0 = unsafe { NonZeroU32::new_unchecked(self.0.get() | 0x8000_0000) };
} else {
self.0 = unsafe { NonZeroU32::new_unchecked(self.0.get() & 0x7FFF_FFFF) };
}
}
pub(crate) fn is_empty(self, agent: &impl AsRef<Vec<ObjectShapeRecord<'static>>>) -> bool {
self.get_direct(agent.as_ref()).is_empty()
}
pub(crate) fn get_prototype(
self,
agent: &impl AsRef<Vec<ObjectShapeRecord<'static>>>,
) -> Option<Object<'a>> {
self.get_direct(agent.as_ref()).prototype
}
pub(crate) fn get_parent(
self,
agent: &impl AsRef<[ObjectShapeTransitionMap<'static>]>,
) -> Option<ObjectShape<'a>> {
self.get_transitions_direct(agent.as_ref()).parent
}
pub(crate) fn get_transition_to(
self,
agent: &Agent,
key: PropertyKey<'a>,
) -> Option<ObjectShape<'a>> {
let transitions = &agent.heap.object_shape_transitions[self.get_index()];
let hash = key.heap_hash(agent);
transitions
.table
.find(hash, |(k, _)| *k == key)
.map(|(_, shape)| *shape)
}
pub(crate) fn last(shapes: &[ObjectShapeRecord<'static>]) -> Self {
debug_assert!(!shapes.is_empty());
ObjectShape::from_non_zero(
unsafe { NonZeroU32::new_unchecked(shapes.len() as u32) },
)
}
#[inline(always)]
pub(crate) const fn from_non_zero(idx: NonZeroU32) -> Self {
if (idx.get() & 0x8000_0000) > 0 {
handle_object_shape_count_overflow();
}
ObjectShape(idx, PhantomData)
}
pub(crate) fn get_cached<'gc>(
self,
agent: &mut Agent,
p: PropertyKey,
receiver: Value,
cache: PropertyLookupCache,
gc: NoGcScope<'gc, '_>,
) -> Option<TryGetResult<'gc>> {
let shape = self;
if let Some((offset, prototype)) = cache.find_cached_property_offset(agent, shape) {
if offset.is_unset() {
TryGetResult::Unset.into()
} else {
let o = prototype.unwrap_or_else(|| Object::try_from(receiver).unwrap());
Some(o.get_own_property_at_offset(agent, offset, gc))
}
} else {
agent
.heap
.caches
.set_current_cache(shape, p, receiver, cache);
None
}
}
pub(crate) fn get_shape_for_prototype<'gc>(
agent: &mut Agent,
prototype: Option<Object<'gc>>,
) -> ObjectShape<'gc> {
if let Some(prototype) = prototype {
if let Some(base_shape) = agent
.heap
.prototype_shapes
.get_shape_for_prototype(prototype)
{
return base_shape;
}
agent.heap.create(ObjectShapeRecord::create_root(prototype))
} else {
ObjectShape::NULL
}
}
fn add_transition(self, agent: &mut Agent, key: PropertyKey<'a>, child: Self) {
let self_transitions =
self.get_transitions_direct_mut(&mut agent.heap.object_shape_transitions);
self_transitions.insert(
key,
child,
&PropertyKeyHeap::new(&mut agent.heap.strings, &mut agent.heap.symbols),
);
}
unsafe fn push_key(
self,
agent: &mut Agent,
key: PropertyKey<'a>,
) -> Result<(), TryReserveError> {
debug_assert_eq!(self.values_capacity(agent), self.len(agent).into());
let ObjectShapeRecord {
prototype: _,
keys,
keys_cap,
values_cap,
len,
} = self.get_direct_mut(&mut agent.heap.object_shapes);
unsafe { agent.heap.elements.push_key(keys_cap, keys, len, key) }?;
*values_cap = (*len).into();
Ok(())
}
pub(crate) fn get_child_shape(
self,
agent: &mut Agent,
key: PropertyKey<'a>,
) -> Result<Self, TryReserveError> {
if self.is_intrinsic(agent) {
unsafe { self.push_key(agent, key)? };
return Ok(self);
}
let frozen = !self.extensible();
if let Some(mut next_shape) = self.get_transition_to(agent, key) {
if frozen {
next_shape.set_extensible(false);
}
return Ok(next_shape);
}
let prototype = self.get_prototype(agent);
let len = self.len(agent) as usize;
let cap = self.keys_capacity(agent);
let keys_index = self.keys_index(agent);
let keys_uninit = agent.heap.elements.get_keys_uninit_raw(cap, keys_index);
let shape_record = if let Some(slot) = keys_uninit.get_mut(len)
&& slot.is_none()
{
slot.replace(key.unbind());
ObjectShapeRecord::create(prototype, keys_index, cap, len.checked_add(1).unwrap())
} else {
let new_len = len.checked_add(1).unwrap();
let (new_keys_cap, new_keys_index) = agent
.heap
.elements
.copy_keys_with_addition(cap, keys_index, len as u32, key)?;
ObjectShapeRecord::create(prototype, new_keys_index, new_keys_cap, new_len)
};
let mut child = agent
.heap
.create((shape_record, ObjectShapeTransitionMap::with_parent(self)));
self.add_transition(agent, key, child);
if frozen {
child.set_extensible(false);
}
Ok(child)
}
fn get_ancestor_shape(self, agent: &mut Agent, new_len: u32) -> Option<Self> {
let original_len = self.len(agent);
debug_assert!(new_len < original_len);
let prototype = self.get_prototype(agent);
if new_len == 0 {
return Some(Self::get_shape_for_prototype(agent, prototype));
}
let keys_cap = self.keys_capacity(agent);
let keys_index = self.keys_index(agent);
let mut ancestor_len = original_len.wrapping_sub(1);
let mut ancestor_shape = self.get_parent(agent);
while let Some(parent) = ancestor_shape {
debug_assert_eq!(parent.len(agent), ancestor_len);
debug_assert_eq!(parent.get_prototype(agent), prototype);
debug_assert_eq!(
parent.keys(&agent.heap.object_shapes, &agent.heap.elements),
agent
.heap
.elements
.get_keys_raw(keys_cap, keys_index, ancestor_len)
);
if parent.len(agent) == new_len {
return Some(parent);
}
ancestor_len = ancestor_len.wrapping_sub(1);
ancestor_shape = parent.get_parent(agent);
}
None
}
fn create_child_with_storage(
self,
agent: &mut Agent,
prototype: Option<Object<'a>>,
cap: ElementArrayKey,
index: PropertyKeyIndex<'a>,
) -> Self {
let new_len = self.len(agent).wrapping_add(1);
let key = *agent
.heap
.elements
.get_keys_raw(cap, index, new_len)
.last()
.unwrap();
let shape_record = ObjectShapeRecord::create(prototype, index, cap, new_len as usize);
let shape = agent
.heap
.create((shape_record, ObjectShapeTransitionMap::with_parent(self)));
self.add_transition(agent, key, shape);
shape
}
fn create_shapes_for_property_storage(
self,
agent: &mut Agent,
prototype: Option<Object<'a>>,
cap: ElementArrayKey,
index: PropertyKeyIndex<'a>,
len: u32,
) -> Self {
let mut shape = self;
for _ in self.len(agent)..len {
shape = shape.create_child_with_storage(agent, prototype, cap, index);
}
shape
}
pub(crate) fn get_shape_with_removal(
self,
agent: &mut Agent,
index: u32,
) -> Result<Self, TryReserveError> {
let len = self.len(agent);
debug_assert!(index < len);
let keys_cap = self.keys_capacity(agent);
let keys_index = self.keys_index(agent);
if self.is_intrinsic(agent) {
debug_assert_eq!(self.values_capacity(agent), self.len(agent).into());
let data = self.get_direct_mut(&mut agent.heap.object_shapes);
unsafe {
agent
.heap
.elements
.remove_key(keys_cap, keys_index, &mut data.len, index)
};
data.values_cap = data.len.into();
debug_assert_eq!(self.values_capacity(agent), self.len(agent).into());
return Ok(self);
}
let prototype = self.get_prototype(agent);
if len == 1 {
return Ok(Self::get_shape_for_prototype(agent, prototype));
}
let ancestor_shape = self.get_ancestor_shape(agent, index);
if let Some(mut parent_shape) = ancestor_shape {
for i in index.wrapping_add(1)..len {
let key = agent.heap.elements.get_keys_raw(keys_cap, keys_index, len)[i as usize];
if let Some(s) = parent_shape.get_transition_to(agent, key) {
parent_shape = s;
continue;
}
let (new_cap, new_keys_index) = agent.heap.elements.copy_keys_with_removal(
keys_cap,
keys_index,
len,
index as usize,
)?;
return Ok(parent_shape.create_shapes_for_property_storage(
agent,
prototype,
new_cap,
new_keys_index,
len.wrapping_sub(1),
));
}
Ok(parent_shape)
} else {
unreachable!()
}
}
unsafe fn insert_intrinsic_private_fields(
self,
agent: &mut Agent,
private_fields: &[PrivateField<'a>],
insertion_index: usize,
) -> Result<(Self, usize), TryReserveError> {
let ObjectShapeRecord {
prototype: _,
keys,
keys_cap,
len,
values_cap,
} = self.get_direct_mut(&mut agent.heap.object_shapes);
let private_fields_count = u32::try_from(private_fields.len()).unwrap();
agent
.heap
.elements
.reserve_keys_raw(keys, keys_cap, *len, private_fields_count)?;
let keys = agent.heap.elements.get_keys_uninit_raw(*keys_cap, *keys);
keys.copy_within(
insertion_index..*len as usize,
insertion_index + private_fields.len(),
);
for (slot, key) in keys[insertion_index..]
.iter_mut()
.zip(private_fields.iter().map(|f| f.get_key()))
{
*slot = Some(key.into());
}
*len += private_fields_count;
*values_cap = (*len).into();
Ok((self, insertion_index))
}
pub(crate) unsafe fn add_private_fields(
self,
agent: &mut Agent,
private_fields: NonNull<[PrivateField<'a>]>,
) -> Result<(Self, usize), TryReserveError> {
let private_fields = unsafe { private_fields.as_ref() };
let original_len = self.len(agent);
let insertion_index = if original_len == 0 {
0
} else {
let keys = self.keys(&agent.heap.object_shapes, &agent.heap.elements);
if !keys[0].is_private_name() {
0
} else if keys.last().unwrap().is_private_name() {
original_len as usize
} else {
keys.binary_search_by(|k| {
if k.is_private_name() {
Ordering::Less
} else {
Ordering::Greater
}
})
.unwrap_err()
}
};
if self.is_intrinsic(agent) {
if insertion_index == original_len as usize {
for field in private_fields {
unsafe { self.push_key(agent, field.get_key().into())? };
}
return Ok((self, insertion_index));
}
return unsafe {
self.insert_intrinsic_private_fields(agent, private_fields, insertion_index)
};
}
if insertion_index == original_len as usize {
let mut shape = self;
for field in private_fields {
shape = shape.get_child_shape(agent, field.get_key().into())?;
}
return Ok((shape, insertion_index));
}
let ancestor_shape = self.get_ancestor_shape(agent, insertion_index as u32);
let prototype = self.get_prototype(agent);
let cap = self.keys_capacity(agent);
let keys_index = self.keys_index(agent);
if let Some(mut parent_shape) = ancestor_shape {
for field in private_fields {
let key = field.get_key();
parent_shape = parent_shape.get_child_shape(agent, key.into())?;
}
for i in insertion_index..original_len as usize {
let key = agent
.heap
.elements
.get_keys_raw(cap, keys_index, original_len)[i];
parent_shape = parent_shape.get_child_shape(agent, key)?;
}
Ok((parent_shape, insertion_index))
} else {
debug_assert_eq!(
insertion_index, 0,
"Object Shape had Private Fields without having an ancestor with them"
);
let final_len = (original_len as usize)
.checked_add(private_fields.len())
.expect("Ridiculous number of fields");
agent.heap.object_shapes.reserve(final_len);
agent.heap.object_shape_transitions.reserve(final_len);
let mut parent_shape = Self::get_shape_for_prototype(agent, prototype);
for field in private_fields {
let key = field.get_key();
parent_shape = parent_shape.get_child_shape(agent, key.into())?;
}
for i in 0..original_len as usize {
let key = agent
.heap
.elements
.get_keys_raw(cap, keys_index, original_len)[i];
parent_shape = parent_shape.get_child_shape(agent, key)?;
}
Ok((parent_shape, 0))
}
}
pub(crate) fn get_shape_with_prototype(
self,
agent: &mut Agent,
prototype: Option<Object<'a>>,
) -> Self {
if self.is_intrinsic(agent) {
self.get_mut(agent).prototype = prototype.unbind();
return self;
}
let original_len = self.len(agent);
let original_cap = self.keys_capacity(agent);
let original_keys_index = self.keys_index(agent);
let mut shape = Self::get_shape_for_prototype(agent, prototype);
let keys = self.keys(&agent.heap.object_shapes, &agent.heap.elements);
for i in 0..original_len as usize {
let key = keys[i];
if let Some(next_shape) = shape.get_transition_to(agent, key) {
shape = next_shape;
continue;
};
let count = (original_len as usize).wrapping_sub(i);
agent.heap.object_shapes.reserve(count);
agent.heap.object_shape_transitions.reserve(count);
let keys =
agent
.heap
.elements
.get_keys_raw(original_cap, original_keys_index, original_len)
as *const [PropertyKey<'a>];
let keys = unsafe { &*keys };
for (i, key) in keys.iter().enumerate().take(original_len as usize).skip(i) {
let next_shape = agent.heap.create((
ObjectShapeRecord::create(
prototype,
original_keys_index,
original_cap,
i.wrapping_add(1),
),
ObjectShapeTransitionMap::with_parent(shape),
));
shape.add_transition(agent, *key, next_shape);
shape = next_shape.unbind();
}
break;
}
shape
}
pub(crate) fn make_intrinsic(self, agent: &mut Agent) -> Result<Self, TryReserveError> {
let properties_count = self.len(agent);
let prototype = self.get_prototype(agent);
let cap = self.keys_capacity(agent);
let keys = self.keys_index(agent);
let (cap, index) = agent.heap.elements.copy_keys_with_capacity(
properties_count as usize,
cap,
keys,
properties_count,
)?;
let cap = cap.make_intrinsic();
Ok(agent.heap.create(ObjectShapeRecord::create(
prototype,
index,
cap,
properties_count as usize,
)))
}
pub(crate) fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>) {
let intrinsics = agent.get_realm_record_by_id(realm).intrinsics();
let base_shape = intrinsics.object_shape();
fn create_intrinsic_shape(
agent: &mut Agent,
realm: Realm<'static>,
base_shape: ObjectShape,
shape_intrinsic: IntrinsicObjectShapes,
) {
let shape = shape_intrinsic.get_object_shape_index(base_shape);
let proto = shape_intrinsic.get_proto_intrinsic();
let prototype = agent
.get_realm_record_by_id(realm)
.intrinsics()
.get_intrinsic_default_proto(proto);
let result = agent.heap.create(ObjectShapeRecord::create_root(prototype));
debug_assert_eq!(shape, result);
}
create_intrinsic_shape(agent, realm, base_shape, IntrinsicObjectShapes::Object);
create_intrinsic_shape(agent, realm, base_shape, IntrinsicObjectShapes::Array);
create_intrinsic_shape(agent, realm, base_shape, IntrinsicObjectShapes::Number);
create_intrinsic_shape(agent, realm, base_shape, IntrinsicObjectShapes::String);
}
}
#[inline(never)]
#[cold]
const fn handle_object_shape_count_overflow() -> ! {
panic!("ObjectShape count overflowed");
}
#[derive(Debug)]
pub(crate) struct ObjectShapeRecord<'a> {
prototype: Option<Object<'a>>,
keys: PropertyKeyIndex<'a>,
keys_cap: ElementArrayKey,
len: u32,
values_cap: ElementArrayKey,
}
impl<'a> ObjectShapeRecord<'a> {
pub(crate) const NULL: Self = Self {
prototype: None,
keys: PropertyKeyIndex::ZERO,
keys_cap: ElementArrayKey::Empty,
len: 0,
values_cap: ElementArrayKey::Empty,
};
#[inline]
pub(crate) fn create_root(prototype: Object<'a>) -> Self {
Self {
prototype: Some(prototype),
keys: PropertyKeyIndex::ZERO,
keys_cap: ElementArrayKey::Empty,
len: 0,
values_cap: ElementArrayKey::Empty,
}
}
#[inline]
pub(crate) fn create(
prototype: Option<Object<'a>>,
keys: PropertyKeyIndex<'a>,
keys_cap: ElementArrayKey,
len: usize,
) -> Self {
let len = u32::try_from(len).expect("Unreasonable object size");
Self {
prototype,
keys,
keys_cap,
len,
values_cap: len.into(),
}
}
pub(crate) fn is_empty(&self) -> bool {
self.len == 0
}
}
bindable_handle!(ObjectShapeRecord);
#[derive(Debug)]
pub(crate) struct ObjectShapeTransitionMap<'a> {
parent: Option<ObjectShape<'a>>,
table: HashTable<(PropertyKey<'a>, ObjectShape<'a>)>,
}
impl<'a> ObjectShapeTransitionMap<'a> {
pub(crate) const ROOT: Self = Self {
parent: None,
table: HashTable::new(),
};
pub(crate) fn with_parent(parent: ObjectShape<'a>) -> Self {
Self {
parent: Some(parent),
table: HashTable::new(),
}
}
pub(crate) fn insert(&mut self, key: PropertyKey, shape: ObjectShape, heap: &PropertyKeyHeap) {
let key = key.unbind();
let shape = shape.unbind();
let hash = key.heap_hash(heap);
match self
.table
.entry(hash, |e| e.0 == key, |e| e.0.heap_hash(heap))
{
Entry::Occupied(_) => {
unreachable!("Attempted to overwrite an existing Object Shape transition")
}
Entry::Vacant(e) => e.insert((key, shape)),
};
}
}
bindable_handle!(ObjectShapeTransitionMap);
#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct PrototypeShapeTable {
table: AHashMap<Object<'static>, WeakReference<ObjectShape<'static>>>,
}
impl PrototypeShapeTable {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
table: AHashMap::with_capacity(capacity),
}
}
pub(crate) fn get_shape_for_prototype<'a>(
&self,
prototype: Object<'a>,
) -> Option<ObjectShape<'a>> {
let shape = self.table.get(&prototype)?;
Some(shape.0)
}
pub(crate) fn set_shape_for_prototype<'a>(
&mut self,
prototype: Object<'a>,
shape: ObjectShape<'a>,
) {
let previous = self
.table
.insert(prototype.unbind(), WeakReference(shape.unbind()));
assert!(previous.is_none(), "Re-set prototype root Object Shape");
}
}
impl Symbol<'_> {
pub(crate) fn object_shape(self, agent: &mut Agent) -> ObjectShape<'static> {
let prototype = agent.current_realm_record().intrinsics().symbol_prototype();
ObjectShape::get_shape_for_prototype(agent, Some(prototype.into()))
}
}
impl Primitive<'_> {
pub(crate) fn object_shape(self, agent: &mut Agent) -> Option<ObjectShape<'static>> {
let intrinsics = agent.current_realm_record().intrinsics();
match self {
Self::Undefined | Self::Null => None,
Self::Boolean(_) => {
let prototype = intrinsics.boolean_prototype();
Some(ObjectShape::get_shape_for_prototype(
agent,
Some(prototype.into()),
))
}
Self::String(_) | Self::SmallString(_) => Some(intrinsics.string_shape()),
Self::Symbol(s) => Some(s.object_shape(agent)),
Self::Number(_) | Self::Integer(_) | Self::SmallF64(_) => {
Some(intrinsics.number_shape())
}
Self::BigInt(_) | Self::SmallBigInt(_) => {
let prototype = intrinsics.big_int_prototype();
Some(ObjectShape::get_shape_for_prototype(
agent,
Some(prototype.into()),
))
}
}
}
}
impl HeapIndexHandle for ObjectShape<'_> {
const _DEF: Self = Self::from_non_zero(NonZeroU32::new(u32::MAX).unwrap());
fn from_index_u32(index: u32) -> Self {
Self::from_non_zero(NonZeroU32::new(index + 1).unwrap())
}
fn get_index_u32(self) -> u32 {
let raw_value = self.0.get();
(raw_value & 0x7FFF_FFFF) - 1
}
}
impl<'a> DirectArenaAccess for ObjectShape<'a> {
type Data = ObjectShapeRecord<'static>;
type Output = ObjectShapeRecord<'a>;
#[inline(always)]
fn get_direct(self, object_shapes: &Vec<Self::Data>) -> &Self::Output {
&object_shapes[self.get_index()]
}
}
impl<'a> DirectArenaAccessMut for ObjectShape<'a> {
#[inline(always)]
fn get_direct_mut(self, object_shapes: &mut Vec<Self::Data>) -> &mut Self::Output {
unsafe { core::mem::transmute(&mut object_shapes[self.get_index()]) }
}
}
impl AsRef<Vec<ObjectShapeRecord<'static>>> for Agent {
#[inline(always)]
fn as_ref(&self) -> &Vec<ObjectShapeRecord<'static>> {
&self.heap.object_shapes
}
}
impl AsMut<Vec<ObjectShapeRecord<'static>>> for Agent {
#[inline(always)]
fn as_mut(&mut self) -> &mut Vec<ObjectShapeRecord<'static>> {
&mut self.heap.object_shapes
}
}
impl<'a> CreateHeapData<ObjectShapeRecord<'a>, ObjectShape<'a>> for Heap {
fn create(&mut self, data: ObjectShapeRecord<'a>) -> ObjectShape<'a> {
self.create((data, ObjectShapeTransitionMap::ROOT))
}
}
impl<'a> CreateHeapData<(ObjectShapeRecord<'a>, ObjectShapeTransitionMap<'a>), ObjectShape<'a>>
for Heap
{
fn create(
&mut self,
data: (ObjectShapeRecord<'a>, ObjectShapeTransitionMap<'a>),
) -> ObjectShape<'a> {
let (record, transitions) = data;
let is_root = record.keys_cap == ElementArrayKey::Empty;
let prototype = record.prototype;
if is_root {
debug_assert_eq!(
transitions.parent, None,
"Object Shape has zero properties but has a parent"
);
debug_assert_eq!(
record.len, 0,
"Object Shape has zero capacity but non-zero length"
);
debug_assert_eq!(
record.keys.get_index(),
0,
"Object Shape has zero capacity but non-zero keys index"
);
}
self.object_shapes.push(record.unbind());
self.object_shape_transitions.push(transitions.unbind());
self.alloc_counter += core::mem::size_of::<ObjectShapeRecord>()
+ core::mem::size_of::<ObjectShapeTransitionMap>();
let shape = ObjectShape::last(&self.object_shapes);
if let Some(prototype) = prototype
&& is_root
{
self.prototype_shapes
.set_shape_for_prototype(prototype, shape);
}
shape
}
}
impl HeapMarkAndSweep for ObjectShape<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
queues.object_shapes.push(*self);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let top_bit = self.0.get() & 0x8000_0000;
self.0 = unsafe { NonZeroU32::new_unchecked(self.0.get() & 0x7FFF_FFFF) };
compactions
.object_shapes
.shift_non_zero_u32_index(&mut self.0);
self.0 = unsafe { NonZeroU32::new_unchecked(self.0.get() | top_bit) };
}
}
impl HeapSweepWeakReference for ObjectShape<'static> {
fn sweep_weak_reference(self, compactions: &CompactionLists) -> Option<Self> {
let top_bit = self.0.get() & 0x8000_0000;
let raw_value = unsafe { NonZeroU32::new_unchecked(self.0.get() & 0x7FFF_FFFF) };
compactions
.object_shapes
.shift_weak_non_zero_u32_index(raw_value)
.map(|i| {
let i = unsafe { NonZeroU32::new_unchecked(i.get() | top_bit) };
Self(i, PhantomData)
})
}
}
impl HeapMarkAndSweep for ObjectShapeRecord<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self {
prototype,
keys,
keys_cap,
len: _,
values_cap: _,
} = self;
prototype.mark_values(queues);
match keys_cap {
ElementArrayKey::Empty | ElementArrayKey::EmptyIntrinsic => {}
ElementArrayKey::E1 => queues.k_2_1.push(*keys),
ElementArrayKey::E2 => queues.k_2_2.push(*keys),
ElementArrayKey::E3 => queues.k_2_3.push(*keys),
ElementArrayKey::E4 => queues.k_2_4.push(*keys),
ElementArrayKey::E6 => queues.k_2_6.push(*keys),
ElementArrayKey::E8 => queues.k_2_8.push(*keys),
ElementArrayKey::E10 => queues.k_2_10.push(*keys),
ElementArrayKey::E12 => queues.k_2_12.push(*keys),
ElementArrayKey::E16 => queues.k_2_16.push(*keys),
ElementArrayKey::E24 => queues.k_2_24.push(*keys),
ElementArrayKey::E32 => queues.k_2_32.push(*keys),
}
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self {
prototype,
keys,
keys_cap,
len: _,
values_cap: _,
} = self;
prototype.sweep_values(compactions);
match keys_cap {
ElementArrayKey::Empty | ElementArrayKey::EmptyIntrinsic => {}
ElementArrayKey::E1 => compactions.k_2_1.shift_index(keys),
ElementArrayKey::E2 => compactions.k_2_2.shift_index(keys),
ElementArrayKey::E3 => compactions.k_2_3.shift_index(keys),
ElementArrayKey::E4 => compactions.k_2_4.shift_index(keys),
ElementArrayKey::E6 => compactions.k_2_6.shift_index(keys),
ElementArrayKey::E8 => compactions.k_2_8.shift_index(keys),
ElementArrayKey::E10 => compactions.k_2_10.shift_index(keys),
ElementArrayKey::E12 => compactions.k_2_12.shift_index(keys),
ElementArrayKey::E16 => compactions.k_2_16.shift_index(keys),
ElementArrayKey::E24 => compactions.k_2_24.shift_index(keys),
ElementArrayKey::E32 => compactions.k_2_32.shift_index(keys),
}
}
}
impl HeapMarkAndSweep for ObjectShapeTransitionMap<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self { parent, table } = self;
parent.mark_values(queues);
for (key, _) in table {
key.mark_values(queues);
}
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self { parent, table } = self;
parent.sweep_values(compactions);
table.retain(|(key, value)| {
let Some(new_value) = value.sweep_weak_reference(compactions) else {
return false;
};
key.sweep_values(compactions);
*value = new_value;
true
});
}
}
impl HeapMarkAndSweep for PrototypeShapeTable {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self { table } = self;
table.mark_values(queues);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self { table } = self;
table.sweep_values(compactions);
}
}
impl AsRef<[ObjectShapeTransitionMap<'static>]> for Agent {
fn as_ref(&self) -> &[ObjectShapeTransitionMap<'static>] {
&self.heap.object_shape_transitions
}
}
impl AsMut<[ObjectShapeTransitionMap<'static>]> for Agent {
fn as_mut(&mut self) -> &mut [ObjectShapeTransitionMap<'static>] {
&mut self.heap.object_shape_transitions
}
}