@ -1,5 +1,5 @@
use super ::{ PropertyDescriptor , PropertyKey } ;
use super ::{ PropertyDescriptor , PropertyKey } ;
use crate ::{ JsString , JsSymbol } ;
use crate ::{ property ::PropertyDescriptorBuilder , JsString , JsSymbol , JsValue } ;
use boa_gc ::{ custom_trace , Finalize , Trace } ;
use boa_gc ::{ custom_trace , Finalize , Trace } ;
use indexmap ::IndexMap ;
use indexmap ::IndexMap ;
use rustc_hash ::{ FxHashMap , FxHasher } ;
use rustc_hash ::{ FxHashMap , FxHasher } ;
@ -28,9 +28,202 @@ unsafe impl<K: Trace> Trace for OrderedHashMap<K> {
} ) ;
} ) ;
}
}
/// This represents all the indexed properties.
///
/// The index properties can be stored in two storage methods:
/// - `Dense` Storage
/// - `Sparse` Storage
///
/// By default it is dense storage.
#[ derive(Debug, Trace, Finalize) ]
enum IndexedProperties {
/// Dense storage holds a contiguous array of properties where the index in the array is the key of the property.
/// These are known to be data descriptors with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.
///
/// Since we know the properties of the property descriptors (and they are all the same) we can omit it and just store only
/// the value field and construct the data property descriptor on demand.
///
/// This storage method is used by default.
Dense ( Vec < JsValue > ) ,
/// Sparse storage this storage is used as a backup if the element keys are not continuous or the property descriptors
/// are not data descriptors with with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.
///
/// This method uses more space, since we also have to store the property descriptors, not just the value.
/// It is also slower because we need to to a hash lookup.
Sparse ( FxHashMap < u32 , PropertyDescriptor > ) ,
}
impl Default for IndexedProperties {
#[ inline ]
fn default ( ) -> Self {
Self ::Dense ( Vec ::new ( ) )
}
}
impl IndexedProperties {
/// Get a property descriptor if it exists.
#[ inline ]
fn get ( & self , key : u32 ) -> Option < PropertyDescriptor > {
match self {
Self ::Sparse ( ref map ) = > map . get ( & key ) . cloned ( ) ,
Self ::Dense ( ref vec ) = > vec . get ( key as usize ) . map ( | value | {
PropertyDescriptorBuilder ::new ( )
. writable ( true )
. enumerable ( true )
. configurable ( true )
. value ( value . clone ( ) )
. build ( )
} ) ,
}
}
/// Helper function for converting from a dense storage type to sparse storage type.
#[ inline ]
fn convert_dense_to_sparse ( vec : & mut Vec < JsValue > ) -> FxHashMap < u32 , PropertyDescriptor > {
let data = std ::mem ::take ( vec ) ;
data . into_iter ( )
. enumerate ( )
. map ( | ( index , value ) | {
(
index as u32 ,
PropertyDescriptorBuilder ::new ( )
. writable ( true )
. enumerable ( true )
. configurable ( true )
. value ( value )
. build ( ) ,
)
} )
. collect ( )
}
/// Inserts a property descriptor with the specified key.
#[ inline ]
fn insert ( & mut self , key : u32 , property : PropertyDescriptor ) -> Option < PropertyDescriptor > {
let vec = match self {
Self ::Sparse ( map ) = > return map . insert ( key , property ) ,
Self ::Dense ( vec ) = > {
let len = vec . len ( ) as u32 ;
if key < = len
& & property . value ( ) . is_some ( )
& & property . writable ( ) . unwrap_or ( false )
& & property . enumerable ( ) . unwrap_or ( false )
& & property . configurable ( ) . unwrap_or ( false )
{
// Fast Path: continues array access.
let mut value = property
. value ( )
. cloned ( )
. expect ( "already checked that the property descriptor has a value field" ) ;
// If the key is pointing one past the last element, we push it!
//
// Since the previous key is the current key - 1. Meaning that the elements are continuos.
if key = = len {
vec . push ( value ) ;
return None ;
}
// If it the key points in at a already taken index, swap and return it.
std ::mem ::swap ( & mut vec [ key as usize ] , & mut value ) ;
return Some (
PropertyDescriptorBuilder ::new ( )
. writable ( true )
. enumerable ( true )
. configurable ( true )
. value ( value )
. build ( ) ,
) ;
}
vec
}
} ;
// Slow path: converting to sparse storage.
let mut map = Self ::convert_dense_to_sparse ( vec ) ;
let old_property = map . insert ( key , property ) ;
* self = Self ::Sparse ( map ) ;
old_property
}
/// Inserts a property descriptor with the specified key.
#[ inline ]
fn remove ( & mut self , key : u32 ) -> Option < PropertyDescriptor > {
let vec = match self {
Self ::Sparse ( map ) = > return map . remove ( & key ) ,
Self ::Dense ( vec ) = > {
// Fast Path: contiguous storage.
// Has no elements or out of range, nothing to delete!
if vec . is_empty ( ) | | key as usize > = vec . len ( ) {
return None ;
}
// If the key is pointing at the last element, then we pop it and return it.
//
// It does not make the storage sparse
if key as usize = = vec . len ( ) . wrapping_sub ( 1 ) {
let value = vec . pop ( ) . expect ( "Already checked if it is out of bounds" ) ;
return Some (
PropertyDescriptorBuilder ::new ( )
. writable ( true )
. enumerable ( true )
. configurable ( true )
. value ( value )
. build ( ) ,
) ;
}
vec
}
} ;
// Slow Path: conversion to sparse storage.
let mut map = Self ::convert_dense_to_sparse ( vec ) ;
let old_property = map . remove ( & key ) ;
* self = Self ::Sparse ( map ) ;
old_property
}
/// Check if we contain the key to a property descriptor.
fn contains_key ( & self , key : u32 ) -> bool {
match self {
Self ::Sparse ( map ) = > map . contains_key ( & key ) ,
Self ::Dense ( vec ) = > ( 0 .. vec . len ( ) as u32 ) . contains ( & key ) ,
}
}
fn iter ( & self ) -> IndexProperties < ' _ > {
match self {
Self ::Dense ( vec ) = > IndexProperties ::Dense ( vec . iter ( ) . enumerate ( ) ) ,
Self ::Sparse ( map ) = > IndexProperties ::Sparse ( map . iter ( ) ) ,
}
}
fn keys ( & self ) -> IndexPropertyKeys < ' _ > {
match self {
Self ::Dense ( vec ) = > IndexPropertyKeys ::Dense ( 0 .. vec . len ( ) as u32 ) ,
Self ::Sparse ( map ) = > IndexPropertyKeys ::Sparse ( map . keys ( ) ) ,
}
}
fn values ( & self ) -> IndexPropertyValues < ' _ > {
match self {
Self ::Dense ( vec ) = > IndexPropertyValues ::Dense ( vec . iter ( ) ) ,
Self ::Sparse ( map ) = > IndexPropertyValues ::Sparse ( map . values ( ) ) ,
}
}
}
#[ derive(Default, Debug, Trace, Finalize) ]
#[ derive(Default, Debug, Trace, Finalize) ]
pub struct PropertyMap {
pub struct PropertyMap {
indexed_properties : FxHashMap < u32 , PropertyDescriptor > ,
indexed_properties : IndexedProperties ,
/// Properties
/// Properties
string_properties : OrderedHashMap < JsString > ,
string_properties : OrderedHashMap < JsString > ,
/// Symbol Properties
/// Symbol Properties
@ -41,11 +234,12 @@ impl PropertyMap {
pub fn new ( ) -> Self {
pub fn new ( ) -> Self {
Self ::default ( )
Self ::default ( )
}
}
pub fn get ( & self , key : & PropertyKey ) -> Option < & PropertyDescriptor > {
pub fn get ( & self , key : & PropertyKey ) -> Option < PropertyDescriptor > {
match key {
match key {
PropertyKey ::Index ( index ) = > self . indexed_properties . get ( index ) ,
PropertyKey ::Index ( index ) = > self . indexed_properties . get ( * index ) ,
PropertyKey ::String ( string ) = > self . string_properties . 0. get ( string ) ,
PropertyKey ::String ( string ) = > self . string_properties . 0. get ( string ) . cloned ( ) ,
PropertyKey ::Symbol ( symbol ) = > self . symbol_properties . 0. get ( symbol ) ,
PropertyKey ::Symbol ( symbol ) = > self . symbol_properties . 0. get ( symbol ) . cloned ( ) ,
}
}
}
}
@ -67,12 +261,17 @@ impl PropertyMap {
pub fn remove ( & mut self , key : & PropertyKey ) -> Option < PropertyDescriptor > {
pub fn remove ( & mut self , key : & PropertyKey ) -> Option < PropertyDescriptor > {
match key {
match key {
PropertyKey ::Index ( index ) = > self . indexed_properties . remove ( index ) ,
PropertyKey ::Index ( index ) = > self . indexed_properties . remove ( * index ) ,
PropertyKey ::String ( string ) = > self . string_properties . 0. shift_remove ( string ) ,
PropertyKey ::String ( string ) = > self . string_properties . 0. shift_remove ( string ) ,
PropertyKey ::Symbol ( symbol ) = > self . symbol_properties . 0. shift_remove ( symbol ) ,
PropertyKey ::Symbol ( symbol ) = > self . symbol_properties . 0. shift_remove ( symbol ) ,
}
}
}
}
/// Overrides all the indexed properties, setting it to dense storage.
pub ( crate ) fn override_indexed_properties ( & mut self , properties : Vec < JsValue > ) {
self . indexed_properties = IndexedProperties ::Dense ( properties ) ;
}
/// An iterator visiting all key-value pairs in arbitrary order. The iterator element type is `(PropertyKey, &'a Property)`.
/// An iterator visiting all key-value pairs in arbitrary order. The iterator element type is `(PropertyKey, &'a Property)`.
///
///
/// This iterator does not recurse down the prototype chain.
/// This iterator does not recurse down the prototype chain.
@ -131,7 +330,7 @@ impl PropertyMap {
/// This iterator does not recurse down the prototype chain.
/// This iterator does not recurse down the prototype chain.
#[ inline ]
#[ inline ]
pub fn index_properties ( & self ) -> IndexProperties < ' _ > {
pub fn index_properties ( & self ) -> IndexProperties < ' _ > {
IndexProperties ( self . indexed_properties . iter ( ) )
self . indexed_properties . iter ( )
}
}
/// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`.
/// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`.
@ -139,7 +338,7 @@ impl PropertyMap {
/// This iterator does not recurse down the prototype chain.
/// This iterator does not recurse down the prototype chain.
#[ inline ]
#[ inline ]
pub fn index_property_keys ( & self ) -> IndexPropertyKeys < ' _ > {
pub fn index_property_keys ( & self ) -> IndexPropertyKeys < ' _ > {
IndexPropertyKeys ( self . indexed_properties . keys ( ) )
self . indexed_properties . keys ( )
}
}
/// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`.
/// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`.
@ -147,7 +346,7 @@ impl PropertyMap {
/// This iterator does not recurse down the prototype chain.
/// This iterator does not recurse down the prototype chain.
#[ inline ]
#[ inline ]
pub fn index_property_values ( & self ) -> IndexPropertyValues < ' _ > {
pub fn index_property_values ( & self ) -> IndexPropertyValues < ' _ > {
IndexPropertyValues ( self . indexed_properties . values ( ) )
self . indexed_properties . values ( )
}
}
/// An iterator visiting all string key-value pairs in arbitrary order. The iterator element type is `(&'a RcString, &'a Property)`.
/// An iterator visiting all string key-value pairs in arbitrary order. The iterator element type is `(&'a RcString, &'a Property)`.
@ -177,7 +376,7 @@ impl PropertyMap {
#[ inline ]
#[ inline ]
pub fn contains_key ( & self , key : & PropertyKey ) -> bool {
pub fn contains_key ( & self , key : & PropertyKey ) -> bool {
match key {
match key {
PropertyKey ::Index ( index ) = > self . indexed_properties . contains_key ( index ) ,
PropertyKey ::Index ( index ) = > self . indexed_properties . contains_key ( * index ) ,
PropertyKey ::String ( string ) = > self . string_properties . 0. contains_key ( string ) ,
PropertyKey ::String ( string ) = > self . string_properties . 0. contains_key ( string ) ,
PropertyKey ::Symbol ( symbol ) = > self . symbol_properties . 0. contains_key ( symbol ) ,
PropertyKey ::Symbol ( symbol ) = > self . symbol_properties . 0. contains_key ( symbol ) ,
}
}
@ -197,21 +396,21 @@ impl PropertyMap {
/// An iterator over the property entries of an `Object`
/// An iterator over the property entries of an `Object`
#[ derive(Debug, Clone) ]
#[ derive(Debug, Clone) ]
pub struct Iter < ' a > {
pub struct Iter < ' a > {
indexed_properties : hash_map ::Iter < ' a , u32 , PropertyDescriptor > ,
indexed_properties : IndexProperties < ' a > ,
string_properties : indexmap ::map ::Iter < ' a , JsString , PropertyDescriptor > ,
string_properties : indexmap ::map ::Iter < ' a , JsString , PropertyDescriptor > ,
symbol_properties : indexmap ::map ::Iter < ' a , JsSymbol , PropertyDescriptor > ,
symbol_properties : indexmap ::map ::Iter < ' a , JsSymbol , PropertyDescriptor > ,
}
}
impl < ' a > Iterator for Iter < ' a > {
impl < ' a > Iterator for Iter < ' a > {
type Item = ( PropertyKey , & ' a PropertyDescriptor ) ;
type Item = ( PropertyKey , PropertyDescriptor ) ;
fn next ( & mut self ) -> Option < Self ::Item > {
fn next ( & mut self ) -> Option < Self ::Item > {
if let Some ( ( key , value ) ) = self . indexed_properties . next ( ) {
if let Some ( ( key , value ) ) = self . indexed_properties . next ( ) {
Some ( ( ( * key ) . into ( ) , value ) )
Some ( ( key . into ( ) , value ) )
} else if let Some ( ( key , value ) ) = self . string_properties . next ( ) {
} else if let Some ( ( key , value ) ) = self . string_properties . next ( ) {
Some ( ( key . clone ( ) . into ( ) , value ) )
Some ( ( key . clone ( ) . into ( ) , value . clone ( ) ) )
} else {
} else {
let ( key , value ) = self . symbol_properties . next ( ) ? ;
let ( key , value ) = self . symbol_properties . next ( ) ? ;
Some ( ( key . clone ( ) . into ( ) , value ) )
Some ( ( key . clone ( ) . into ( ) , value . clone ( ) ) )
}
}
}
}
}
}
@ -251,7 +450,7 @@ impl FusedIterator for Keys<'_> {}
pub struct Values < ' a > ( Iter < ' a > ) ;
pub struct Values < ' a > ( Iter < ' a > ) ;
impl < ' a > Iterator for Values < ' a > {
impl < ' a > Iterator for Values < ' a > {
type Item = & ' a PropertyDescriptor ;
type Item = PropertyDescriptor ;
fn next ( & mut self ) -> Option < Self ::Item > {
fn next ( & mut self ) -> Option < Self ::Item > {
let ( _ , value ) = self . 0. next ( ) ? ;
let ( _ , value ) = self . 0. next ( ) ? ;
Some ( value )
Some ( value )
@ -350,26 +549,48 @@ impl FusedIterator for SymbolPropertyValues<'_> {}
/// An iterator over the indexed property entries of an `Object`
/// An iterator over the indexed property entries of an `Object`
#[ derive(Debug, Clone) ]
#[ derive(Debug, Clone) ]
pub struct IndexProperties < ' a > ( hash_map ::Iter < ' a , u32 , PropertyDescriptor > ) ;
pub enum IndexProperties < ' a > {
Dense ( std ::iter ::Enumerate < std ::slice ::Iter < ' a , JsValue > > ) ,
Sparse ( hash_map ::Iter < ' a , u32 , PropertyDescriptor > ) ,
}
impl < ' a > Iterator for IndexProperties < ' a > {
impl < ' a > Iterator for IndexProperties < ' a > {
type Item = ( & ' a u32 , & ' a PropertyDescriptor ) ;
type Item = ( u32 , PropertyDescriptor ) ;
#[ inline ]
#[ inline ]
fn next ( & mut self ) -> Option < Self ::Item > {
fn next ( & mut self ) -> Option < Self ::Item > {
self . 0. next ( )
match self {
Self ::Dense ( vec ) = > vec . next ( ) . map ( | ( index , value ) | {
(
index as u32 ,
PropertyDescriptorBuilder ::new ( )
. writable ( true )
. configurable ( true )
. enumerable ( true )
. value ( value . clone ( ) )
. build ( ) ,
)
} ) ,
Self ::Sparse ( map ) = > map . next ( ) . map ( | ( index , value ) | ( * index , value . clone ( ) ) ) ,
}
}
}
#[ inline ]
#[ inline ]
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
self . 0. size_hint ( )
match self {
Self ::Dense ( vec ) = > vec . size_hint ( ) ,
Self ::Sparse ( map ) = > map . size_hint ( ) ,
}
}
}
}
}
impl ExactSizeIterator for IndexProperties < ' _ > {
impl ExactSizeIterator for IndexProperties < ' _ > {
#[ inline ]
#[ inline ]
fn len ( & self ) -> usize {
fn len ( & self ) -> usize {
self . 0. len ( )
match self {
Self ::Dense ( vec ) = > vec . len ( ) ,
Self ::Sparse ( map ) = > map . len ( ) ,
}
}
}
}
}
@ -377,26 +598,38 @@ impl FusedIterator for IndexProperties<'_> {}
/// An iterator over the index keys (`u32`) of an `Object`.
/// An iterator over the index keys (`u32`) of an `Object`.
#[ derive(Debug, Clone) ]
#[ derive(Debug, Clone) ]
pub struct IndexPropertyKeys < ' a > ( hash_map ::Keys < ' a , u32 , PropertyDescriptor > ) ;
pub enum IndexPropertyKeys < ' a > {
Dense ( std ::ops ::Range < u32 > ) ,
Sparse ( hash_map ::Keys < ' a , u32 , PropertyDescriptor > ) ,
}
impl < ' a > Iterator for IndexPropertyKeys < ' a > {
impl < ' a > Iterator for IndexPropertyKeys < ' a > {
type Item = & ' a u32 ;
type Item = u32 ;
#[ inline ]
#[ inline ]
fn next ( & mut self ) -> Option < Self ::Item > {
fn next ( & mut self ) -> Option < Self ::Item > {
self . 0. next ( )
match self {
Self ::Dense ( vec ) = > vec . next ( ) ,
Self ::Sparse ( map ) = > map . next ( ) . copied ( ) ,
}
}
}
#[ inline ]
#[ inline ]
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
self . 0. size_hint ( )
match self {
Self ::Dense ( vec ) = > vec . size_hint ( ) ,
Self ::Sparse ( map ) = > map . size_hint ( ) ,
}
}
}
}
}
impl ExactSizeIterator for IndexPropertyKeys < ' _ > {
impl ExactSizeIterator for IndexPropertyKeys < ' _ > {
#[ inline ]
#[ inline ]
fn len ( & self ) -> usize {
fn len ( & self ) -> usize {
self . 0. len ( )
match self {
Self ::Dense ( vec ) = > vec . len ( ) ,
Self ::Sparse ( map ) = > map . len ( ) ,
}
}
}
}
}
@ -404,26 +637,45 @@ impl FusedIterator for IndexPropertyKeys<'_> {}
/// An iterator over the index values (`Property`) of an `Object`.
/// An iterator over the index values (`Property`) of an `Object`.
#[ derive(Debug, Clone) ]
#[ derive(Debug, Clone) ]
pub struct IndexPropertyValues < ' a > ( hash_map ::Values < ' a , u32 , PropertyDescriptor > ) ;
pub enum IndexPropertyValues < ' a > {
Dense ( std ::slice ::Iter < ' a , JsValue > ) ,
Sparse ( hash_map ::Values < ' a , u32 , PropertyDescriptor > ) ,
}
impl < ' a > Iterator for IndexPropertyValues < ' a > {
impl < ' a > Iterator for IndexPropertyValues < ' a > {
type Item = & ' a PropertyDescriptor ;
type Item = PropertyDescriptor ;
#[ inline ]
#[ inline ]
fn next ( & mut self ) -> Option < Self ::Item > {
fn next ( & mut self ) -> Option < Self ::Item > {
self . 0. next ( )
match self {
Self ::Dense ( vec ) = > vec . next ( ) . map ( | value | {
PropertyDescriptorBuilder ::new ( )
. writable ( true )
. configurable ( true )
. enumerable ( true )
. value ( value . clone ( ) )
. build ( )
} ) ,
Self ::Sparse ( map ) = > map . next ( ) . cloned ( ) ,
}
}
}
#[ inline ]
#[ inline ]
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
self . 0. size_hint ( )
match self {
Self ::Dense ( vec ) = > vec . size_hint ( ) ,
Self ::Sparse ( map ) = > map . size_hint ( ) ,
}
}
}
}
}
impl ExactSizeIterator for IndexPropertyValues < ' _ > {
impl ExactSizeIterator for IndexPropertyValues < ' _ > {
#[ inline ]
#[ inline ]
fn len ( & self ) -> usize {
fn len ( & self ) -> usize {
self . 0. len ( )
match self {
Self ::Dense ( vec ) = > vec . len ( ) ,
Self ::Sparse ( map ) = > map . len ( ) ,
}
}
}
}
}