|
|
|
@ -18,14 +18,226 @@ impl GcObject {
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-hasproperty
|
|
|
|
|
// NOTE: for now context is not used but it will in the future.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn has_property(&self, key: &PropertyKey) -> bool { |
|
|
|
|
let prop = self.get_own_property(key); |
|
|
|
|
pub fn has_property<K>(&self, key: K, _context: &mut Context) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Return ? O.[[HasProperty]](P).
|
|
|
|
|
Ok(self.__has_property__(&key.into())) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Check if it is extensible.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isextensible-o
|
|
|
|
|
// NOTE: for now context is not used but it will in the future.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_extensible(&self, _context: &mut Context) -> Result<bool> { |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Return ? O.[[IsExtensible]]().
|
|
|
|
|
Ok(self.__is_extensible__()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Delete property, if deleted return `true`.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn delete<K>(&self, key: K) -> bool |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
self.__delete__(&key.into()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Defines the property or throws a `TypeError` if the operation fails.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [EcmaScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn delete_property_or_throw<K>(&self, key: K, context: &mut Context) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
let key = key.into(); |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Let success be ? O.[[Delete]](P).
|
|
|
|
|
let success = self.__delete__(&key); |
|
|
|
|
// 4. If success is false, throw a TypeError exception.
|
|
|
|
|
if !success { |
|
|
|
|
return Err(context.construct_type_error(format!("cannot delete property: {}", key))); |
|
|
|
|
} |
|
|
|
|
// 5. Return success.
|
|
|
|
|
Ok(success) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Check if object has an own property.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-hasownproperty
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn has_own_property<K>(&self, key: K, _context: &mut Context) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
let key = key.into(); |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Let desc be ? O.[[GetOwnProperty]](P).
|
|
|
|
|
let desc = self.__get_own_property__(&key); |
|
|
|
|
// 4. If desc is undefined, return false.
|
|
|
|
|
// 5. Return true.
|
|
|
|
|
Ok(desc.is_some()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get property from object or throw.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-get-o-p
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn get<K>(&self, key: K, context: &mut Context) -> Result<Value> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Return ? O.[[Get]](P, O).
|
|
|
|
|
self.__get__(&key.into(), self.clone().into(), context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// set property of object or throw if bool flag is passed.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn set<K, V>(&self, key: K, value: V, throw: bool, context: &mut Context) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
V: Into<Value>, |
|
|
|
|
{ |
|
|
|
|
let key = key.into(); |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Assert: Type(Throw) is Boolean.
|
|
|
|
|
// 4. Let success be ? O.[[Set]](P, V, O).
|
|
|
|
|
let success = self.__set__(key.clone(), value.into(), self.clone().into(), context)?; |
|
|
|
|
// 5. If success is false and Throw is true, throw a TypeError exception.
|
|
|
|
|
if !success && throw { |
|
|
|
|
return Err( |
|
|
|
|
context.construct_type_error(format!("cannot set non-writable property: {}", key)) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
// 6. Return success.
|
|
|
|
|
Ok(success) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Define property or throw.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn define_property_or_throw<K, P>( |
|
|
|
|
&mut self, |
|
|
|
|
key: K, |
|
|
|
|
desc: P, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
P: Into<PropertyDescriptor>, |
|
|
|
|
{ |
|
|
|
|
let key = key.into(); |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Let success be ? O.[[DefineOwnProperty]](P, desc).
|
|
|
|
|
let success = self.__define_own_property__(key.clone(), desc.into(), context)?; |
|
|
|
|
// 4. If success is false, throw a TypeError exception.
|
|
|
|
|
if !success { |
|
|
|
|
return Err(context.construct_type_error(format!("cannot redefine property: {}", key))); |
|
|
|
|
} |
|
|
|
|
// 5. Return success.
|
|
|
|
|
Ok(success) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create data property
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow
|
|
|
|
|
pub fn create_data_property<K, V>( |
|
|
|
|
&self, |
|
|
|
|
key: K, |
|
|
|
|
value: V, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
V: Into<Value>, |
|
|
|
|
{ |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
|
|
|
|
|
let new_desc = DataDescriptor::new( |
|
|
|
|
value, |
|
|
|
|
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, |
|
|
|
|
); |
|
|
|
|
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
|
|
|
|
|
self.__define_own_property__(key.into(), new_desc.into(), context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create data property or throw
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow
|
|
|
|
|
pub fn create_data_property_or_throw<K, V>( |
|
|
|
|
&self, |
|
|
|
|
key: K, |
|
|
|
|
value: V, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
V: Into<Value>, |
|
|
|
|
{ |
|
|
|
|
let key = key.into(); |
|
|
|
|
// 1. Assert: Type(O) is Object.
|
|
|
|
|
// 2. Assert: IsPropertyKey(P) is true.
|
|
|
|
|
// 3. Let success be ? CreateDataProperty(O, P, V).
|
|
|
|
|
let success = self.create_data_property(key.clone(), value, context)?; |
|
|
|
|
// 4. If success is false, throw a TypeError exception.
|
|
|
|
|
if !success { |
|
|
|
|
return Err(context.construct_type_error(format!("cannot redefine property: {}", key))); |
|
|
|
|
} |
|
|
|
|
// 5. Return success.
|
|
|
|
|
Ok(success) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `[[hasProperty]]`
|
|
|
|
|
#[inline] |
|
|
|
|
pub(crate) fn __has_property__(&self, key: &PropertyKey) -> bool { |
|
|
|
|
let prop = self.__get_own_property__(key); |
|
|
|
|
if prop.is_none() { |
|
|
|
|
let parent = self.get_prototype_of(); |
|
|
|
|
let parent = self.__get_prototype_of__(); |
|
|
|
|
return if let Value::Object(ref object) = parent { |
|
|
|
|
object.has_property(key) |
|
|
|
|
object.__has_property__(key) |
|
|
|
|
} else { |
|
|
|
|
false |
|
|
|
|
}; |
|
|
|
@ -40,7 +252,7 @@ impl GcObject {
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn is_extensible(&self) -> bool { |
|
|
|
|
pub(crate) fn __is_extensible__(&self) -> bool { |
|
|
|
|
self.borrow().extensible |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -51,15 +263,15 @@ impl GcObject {
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn prevent_extensions(&mut self) -> bool { |
|
|
|
|
pub fn __prevent_extensions__(&mut self) -> bool { |
|
|
|
|
self.borrow_mut().extensible = false; |
|
|
|
|
true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Delete property.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn delete(&mut self, key: &PropertyKey) -> bool { |
|
|
|
|
match self.get_own_property(key) { |
|
|
|
|
pub(crate) fn __delete__(&self, key: &PropertyKey) -> bool { |
|
|
|
|
match self.__get_own_property__(key) { |
|
|
|
|
Some(desc) if desc.configurable() => { |
|
|
|
|
self.remove(&key); |
|
|
|
|
true |
|
|
|
@ -70,13 +282,17 @@ impl GcObject {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `[[Get]]`
|
|
|
|
|
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver>
|
|
|
|
|
pub fn get(&self, key: &PropertyKey, receiver: Value, context: &mut Context) -> Result<Value> { |
|
|
|
|
match self.get_own_property(key) { |
|
|
|
|
pub fn __get__( |
|
|
|
|
&self, |
|
|
|
|
key: &PropertyKey, |
|
|
|
|
receiver: Value, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<Value> { |
|
|
|
|
match self.__get_own_property__(key) { |
|
|
|
|
None => { |
|
|
|
|
// parent will either be null or an Object
|
|
|
|
|
if let Some(parent) = self.get_prototype_of().as_object() { |
|
|
|
|
Ok(parent.get(key, receiver, context)?) |
|
|
|
|
if let Some(parent) = self.__get_prototype_of__().as_object() { |
|
|
|
|
Ok(parent.__get__(key, receiver, context)?) |
|
|
|
|
} else { |
|
|
|
|
Ok(Value::undefined()) |
|
|
|
|
} |
|
|
|
@ -92,9 +308,8 @@ impl GcObject {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `[[Set]]`
|
|
|
|
|
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
|
|
|
|
|
pub fn set( |
|
|
|
|
&mut self, |
|
|
|
|
pub fn __set__( |
|
|
|
|
&self, |
|
|
|
|
key: PropertyKey, |
|
|
|
|
value: Value, |
|
|
|
|
receiver: Value, |
|
|
|
@ -103,10 +318,10 @@ impl GcObject {
|
|
|
|
|
let _timer = BoaProfiler::global().start_event("Object::set", "object"); |
|
|
|
|
|
|
|
|
|
// Fetch property key
|
|
|
|
|
let own_desc = if let Some(desc) = self.get_own_property(&key) { |
|
|
|
|
let own_desc = if let Some(desc) = self.__get_own_property__(&key) { |
|
|
|
|
desc |
|
|
|
|
} else if let Some(ref mut parent) = self.get_prototype_of().as_object() { |
|
|
|
|
return parent.set(key, value, receiver, context); |
|
|
|
|
} else if let Some(ref mut parent) = self.__get_prototype_of__().as_object() { |
|
|
|
|
return parent.__set__(key, value, receiver, context); |
|
|
|
|
} else { |
|
|
|
|
DataDescriptor::new(Value::undefined(), Attribute::all()).into() |
|
|
|
|
}; |
|
|
|
@ -117,14 +332,14 @@ impl GcObject {
|
|
|
|
|
return Ok(false); |
|
|
|
|
} |
|
|
|
|
if let Some(ref mut receiver) = receiver.as_object() { |
|
|
|
|
if let Some(ref existing_desc) = receiver.get_own_property(&key) { |
|
|
|
|
if let Some(ref existing_desc) = receiver.__get_own_property__(&key) { |
|
|
|
|
match existing_desc { |
|
|
|
|
PropertyDescriptor::Accessor(_) => Ok(false), |
|
|
|
|
PropertyDescriptor::Data(existing_data_desc) => { |
|
|
|
|
if !existing_data_desc.writable() { |
|
|
|
|
return Ok(false); |
|
|
|
|
} |
|
|
|
|
receiver.define_own_property( |
|
|
|
|
receiver.__define_own_property__( |
|
|
|
|
key, |
|
|
|
|
DataDescriptor::new(value, existing_data_desc.attributes()) |
|
|
|
|
.into(), |
|
|
|
@ -133,7 +348,7 @@ impl GcObject {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
receiver.define_own_property( |
|
|
|
|
receiver.__define_own_property__( |
|
|
|
|
key, |
|
|
|
|
DataDescriptor::new(value, Attribute::all()).into(), |
|
|
|
|
context, |
|
|
|
@ -151,21 +366,13 @@ impl GcObject {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Define an own property.
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [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, |
|
|
|
|
/// `[[defineOwnProperty]]`
|
|
|
|
|
pub fn __define_own_property__( |
|
|
|
|
&self, |
|
|
|
|
key: PropertyKey, |
|
|
|
|
desc: PropertyDescriptor, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
) -> Result<bool> { |
|
|
|
|
if self.is_array() { |
|
|
|
|
self.array_define_own_property(key, desc, context) |
|
|
|
|
} else { |
|
|
|
@ -179,16 +386,12 @@ impl GcObject {
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty
|
|
|
|
|
pub fn ordinary_define_own_property<K>(&mut self, key: K, desc: PropertyDescriptor) -> bool |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
pub fn ordinary_define_own_property(&self, key: PropertyKey, desc: PropertyDescriptor) -> bool { |
|
|
|
|
let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object"); |
|
|
|
|
|
|
|
|
|
let key = key.into(); |
|
|
|
|
let extensible = self.is_extensible(); |
|
|
|
|
let extensible = self.__is_extensible__(); |
|
|
|
|
|
|
|
|
|
let current = if let Some(desc) = self.get_own_property(&key) { |
|
|
|
|
let current = if let Some(desc) = self.__get_own_property__(&key) { |
|
|
|
|
desc |
|
|
|
|
} else { |
|
|
|
|
if !extensible { |
|
|
|
@ -294,25 +497,21 @@ impl GcObject {
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc
|
|
|
|
|
fn array_define_own_property<K>( |
|
|
|
|
&mut self, |
|
|
|
|
key: K, |
|
|
|
|
fn array_define_own_property( |
|
|
|
|
&self, |
|
|
|
|
key: PropertyKey, |
|
|
|
|
desc: PropertyDescriptor, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<bool> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
{ |
|
|
|
|
let key = key.into(); |
|
|
|
|
) -> Result<bool> { |
|
|
|
|
match key { |
|
|
|
|
PropertyKey::String(ref s) if s == "length" => { |
|
|
|
|
match desc { |
|
|
|
|
PropertyDescriptor::Accessor(_) => { |
|
|
|
|
return Ok(self.ordinary_define_own_property("length", desc)) |
|
|
|
|
return Ok(self.ordinary_define_own_property("length".into(), desc)) |
|
|
|
|
} |
|
|
|
|
PropertyDescriptor::Data(ref d) => { |
|
|
|
|
if d.value().is_undefined() { |
|
|
|
|
return Ok(self.ordinary_define_own_property("length", desc)); |
|
|
|
|
return Ok(self.ordinary_define_own_property("length".into(), desc)); |
|
|
|
|
} |
|
|
|
|
let new_len = d.value().to_u32(context)?; |
|
|
|
|
let number_len = d.value().to_number(context)?; |
|
|
|
@ -322,11 +521,13 @@ impl GcObject {
|
|
|
|
|
} |
|
|
|
|
let mut new_len_desc = |
|
|
|
|
PropertyDescriptor::Data(DataDescriptor::new(new_len, d.attributes())); |
|
|
|
|
let old_len_desc = self.get_own_property(&"length".into()).unwrap(); |
|
|
|
|
let old_len_desc = self.__get_own_property__(&"length".into()).unwrap(); |
|
|
|
|
let old_len_desc = old_len_desc.as_data_descriptor().unwrap(); |
|
|
|
|
let old_len = old_len_desc.value(); |
|
|
|
|
if new_len >= old_len.to_u32(context)? { |
|
|
|
|
return Ok(self.ordinary_define_own_property("length", new_len_desc)); |
|
|
|
|
return Ok( |
|
|
|
|
self.ordinary_define_own_property("length".into(), new_len_desc) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
if !old_len_desc.writable() { |
|
|
|
|
return Ok(false); |
|
|
|
@ -342,7 +543,8 @@ impl GcObject {
|
|
|
|
|
)); |
|
|
|
|
false |
|
|
|
|
}; |
|
|
|
|
if !self.ordinary_define_own_property("length", new_len_desc.clone()) { |
|
|
|
|
if !self.ordinary_define_own_property("length".into(), new_len_desc.clone()) |
|
|
|
|
{ |
|
|
|
|
return Ok(false); |
|
|
|
|
} |
|
|
|
|
let keys_to_delete = { |
|
|
|
@ -356,7 +558,7 @@ impl GcObject {
|
|
|
|
|
keys |
|
|
|
|
}; |
|
|
|
|
for key in keys_to_delete.into_iter().rev() { |
|
|
|
|
if !self.delete(&key.into()) { |
|
|
|
|
if !self.__delete__(&key.into()) { |
|
|
|
|
let mut new_len_desc_attribute = new_len_desc.attributes(); |
|
|
|
|
if !new_writable { |
|
|
|
|
new_len_desc_attribute.set_writable(false); |
|
|
|
@ -365,7 +567,7 @@ impl GcObject {
|
|
|
|
|
key + 1, |
|
|
|
|
new_len_desc_attribute, |
|
|
|
|
)); |
|
|
|
|
self.ordinary_define_own_property("length", new_len_desc); |
|
|
|
|
self.ordinary_define_own_property("length".into(), new_len_desc); |
|
|
|
|
return Ok(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -376,14 +578,14 @@ impl GcObject {
|
|
|
|
|
new_len, |
|
|
|
|
new_desc_attr, |
|
|
|
|
)); |
|
|
|
|
self.ordinary_define_own_property("length", new_desc); |
|
|
|
|
self.ordinary_define_own_property("length".into(), new_desc); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Ok(true) |
|
|
|
|
} |
|
|
|
|
PropertyKey::Index(index) => { |
|
|
|
|
let old_len_desc = self.get_own_property(&"length".into()).unwrap(); |
|
|
|
|
let old_len_desc = self.__get_own_property__(&"length".into()).unwrap(); |
|
|
|
|
let old_len_data_desc = old_len_desc.as_data_descriptor().unwrap(); |
|
|
|
|
let old_len = old_len_data_desc.value().to_u32(context)?; |
|
|
|
|
if index >= old_len && !old_len_data_desc.writable() { |
|
|
|
@ -395,7 +597,7 @@ impl GcObject {
|
|
|
|
|
index + 1, |
|
|
|
|
old_len_data_desc.attributes(), |
|
|
|
|
)); |
|
|
|
|
self.ordinary_define_own_property("length", desc); |
|
|
|
|
self.ordinary_define_own_property("length".into(), desc); |
|
|
|
|
} |
|
|
|
|
Ok(true) |
|
|
|
|
} else { |
|
|
|
@ -409,7 +611,7 @@ impl GcObject {
|
|
|
|
|
/// Gets own property of 'Object'
|
|
|
|
|
///
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn get_own_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
|
|
|
pub fn __get_own_property__(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
|
|
|
let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); |
|
|
|
|
|
|
|
|
|
let object = self.borrow(); |
|
|
|
@ -516,9 +718,9 @@ impl GcObject {
|
|
|
|
|
let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new(); |
|
|
|
|
|
|
|
|
|
for next_key in keys { |
|
|
|
|
if let Some(prop_desc) = props.get_own_property(&next_key) { |
|
|
|
|
if let Some(prop_desc) = props.__get_own_property__(&next_key) { |
|
|
|
|
if prop_desc.enumerable() { |
|
|
|
|
let desc_obj = props.get(&next_key, props.clone().into(), context)?; |
|
|
|
|
let desc_obj = props.__get__(&next_key, props.clone().into(), context)?; |
|
|
|
|
let desc = desc_obj.to_property_descriptor(context)?; |
|
|
|
|
descriptors.push((next_key, desc)); |
|
|
|
|
} |
|
|
|
@ -526,7 +728,7 @@ impl GcObject {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (p, d) in descriptors { |
|
|
|
|
self.define_own_property(p, d, context)?; |
|
|
|
|
self.__define_own_property__(p, d, context)?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
@ -544,13 +746,13 @@ impl GcObject {
|
|
|
|
|
/// [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
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn set_prototype_of(&mut self, val: Value) -> bool { |
|
|
|
|
pub fn __set_prototype_of__(&mut self, val: Value) -> bool { |
|
|
|
|
debug_assert!(val.is_object() || val.is_null()); |
|
|
|
|
let current = self.get_prototype_of(); |
|
|
|
|
let current = self.__get_prototype_of__(); |
|
|
|
|
if Value::same_value(¤t, &val) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if !self.is_extensible() { |
|
|
|
|
if !self.__is_extensible__() { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
let mut p = val.clone(); |
|
|
|
@ -564,7 +766,7 @@ impl GcObject {
|
|
|
|
|
let prototype = p |
|
|
|
|
.as_object() |
|
|
|
|
.expect("prototype should be null or object") |
|
|
|
|
.get_prototype_of(); |
|
|
|
|
.__get_prototype_of__(); |
|
|
|
|
p = prototype; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -582,14 +784,14 @@ impl GcObject {
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
|
|
|
|
|
#[inline] |
|
|
|
|
#[track_caller] |
|
|
|
|
pub fn get_prototype_of(&self) -> Value { |
|
|
|
|
pub fn __get_prototype_of__(&self) -> Value { |
|
|
|
|
self.borrow().prototype.clone() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function for property insertion.
|
|
|
|
|
#[inline] |
|
|
|
|
#[track_caller] |
|
|
|
|
pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> Option<PropertyDescriptor> |
|
|
|
|
pub(crate) fn insert<K, P>(&self, key: K, property: P) -> Option<PropertyDescriptor> |
|
|
|
|
where |
|
|
|
|
K: Into<PropertyKey>, |
|
|
|
|
P: Into<PropertyDescriptor>, |
|
|
|
@ -600,7 +802,7 @@ impl GcObject {
|
|
|
|
|
/// Helper function for property removal.
|
|
|
|
|
#[inline] |
|
|
|
|
#[track_caller] |
|
|
|
|
pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
|
|
|
pub(crate) fn remove(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
|
|
|
self.borrow_mut().remove(key) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -610,7 +812,7 @@ impl GcObject {
|
|
|
|
|
/// with that field, otherwise None is returned.
|
|
|
|
|
#[inline] |
|
|
|
|
pub fn insert_property<K, V>( |
|
|
|
|
&mut self, |
|
|
|
|
&self, |
|
|
|
|
key: K, |
|
|
|
|
value: V, |
|
|
|
|
attribute: Attribute, |
|
|
|
@ -663,6 +865,7 @@ impl GcObject {
|
|
|
|
|
element_types: &[Type], |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> Result<Vec<Value>> { |
|
|
|
|
// 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».
|
|
|
|
|
let types = if element_types.is_empty() { |
|
|
|
|
&[ |
|
|
|
|
Type::Undefined, |
|
|
|
@ -672,24 +875,44 @@ impl GcObject {
|
|
|
|
|
Type::Symbol, |
|
|
|
|
Type::Number, |
|
|
|
|
Type::BigInt, |
|
|
|
|
Type::Symbol, |
|
|
|
|
Type::Object, |
|
|
|
|
] |
|
|
|
|
} else { |
|
|
|
|
element_types |
|
|
|
|
}; |
|
|
|
|
let len = self |
|
|
|
|
.get(&"length".into(), self.clone().into(), context)? |
|
|
|
|
.to_length(context)?; |
|
|
|
|
|
|
|
|
|
// TODO: 2. If Type(obj) is not Object, throw a TypeError exception.
|
|
|
|
|
|
|
|
|
|
// 3. Let len be ? LengthOfArrayLike(obj).
|
|
|
|
|
let len = self.length_of_array_like(context)?; |
|
|
|
|
|
|
|
|
|
// 4. Let list be a new empty List.
|
|
|
|
|
let mut list = Vec::with_capacity(len); |
|
|
|
|
|
|
|
|
|
// 5. Let index be 0.
|
|
|
|
|
// 6. Repeat, while index < len,
|
|
|
|
|
for index in 0..len { |
|
|
|
|
let next = self.get(&index.into(), self.clone().into(), context)?; |
|
|
|
|
// a. Let indexName be ! ToString(𝔽(index)).
|
|
|
|
|
// b. Let next be ? Get(obj, indexName).
|
|
|
|
|
let next = self.get(index, context)?; |
|
|
|
|
// c. If Type(next) is not an element of elementTypes, throw a TypeError exception.
|
|
|
|
|
if !types.contains(&next.get_type()) { |
|
|
|
|
return Err(context.construct_type_error("bad type")); |
|
|
|
|
} |
|
|
|
|
// d. Append next as the last element of list.
|
|
|
|
|
list.push(next.clone()); |
|
|
|
|
// e. Set index to index + 1.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 7. Return list.
|
|
|
|
|
Ok(list) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub(crate) fn length_of_array_like(&self, context: &mut Context) -> Result<usize> { |
|
|
|
|
// 1. Assert: Type(obj) is Object.
|
|
|
|
|
// 2. Return ℝ(? ToLength(? Get(obj, "length"))).
|
|
|
|
|
self.get("length", context)?.to_length(context) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Object { |
|
|
|
|