@ -6,8 +6,8 @@
//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
use crate ::{
object ::Object ,
property ::{ Attribute , Property , PropertyKey } ,
object ::{ GcObject , Object } ,
property ::{ AccessorDescriptor , A ttribute , DataDescriptor , PropertyDescriptor , PropertyKey } ,
value ::{ same_value , Value } ,
BoaProfiler , Context , Result ,
} ;
@ -19,12 +19,12 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
pub fn has_property ( & self , property_ key : & PropertyKey ) -> bool {
let prop = self . get_own_property ( property_ key) ;
pub fn has_property ( & self , key : & PropertyKey ) -> bool {
let prop = self . get_own_property ( key ) ;
if prop . is_none ( ) {
let parent = self . get_prototype_of ( ) ;
return if let Value ::Object ( ref object ) = parent {
object . borrow ( ) . has_property ( property_ key)
object . borrow ( ) . has_property ( key )
} else {
false
} ;
@ -56,92 +56,67 @@ impl Object {
}
/// Delete property.
pub fn delete ( & mut self , property_key : & PropertyKey ) -> bool {
let desc = self . get_own_property ( property_key ) ;
if desc
. value
. clone ( )
. expect ( "unable to get value" )
. is_undefined ( )
{
return true ;
}
if desc . configurable_or ( false ) {
self . remove_property ( & property_key ) ;
return true ;
pub fn delete ( & mut self , key : & PropertyKey ) -> bool {
match self . get_own_property ( key ) {
Some ( desc ) if desc . configurable ( ) = > {
self . remove_property ( & key ) ;
true
}
Some ( _ ) = > false ,
None = > true ,
}
false
}
/// [[Get]]
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver
pub fn get ( & self , property_key : & PropertyKey ) -> Value {
let desc = self . get_own_property ( property_key ) ;
if desc . value . clone ( ) . is_none ( )
| | desc
. value
. clone ( )
. expect ( "Failed to get object" )
. is_undefined ( )
{
// parent will either be null or an Object
let parent = self . get_prototype_of ( ) ;
if parent . is_null ( ) {
return Value ::undefined ( ) ;
}
return parent . get_field ( property_key . clone ( ) ) ;
}
if desc . is_data_descriptor ( ) {
return desc . value . clone ( ) . expect ( "failed to extract value" ) ;
}
pub fn get ( & self , key : & PropertyKey ) -> Value {
match self . get_own_property ( key ) {
None = > {
// parent will either be null or an Object
let parent = self . get_prototype_of ( ) ;
if parent . is_null ( ) {
return Value ::undefined ( ) ;
}
let getter = desc . get . clone ( ) ;
if getter . is_none ( ) | | getter . expect ( "Failed to get object" ) . is_undefined ( ) {
return Value ::undefined ( ) ;
parent . get_field ( key . clone ( ) )
}
Some ( ref desc ) = > match desc {
PropertyDescriptor ::Accessor ( _ ) = > todo! ( ) ,
PropertyDescriptor ::Data ( desc ) = > desc . value ( ) ,
} ,
}
// TODO: Call getter from here!
Value ::undefined ( )
}
/// [[Set]]
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
pub fn set ( & mut self , property_ key : PropertyKey , val : Value ) -> bool {
pub fn set ( & mut self , key : PropertyKey , val : Value ) -> bool {
let _timer = BoaProfiler ::global ( ) . start_event ( "Object::set" , "object" ) ;
// Fetch property key
let mut own_desc = self . get_own_property ( & property_key ) ;
// [2]
if own_desc . is_none ( ) {
let own_desc = if let Some ( desc ) = self . get_own_property ( & key ) {
desc
} else {
let parent = self . get_prototype_of ( ) ;
if ! parent . is_null ( ) {
// TODO: come back to this
}
own_desc = Property ::data_descriptor (
DataDescriptor ::new (
Value ::undefined ( ) ,
Attribute ::WRITABLE | Attribute ::ENUMERABLE | Attribute ::CONFIGURABLE ,
) ;
}
// [3]
if own_desc . is_data_descriptor ( ) {
if ! own_desc . writable ( ) {
return false ;
}
)
. into ( )
} ;
// Change value on the current descriptor
own_desc = own_desc . value ( val ) ;
return self . define_own_property ( property_key , own_desc ) ;
}
// [4]
debug_assert! ( own_desc . is_accessor_descriptor ( ) ) ;
match own_desc . set {
None = > false ,
Some ( _ ) = > {
unimplemented! ( ) ;
match & own_desc {
PropertyDescriptor ::Data ( desc ) = > {
if ! desc . writable ( ) {
return false ;
}
let desc = DataDescriptor ::new ( val , own_desc . attributes ( ) ) . into ( ) ;
self . define_own_property ( key , desc )
}
PropertyDescriptor ::Accessor ( _ ) = > todo! ( ) ,
}
}
@ -151,102 +126,84 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
pub fn define_own_property < K > ( & mut self , key : K , desc : Property ) -> bool
pub fn define_own_property < K > ( & mut self , key : K , desc : PropertyDescriptor ) -> bool
where
K : Into < PropertyKey > ,
{
let _timer = BoaProfiler ::global ( ) . start_event ( "Object::define_own_property" , "object" ) ;
let key = key . into ( ) ;
let mut current = self . get_own_property ( & key ) ;
let extensible = self . is_extensible ( ) ;
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
// There currently isn't a property, lets create a new one
if current . value . is_none ( ) | | current . value . as_ref ( ) . expect ( "failed" ) . is_undefined ( ) {
let current = if let Some ( desc ) = self . get_own_property ( & key ) {
desc
} else {
if ! extensible {
return false ;
}
self . insert ( key , desc ) ;
return true ;
}
// If every field is absent we don't need to set anything
if desc . is_none ( ) {
return true ;
}
} ;
// 4
if ! current . configurable_or ( false ) {
if desc . configurable_or ( false ) {
if ! current . configurable ( ) {
if desc . configurable ( ) {
return false ;
}
if desc . enumerable_or ( false ) ! = current . enumerable_or ( false ) {
if desc . enumerable ( ) ! = current . enumerable ( ) {
return false ;
}
}
// 5
if desc . is_generic_descriptor ( ) {
// 6
} else if current . is_data_descriptor ( ) ! = desc . is_data_descriptor ( ) {
// a
if ! current . configurable ( ) {
return false ;
}
// b
if current . is_data_descriptor ( ) {
// Convert to accessor
current . value = None ;
current . attribute . remove ( Attribute ::WRITABLE ) ;
} else {
// c
// convert to data
current . get = None ;
current . set = None ;
}
match ( & current , & desc ) {
( PropertyDescriptor ::Data ( current ) , PropertyDescriptor ::Data ( desc ) ) = > {
if ! current . configurable ( ) & & ! current . writable ( ) {
if desc . writable ( ) {
return false ;
}
self . insert ( key , current ) ;
return true ;
// 7
} else if current . is_data_descriptor ( ) & & desc . is_data_descriptor ( ) {
// a
if ! current . configurable ( ) & & ! current . writable ( ) {
if desc . writable_or ( false ) {
return false ;
if ! same_value ( & desc . value ( ) , & current . value ( ) ) {
return false ;
}
}
if desc . value . is_some ( )
& & ! same_value (
& desc . value . clone ( ) . unwrap ( ) ,
& current . value . clone ( ) . unwrap ( ) ,
)
{
}
( PropertyDescriptor ::Data ( current ) , PropertyDescriptor ::Accessor ( _ ) ) = > {
if ! current . configurable ( ) {
return false ;
}
let current = AccessorDescriptor ::new ( None , None , current . attributes ( ) ) ;
self . insert ( key , current ) ;
return true ;
}
// 8
} else {
if ! current . configurable ( ) {
if desc . set . is_some ( )
& & ! same_value ( & desc . set . clone ( ) . unwrap ( ) , & current . set . clone ( ) . unwrap ( ) )
{
( PropertyDescriptor ::Accessor ( current ) , PropertyDescriptor ::Data ( _ ) ) = > {
if ! current . configurable ( ) {
return false ;
}
if desc . get . is_some ( )
& & ! same_value ( & desc . get . clone ( ) . unwrap ( ) , & current . get . clone ( ) . unwrap ( ) )
{
return false ;
let current = DataDescriptor ::new ( Value ::undefined ( ) , current . attributes ( ) ) ;
self . insert ( key , current ) ;
return true ;
}
( PropertyDescriptor ::Accessor ( current ) , PropertyDescriptor ::Accessor ( desc ) ) = > {
if ! current . configurable ( ) {
if let ( Some ( current_get ) , Some ( desc_get ) ) = ( current . getter ( ) , desc . getter ( ) ) {
if ! GcObject ::equals ( & current_get , & desc_get ) {
return false ;
}
}
if let ( Some ( current_set ) , Some ( desc_set ) ) = ( current . setter ( ) , desc . setter ( ) ) {
if ! GcObject ::equals ( & current_set , & desc_set ) {
return false ;
}
}
}
}
return true ;
}
// 9
self . insert ( key , desc ) ;
true
}
@ -259,27 +216,16 @@ impl Object {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
pub fn get_own_property ( & self , key : & PropertyKey ) -> Property {
pub fn get_own_property ( & self , key : & PropertyKey ) -> Option < PropertyDescriptor > {
let _timer = BoaProfiler ::global ( ) . start_event ( "Object::get_own_property" , "object" ) ;
// Prop could either be a String or Symbol
let property = match key {
PropertyKey ::Index ( index ) = > self . indexed_properties . get ( & index ) ,
PropertyKey ::String ( ref st ) = > self . string_properties . get ( st ) ,
PropertyKey ::Symbol ( ref symbol ) = > self . symbol_properties . get ( symbol ) ,
} ;
property . map_or_else ( Property ::empty , | v | {
let mut d = Property ::empty ( ) ;
if v . is_data_descriptor ( ) {
d . value = v . value . clone ( ) ;
} else {
debug_assert! ( v . is_accessor_descriptor ( ) ) ;
d . get = v . get . clone ( ) ;
d . set = v . set . clone ( ) ;
}
d . attribute = v . attribute ;
d
} )
property . cloned ( )
}
/// Essential internal method OwnPropertyKeys
@ -301,14 +247,15 @@ impl Object {
pub fn define_properties ( & mut self , props : Value , ctx : & mut Context ) -> Result < ( ) > {
let props = props . to_object ( ctx ) ? ;
let keys = props . borrow ( ) . own_property_keys ( ) ;
let mut descriptors : Vec < ( PropertyKey , Property ) > = Vec ::new ( ) ;
let mut descriptors : Vec < ( PropertyKey , PropertyDescriptor ) > = Vec ::new ( ) ;
for next_key in keys {
let prop_desc = props . borrow ( ) . get_own_property ( & next_key ) ;
if prop_desc . enumerable ( ) {
let desc_obj = props . borrow ( ) . get ( & next_key ) ;
let desc = Property ::from ( & desc_obj ) ;
descriptors . push ( ( next_key , desc ) ) ;
if let Some ( prop_desc ) = props . borrow ( ) . get_own_property ( & next_key ) {
if prop_desc . enumerable ( ) {
let desc_obj = props . borrow ( ) . get ( & next_key ) ;
let desc = desc_obj . to_property_descriptor ( ctx ) ? ;
descriptors . push ( ( next_key , desc ) ) ;
}
}
}
@ -319,45 +266,46 @@ impl Object {
Ok ( ( ) )
}
// /// `Object.setPropertyOf(obj, prototype)`
// ///
// /// This method sets the prototype (i.e., the internal `[[Prototype]]` property)
// /// of a specified object to another object or `null`.
// ///
// /// More information:
// /// - [ECMAScript reference][spec]
// /// - [MDN documentation][mdn]
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
// pub fn set_prototype_of(&mut self, val: Value) -> bool {
// debug_assert!(val.is_object() || val.is_null());
// let current = self.prototype.clone();
// if same_value(¤t, &val) {
// return true;
// }
// if !self.is_extensible() {
// return false;
// }
// let mut p = val.clone();
// let mut done = false;
// while !done {
// if p.is_null() {
// done = true
// } else if same_value(&Value::from(self.clone()), &p) {
// return false;
// } else {
// let prototype = p
// .as_object()
// .expect("prototype should be null or object")
// .prototype
// .clone();
// p = prototype;
// }
// }
// self.prototype = val;
// true
// }
/// `Object.setPropertyOf(obj, prototype)`
///
/// This method sets the prototype (i.e., the internal `[[Prototype]]` property)
/// of a specified object to another object or `null`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
pub fn set_prototype_of ( & mut self , _val : Value ) -> bool {
// debug_assert!(val.is_object() || val.is_null());
// let current = self.prototype.clone();
// if same_value(¤t, &val) {
// return true;
// }
// if !self.is_extensible() {
// return false;
// }
// let mut p = val.clone();
// let mut done = false;
// while !done {
// if p.is_null() {
// done = true
// } else if same_value(&Value::from(self.clone()), &p) {
// return false;
// } else {
// let prototype = p
// .as_object()
// .expect("prototype should be null or object")
// .prototype
// .clone();
// p = prototype;
// }
// }
// self.prototype = val;
// true
todo! ( "Object.setPropertyOf(obj, prototype)" )
}
/// Returns either the prototype or null
///
@ -373,10 +321,12 @@ impl Object {
/// Helper function for property insertion.
#[ inline ]
pub ( crate ) fn insert < K > ( & mut self , key : K , property : Property ) -> Option < Property >
pub ( crate ) fn insert < K , P > ( & mut self , key : K , property : P ) -> Option < PropertyDescriptor >
where
K : Into < PropertyKey > ,
P : Into < PropertyDescriptor > ,
{
let property = property . into ( ) ;
match key . into ( ) {
PropertyKey ::Index ( index ) = > self . indexed_properties . insert ( index , property ) ,
PropertyKey ::String ( ref string ) = > {
@ -390,7 +340,7 @@ impl Object {
/// Helper function for property removal.
#[ inline ]
pub ( crate ) fn remove_property ( & mut self , key : & PropertyKey ) -> Option < Property > {
pub ( crate ) fn remove_property ( & mut self , key : & PropertyKey ) -> Option < PropertyDescriptor > {
match key {
PropertyKey ::Index ( index ) = > self . indexed_properties . remove ( & index ) ,
PropertyKey ::String ( ref string ) = > self . string_properties . remove ( string ) ,
@ -408,20 +358,11 @@ impl Object {
key : K ,
value : V ,
attribute : Attribute ,
) -> Option < Property >
) -> Option < PropertyDescriptor >
where
K : Into < PropertyKey > ,
V : Into < Value > ,
{
self . insert (
key . into ( ) ,
Property ::data_descriptor (
value . into ( ) ,
attribute
| Attribute ::HAS_WRITABLE
| Attribute ::HAS_ENUMERABLE
| Attribute ::HAS_CONFIGURABLE ,
) ,
)
self . insert ( key . into ( ) , DataDescriptor ::new ( value , attribute ) )
}
}