Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions core/engine/src/object/internal_methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::ops::{Deref, DerefMut};

use super::{
JsPrototype, PROTOTYPE,
property_map::OwnDataLookup,
shape::slot::{Slot, SlotAttributes},
};
use crate::{
Expand Down Expand Up @@ -709,6 +710,32 @@ pub(crate) fn ordinary_get(
receiver: JsValue,
context: &mut InternalMethodPropertyContext<'_>,
) -> JsResult<JsValue> {
// Fast path: read a plain data own-property's value directly, skipping
// `PropertyDescriptor` construction. Accessors, indexed keys and exotic objects fall
// through to the spec path below.
if !matches!(key, PropertyKey::Index(_))
&& obj.vtable().__get_own_property__ as usize
== ordinary_get_own_property as *const () as usize
{
let lookup = obj
.borrow()
.properties
.get_own_data_named(key, context.slot());
match lookup {
OwnDataLookup::Data(value) => return Ok(value),
OwnDataLookup::Absent => {
if let Some(parent) = obj.__get_prototype_of__(context)? {
context.slot().set_not_cacheable_if_already_prototype();
context.slot().attributes |= SlotAttributes::PROTOTYPE;
return parent.__get__(key, receiver, context);
}
return Ok(JsValue::undefined());
}
// Accessor: fall through to invoke the getter.
OwnDataLookup::Accessor => {}
}
}

// 1. Assert: IsPropertyKey(P) is true.
// 2. Let desc be ? O.[[GetOwnProperty]](P).
match obj.__get_own_property__(key, context)? {
Expand Down
38 changes: 38 additions & 0 deletions core/engine/src/object/property_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ use rustc_hash::FxHashMap;
use std::{collections::hash_map, iter::FusedIterator};
use thin_vec::ThinVec;

/// Result of [`PropertyMap::get_own_data_named`], the fast-path own-property lookup
/// used by `OrdinaryGet`.
#[derive(Debug)]
pub(crate) enum OwnDataLookup {
/// A plain data own property; carries its value read directly from storage.
Data(JsValue),
/// The own property exists but is an accessor; the caller must use the descriptor path.
Accessor,
/// No such own property; the caller can proceed directly to the prototype walk.
Absent,
}

/// This represents all the indexed properties.
///
/// The index properties can be stored in two storage methods:
Expand Down Expand Up @@ -565,6 +577,32 @@ impl PropertyMap {
None
}

/// Fast path for `OrdinaryGet`: resolves a named own-property lookup in a single shape
/// probe, returning a plain data property's value straight from storage without building
/// a [`PropertyDescriptor`]. `out_slot` is updated only on a data hit.
#[must_use]
pub(crate) fn get_own_data_named(
&self,
key: &PropertyKey,
out_slot: &mut Slot,
) -> OwnDataLookup {
debug_assert!(!matches!(key, PropertyKey::Index(_)));

let Some(slot) = self.shape.lookup(key) else {
return OwnDataLookup::Absent;
};
if slot.attributes.is_accessor_descriptor() {
return OwnDataLookup::Accessor;
}

out_slot.index = slot.index;
// Remove all descriptor attributes, but keep inline caching bits.
out_slot.attributes = (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS)
| slot.attributes
| SlotAttributes::FOUND;
OwnDataLookup::Data(self.storage[slot.index as usize].clone())
}

/// Get the property with the given key from the [`PropertyMap`].
#[must_use]
pub(crate) fn get_storage(&self, Slot { index, attributes }: Slot) -> PropertyDescriptor {
Expand Down