use arrayvec::ArrayVec;
use itertools::Itertools;
use std::{cell::Cell, fmt};
use boa_gc::GcRefCell;
use boa_macros::{Finalize, Trace};
use crate::{
JsString,
object::shape::{Shape, WeakShape, slot::Slot},
};
#[cfg(test)]
mod tests;
pub(crate) const PIC_CAPACITY: usize = 4;
#[derive(Clone, Debug, Trace, Finalize)]
pub(crate) struct CacheEntry {
pub(crate) shape: WeakShape,
#[unsafe_ignore_trace]
pub(crate) slot: Slot,
}
#[derive(Clone, Debug, Trace, Finalize)]
pub(crate) struct InlineCache {
pub(crate) name: JsString,
pub(crate) entries: GcRefCell<ArrayVec<CacheEntry, PIC_CAPACITY>>,
#[unsafe_ignore_trace]
pub(crate) megamorphic: Cell<bool>,
}
impl fmt::Display for InlineCache {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(name:{} entries:", self.name.display_escaped())?;
if self.megamorphic.get() {
return write!(f, "(megamorphic))");
}
let entries = self.entries.borrow();
let entries = entries.iter().map(|e| e.shape.to_addr_usize()).format(", ");
write!(f, "({entries:#x}))")
}
}
impl InlineCache {
pub(crate) fn new(name: JsString) -> Self {
Self {
name,
entries: GcRefCell::new(ArrayVec::new()),
megamorphic: Cell::new(false),
}
}
pub(crate) fn set(&self, shape: &Shape, slot: Slot) {
if self.megamorphic.get() {
return;
}
let mut entries = self.entries.borrow_mut();
if entries
.try_push(CacheEntry {
shape: shape.into(),
slot,
})
.is_err()
{
self.megamorphic.set(true);
entries.clear();
}
}
pub(crate) fn get(&self, shape: &Shape) -> Option<(Shape, Slot)> {
if self.megamorphic.get() {
return None;
}
let mut entries = self.entries.borrow_mut();
let mut i = 0;
let mut result = None;
let shape_addr = shape.to_addr_usize();
while i < entries.len() {
if let Some(upgraded) = entries[i].shape.upgrade() {
if upgraded.to_addr_usize() == shape_addr {
result = Some((upgraded, entries[i].slot));
break;
}
i += 1;
} else {
entries.swap_remove(i);
}
}
result
}
}