mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
726 lines
26 KiB
726 lines
26 KiB
//! This module defines the object internal methods. |
|
//! |
|
//! More information: |
|
//! - [ECMAScript reference][spec] |
|
//! |
|
//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots |
|
|
|
use crate::{ |
|
object::{GcObject, Object, ObjectData}, |
|
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, |
|
value::{Type, Value}, |
|
BoaProfiler, Context, Result, |
|
}; |
|
|
|
impl GcObject { |
|
/// Check if object has property. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p |
|
#[inline] |
|
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.has_property(key) |
|
} else { |
|
false |
|
}; |
|
} |
|
true |
|
} |
|
|
|
/// Check if it is extensible. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible |
|
#[inline] |
|
pub fn is_extensible(&self) -> bool { |
|
self.borrow().extensible |
|
} |
|
|
|
/// Disable extensibility. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions |
|
#[inline] |
|
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) { |
|
Some(desc) if desc.configurable() => { |
|
self.remove(&key); |
|
true |
|
} |
|
Some(_) => false, |
|
None => true, |
|
} |
|
} |
|
|
|
/// `[[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) { |
|
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)?) |
|
} else { |
|
Ok(Value::undefined()) |
|
} |
|
} |
|
Some(ref desc) => match desc { |
|
PropertyDescriptor::Data(desc) => Ok(desc.value()), |
|
PropertyDescriptor::Accessor(AccessorDescriptor { get: Some(get), .. }) => { |
|
get.call(&receiver, &[], context) |
|
} |
|
_ => Ok(Value::undefined()), |
|
}, |
|
} |
|
} |
|
|
|
/// `[[Set]]` |
|
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver> |
|
pub fn set( |
|
&mut self, |
|
key: PropertyKey, |
|
value: Value, |
|
receiver: Value, |
|
context: &mut Context, |
|
) -> Result<bool> { |
|
let _timer = BoaProfiler::global().start_event("Object::set", "object"); |
|
|
|
// Fetch 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 { |
|
DataDescriptor::new(Value::undefined(), Attribute::all()).into() |
|
}; |
|
|
|
match &own_desc { |
|
PropertyDescriptor::Data(desc) => { |
|
if !desc.writable() { |
|
return Ok(false); |
|
} |
|
if let Some(ref mut receiver) = receiver.as_object() { |
|
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( |
|
key, |
|
DataDescriptor::new(value, existing_data_desc.attributes()) |
|
.into(), |
|
context, |
|
) |
|
} |
|
} |
|
} else { |
|
receiver.define_own_property( |
|
key, |
|
DataDescriptor::new(value, Attribute::all()).into(), |
|
context, |
|
) |
|
} |
|
} else { |
|
Ok(false) |
|
} |
|
} |
|
PropertyDescriptor::Accessor(AccessorDescriptor { set: Some(set), .. }) => { |
|
set.call(&receiver, &[value], context)?; |
|
Ok(true) |
|
} |
|
_ => Ok(false), |
|
} |
|
} |
|
|
|
/// 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, |
|
desc: PropertyDescriptor, |
|
context: &mut Context, |
|
) -> Result<bool> |
|
where |
|
K: Into<PropertyKey>, |
|
{ |
|
if self.is_array() { |
|
self.array_define_own_property(key, desc, context) |
|
} else { |
|
Ok(self.ordinary_define_own_property(key, desc)) |
|
} |
|
} |
|
|
|
/// Define an own property for an ordinary object. |
|
/// |
|
/// More information: |
|
/// - [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>, |
|
{ |
|
let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object"); |
|
|
|
let key = key.into(); |
|
let extensible = self.is_extensible(); |
|
|
|
let current = if let Some(desc) = self.get_own_property(&key) { |
|
desc |
|
} else { |
|
if !extensible { |
|
return false; |
|
} |
|
|
|
self.insert(key, desc); |
|
return true; |
|
}; |
|
|
|
// 4 |
|
if !current.configurable() { |
|
if desc.configurable() { |
|
return false; |
|
} |
|
|
|
if desc.enumerable() != current.enumerable() { |
|
return false; |
|
} |
|
} |
|
|
|
match (¤t, &desc) { |
|
( |
|
PropertyDescriptor::Data(current), |
|
PropertyDescriptor::Accessor(AccessorDescriptor { get, set, .. }), |
|
) => { |
|
// 6. b |
|
if !current.configurable() { |
|
return false; |
|
} |
|
|
|
let current = |
|
AccessorDescriptor::new(get.clone(), set.clone(), current.attributes()); |
|
self.insert(key, current); |
|
return true; |
|
} |
|
( |
|
PropertyDescriptor::Accessor(current), |
|
PropertyDescriptor::Data(DataDescriptor { value, .. }), |
|
) => { |
|
// 6. c |
|
if !current.configurable() { |
|
return false; |
|
} |
|
|
|
let current = DataDescriptor::new(value, current.attributes()); |
|
self.insert(key, current); |
|
return true; |
|
} |
|
(PropertyDescriptor::Data(current), PropertyDescriptor::Data(desc)) => { |
|
// 7. |
|
if !current.configurable() && !current.writable() { |
|
if desc.writable() { |
|
return false; |
|
} |
|
|
|
if !Value::same_value(&desc.value(), ¤t.value()) { |
|
return false; |
|
} |
|
} |
|
} |
|
(PropertyDescriptor::Accessor(current), PropertyDescriptor::Accessor(desc)) => { |
|
// 8. |
|
if !current.configurable() { |
|
if let (Some(current_get), Some(desc_get)) = (current.getter(), desc.getter()) { |
|
if !GcObject::equals(¤t_get, &desc_get) { |
|
return false; |
|
} |
|
} |
|
|
|
if let (Some(current_set), Some(desc_set)) = (current.setter(), desc.setter()) { |
|
if !GcObject::equals(¤t_set, &desc_set) { |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
self.insert(key, desc); |
|
true |
|
} |
|
|
|
/// Define an own property for an array. |
|
/// |
|
/// More information: |
|
/// - [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, |
|
desc: PropertyDescriptor, |
|
context: &mut Context, |
|
) -> Result<bool> |
|
where |
|
K: Into<PropertyKey>, |
|
{ |
|
let key = key.into(); |
|
match key { |
|
PropertyKey::String(ref s) if s == "length" => { |
|
match desc { |
|
PropertyDescriptor::Accessor(_) => { |
|
return Ok(self.ordinary_define_own_property("length", desc)) |
|
} |
|
PropertyDescriptor::Data(ref d) => { |
|
if d.value().is_undefined() { |
|
return Ok(self.ordinary_define_own_property("length", desc)); |
|
} |
|
let new_len = d.value().to_u32(context)?; |
|
let number_len = d.value().to_number(context)?; |
|
#[allow(clippy::float_cmp)] |
|
if new_len as f64 != number_len { |
|
return Err(context.construct_range_error("bad length for array")); |
|
} |
|
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 = 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)); |
|
} |
|
if !old_len_desc.writable() { |
|
return Ok(false); |
|
} |
|
let new_writable = if new_len_desc.attributes().writable() { |
|
true |
|
} else { |
|
let mut new_attributes = new_len_desc.attributes(); |
|
new_attributes.set_writable(true); |
|
new_len_desc = PropertyDescriptor::Data(DataDescriptor::new( |
|
new_len, |
|
new_attributes, |
|
)); |
|
false |
|
}; |
|
if !self.ordinary_define_own_property("length", new_len_desc.clone()) { |
|
return Ok(false); |
|
} |
|
let keys_to_delete = { |
|
let obj = self.borrow(); |
|
let mut keys = obj |
|
.index_property_keys() |
|
.filter(|&&k| k >= new_len) |
|
.cloned() |
|
.collect::<Vec<_>>(); |
|
keys.sort_unstable(); |
|
keys |
|
}; |
|
for key in keys_to_delete.into_iter().rev() { |
|
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); |
|
} |
|
let new_len_desc = PropertyDescriptor::Data(DataDescriptor::new( |
|
key + 1, |
|
new_len_desc_attribute, |
|
)); |
|
self.ordinary_define_own_property("length", new_len_desc); |
|
return Ok(false); |
|
} |
|
} |
|
if !new_writable { |
|
let mut new_desc_attr = new_len_desc.attributes(); |
|
new_desc_attr.set_writable(false); |
|
let new_desc = PropertyDescriptor::Data(DataDescriptor::new( |
|
new_len, |
|
new_desc_attr, |
|
)); |
|
self.ordinary_define_own_property("length", new_desc); |
|
} |
|
} |
|
} |
|
Ok(true) |
|
} |
|
PropertyKey::Index(index) => { |
|
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() { |
|
return Ok(false); |
|
} |
|
if self.ordinary_define_own_property(key, desc) { |
|
if index >= old_len && index < u32::MAX { |
|
let desc = PropertyDescriptor::Data(DataDescriptor::new( |
|
index + 1, |
|
old_len_data_desc.attributes(), |
|
)); |
|
self.ordinary_define_own_property("length", desc); |
|
} |
|
Ok(true) |
|
} else { |
|
Ok(false) |
|
} |
|
} |
|
_ => Ok(self.ordinary_define_own_property(key, desc)), |
|
} |
|
} |
|
|
|
/// Gets own property of 'Object' |
|
/// |
|
#[inline] |
|
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(); |
|
match object.data { |
|
ObjectData::String(_) => self.string_exotic_get_own_property(key), |
|
_ => self.ordinary_get_own_property(key), |
|
} |
|
} |
|
|
|
/// StringGetOwnProperty abstract operation |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-stringgetownproperty |
|
#[inline] |
|
pub fn string_get_own_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
let object = self.borrow(); |
|
|
|
match key { |
|
PropertyKey::Index(index) => { |
|
let string = object.as_string().unwrap(); |
|
let pos = *index as usize; |
|
|
|
if pos >= string.len() { |
|
return None; |
|
} |
|
|
|
let result_str = string.encode_utf16().nth(pos).map(|utf16_val| { |
|
char::from_u32(u32::from(utf16_val)) |
|
.map_or_else(|| Value::from(format!("\\u{:x}", utf16_val)), Value::from) |
|
})?; |
|
|
|
let desc = PropertyDescriptor::from(DataDescriptor::new( |
|
result_str, |
|
Attribute::READONLY | Attribute::ENUMERABLE | Attribute::PERMANENT, |
|
)); |
|
|
|
Some(desc) |
|
} |
|
_ => None, |
|
} |
|
} |
|
|
|
/// Gets own property of 'String' exotic object |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-string-exotic-objects-getownproperty-p |
|
#[inline] |
|
pub fn string_exotic_get_own_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
let desc = self.ordinary_get_own_property(key); |
|
|
|
if desc.is_some() { |
|
desc |
|
} else { |
|
self.string_get_own_property(key) |
|
} |
|
} |
|
|
|
/// The specification returns a Property Descriptor or Undefined. |
|
/// |
|
/// These are 2 separate types and we can't do that here. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p |
|
#[inline] |
|
pub fn ordinary_get_own_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
let object = self.borrow(); |
|
let property = match key { |
|
PropertyKey::Index(index) => object.indexed_properties.get(&index), |
|
PropertyKey::String(ref st) => object.string_properties.get(st), |
|
PropertyKey::Symbol(ref symbol) => object.symbol_properties.get(symbol), |
|
}; |
|
|
|
property.cloned() |
|
} |
|
|
|
/// Essential internal method OwnPropertyKeys |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#table-essential-internal-methods |
|
#[inline] |
|
#[track_caller] |
|
pub fn own_property_keys(&self) -> Vec<PropertyKey> { |
|
self.borrow().keys().collect() |
|
} |
|
|
|
/// The abstract operation ObjectDefineProperties |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties |
|
#[inline] |
|
pub fn define_properties(&mut self, props: Value, context: &mut Context) -> Result<()> { |
|
let props = &props.to_object(context)?; |
|
let keys = props.own_property_keys(); |
|
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 prop_desc.enumerable() { |
|
let desc_obj = props.get(&next_key, props.clone().into(), context)?; |
|
let desc = desc_obj.to_property_descriptor(context)?; |
|
descriptors.push((next_key, desc)); |
|
} |
|
} |
|
} |
|
|
|
for (p, d) in descriptors { |
|
self.define_own_property(p, d, context)?; |
|
} |
|
|
|
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 |
|
#[inline] |
|
pub fn set_prototype_of(&mut self, val: Value) -> bool { |
|
debug_assert!(val.is_object() || val.is_null()); |
|
let current = self.get_prototype_of(); |
|
if Value::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 Value::same_value(&Value::from(self.clone()), &p) { |
|
return false; |
|
} else { |
|
let prototype = p |
|
.as_object() |
|
.expect("prototype should be null or object") |
|
.get_prototype_of(); |
|
p = prototype; |
|
} |
|
} |
|
self.set_prototype_instance(val); |
|
true |
|
} |
|
|
|
/// Returns either the prototype or null |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof |
|
/// [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 { |
|
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> |
|
where |
|
K: Into<PropertyKey>, |
|
P: Into<PropertyDescriptor>, |
|
{ |
|
self.borrow_mut().insert(key, property) |
|
} |
|
|
|
/// Helper function for property removal. |
|
#[inline] |
|
#[track_caller] |
|
pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> { |
|
self.borrow_mut().remove(key) |
|
} |
|
|
|
/// Inserts a field in the object `properties` without checking if it's writable. |
|
/// |
|
/// If a field was already in the object with the same name that a `Some` is returned |
|
/// with that field, otherwise None is returned. |
|
#[inline] |
|
pub fn insert_property<K, V>( |
|
&mut self, |
|
key: K, |
|
value: V, |
|
attribute: Attribute, |
|
) -> Option<PropertyDescriptor> |
|
where |
|
K: Into<PropertyKey>, |
|
V: Into<Value>, |
|
{ |
|
self.insert(key.into(), DataDescriptor::new(value, attribute)) |
|
} |
|
|
|
/// It determines if Object is a callable function with a `[[Call]]` internal method. |
|
/// |
|
/// More information: |
|
/// - [EcmaScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-iscallable |
|
#[inline] |
|
#[track_caller] |
|
pub fn is_callable(&self) -> bool { |
|
self.borrow().is_callable() |
|
} |
|
|
|
/// It determines if Object is a function object with a `[[Construct]]` internal method. |
|
/// |
|
/// More information: |
|
/// - [EcmaScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-isconstructor |
|
#[inline] |
|
#[track_caller] |
|
pub fn is_constructable(&self) -> bool { |
|
self.borrow().is_constructable() |
|
} |
|
|
|
/// Returns true if the GcObject is the global for a Realm |
|
pub fn is_global(&self) -> bool { |
|
matches!(self.borrow().data, ObjectData::Global) |
|
} |
|
|
|
/// It is used to create List value whose elements are provided by the indexed properties of |
|
/// self. |
|
/// |
|
/// More information: |
|
/// - [EcmaScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#sec-createlistfromarraylike |
|
pub(crate) fn create_list_from_array_like( |
|
&self, |
|
element_types: &[Type], |
|
context: &mut Context, |
|
) -> Result<Vec<Value>> { |
|
let types = if element_types.is_empty() { |
|
&[ |
|
Type::Undefined, |
|
Type::Null, |
|
Type::Boolean, |
|
Type::String, |
|
Type::Symbol, |
|
Type::Number, |
|
Type::BigInt, |
|
Type::Symbol, |
|
] |
|
} else { |
|
element_types |
|
}; |
|
let len = self |
|
.get(&"length".into(), self.clone().into(), context)? |
|
.to_length(context)?; |
|
let mut list = Vec::with_capacity(len); |
|
for index in 0..len { |
|
let next = self.get(&index.into(), self.clone().into(), context)?; |
|
if !types.contains(&next.get_type()) { |
|
return Err(context.construct_type_error("bad type")); |
|
} |
|
list.push(next.clone()); |
|
} |
|
Ok(list) |
|
} |
|
} |
|
|
|
impl Object { |
|
/// Helper function for property insertion. |
|
#[inline] |
|
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) => { |
|
self.string_properties.insert(string.clone(), property) |
|
} |
|
PropertyKey::Symbol(ref symbol) => { |
|
self.symbol_properties.insert(symbol.clone(), property) |
|
} |
|
} |
|
} |
|
|
|
/// Helper function for property removal. |
|
#[inline] |
|
pub(crate) fn remove(&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), |
|
PropertyKey::Symbol(ref symbol) => self.symbol_properties.remove(symbol), |
|
} |
|
} |
|
|
|
/// Inserts a field in the object `properties` without checking if it's writable. |
|
/// |
|
/// If a field was already in the object with the same name that a `Some` is returned |
|
/// with that field, otherwise None is retuned. |
|
#[inline] |
|
pub fn insert_property<K, V>( |
|
&mut self, |
|
key: K, |
|
value: V, |
|
attribute: Attribute, |
|
) -> Option<PropertyDescriptor> |
|
where |
|
K: Into<PropertyKey>, |
|
V: Into<Value>, |
|
{ |
|
self.insert(key.into(), DataDescriptor::new(value, attribute)) |
|
} |
|
}
|
|
|