|
|
@ -16,7 +16,6 @@ |
|
|
|
|
|
|
|
|
|
|
|
use crate::{ |
|
|
|
use crate::{ |
|
|
|
gc::{Finalize, Trace}, |
|
|
|
gc::{Finalize, Trace}, |
|
|
|
object::GcObject, |
|
|
|
|
|
|
|
JsString, JsSymbol, Value, |
|
|
|
JsString, JsSymbol, Value, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use std::{convert::TryFrom, fmt}; |
|
|
|
use std::{convert::TryFrom, fmt}; |
|
|
@ -24,7 +23,21 @@ use std::{convert::TryFrom, fmt}; |
|
|
|
mod attribute; |
|
|
|
mod attribute; |
|
|
|
pub use attribute::Attribute; |
|
|
|
pub use attribute::Attribute; |
|
|
|
|
|
|
|
|
|
|
|
/// A data descriptor is a property that has a value, which may or may not be writable.
|
|
|
|
/// This represents a JavaScript Property AKA The Property Descriptor.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Property descriptors present in objects come in three main flavors:
|
|
|
|
|
|
|
|
/// - data descriptors
|
|
|
|
|
|
|
|
/// - accessor descriptors
|
|
|
|
|
|
|
|
/// - generic descriptor
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// A data Property Descriptor is one that includes any fields named either
|
|
|
|
|
|
|
|
/// \[\[Value\]\] or \[\[Writable\]\].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// An accessor Property Descriptor is one that includes any fields named either
|
|
|
|
|
|
|
|
/// \[\[Get\]\] or \[\[Set\]\].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// A generic Property Descriptor is a Property Descriptor value that is neither
|
|
|
|
|
|
|
|
/// a data Property Descriptor nor an accessor Property Descriptor.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// More information:
|
|
|
|
/// More information:
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
@ -32,285 +45,423 @@ pub use attribute::Attribute; |
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
|
|
|
|
|
|
|
|
#[derive(Default, Debug, Clone, Trace, Finalize)] |
|
|
|
|
|
|
|
pub struct PropertyDescriptor { |
|
|
|
|
|
|
|
enumerable: Option<bool>, |
|
|
|
|
|
|
|
configurable: Option<bool>, |
|
|
|
|
|
|
|
kind: DescriptorKind, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Trace, Finalize)] |
|
|
|
#[derive(Debug, Clone, Trace, Finalize)] |
|
|
|
pub struct DataDescriptor { |
|
|
|
pub enum DescriptorKind { |
|
|
|
pub(crate) value: Value, |
|
|
|
Data { |
|
|
|
attributes: Attribute, |
|
|
|
value: Option<Value>, |
|
|
|
has_value: bool, |
|
|
|
writable: Option<bool>, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Accessor { |
|
|
|
|
|
|
|
get: Option<Value>, |
|
|
|
|
|
|
|
set: Option<Value>, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Generic, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl DataDescriptor { |
|
|
|
impl Default for DescriptorKind { |
|
|
|
/// Create a new `DataDescriptor`.
|
|
|
|
fn default() -> Self { |
|
|
|
#[inline] |
|
|
|
Self::Generic |
|
|
|
pub fn new<V>(value: V, attributes: Attribute) -> Self |
|
|
|
|
|
|
|
where |
|
|
|
|
|
|
|
V: Into<Value>, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Self { |
|
|
|
|
|
|
|
value: value.into(), |
|
|
|
|
|
|
|
attributes, |
|
|
|
|
|
|
|
has_value: true, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Create a new `DataDescriptor` without a value.
|
|
|
|
impl PropertyDescriptor { |
|
|
|
|
|
|
|
/// An accessor Property Descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// More information:
|
|
|
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn new_without_value(attributes: Attribute) -> Self { |
|
|
|
pub fn is_accessor_descriptor(&self) -> bool { |
|
|
|
Self { |
|
|
|
matches!(self.kind, DescriptorKind::Accessor { .. }) |
|
|
|
value: Value::undefined(), |
|
|
|
|
|
|
|
attributes, |
|
|
|
|
|
|
|
has_value: false, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the `value` of the data descriptor.
|
|
|
|
/// A data Property Descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// More information:
|
|
|
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn value(&self) -> Value { |
|
|
|
pub fn is_data_descriptor(&self) -> bool { |
|
|
|
self.value.clone() |
|
|
|
matches!(self.kind, DescriptorKind::Data { .. }) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the data descriptor has a value.
|
|
|
|
/// A generic Property Descriptor is one that is neither a data descriptor nor an accessor descriptor.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// More information:
|
|
|
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isgenericdescriptor
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn has_value(&self) -> bool { |
|
|
|
pub fn is_generic_descriptor(&self) -> bool { |
|
|
|
self.has_value |
|
|
|
matches!(self.kind, DescriptorKind::Generic) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the attributes of the descriptor.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn attributes(&self) -> Attribute { |
|
|
|
pub fn is_empty(&self) -> bool { |
|
|
|
self.attributes |
|
|
|
self.is_generic_descriptor() && self.enumerable.is_none() && self.configurable.is_none() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is configurable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn configurable(&self) -> bool { |
|
|
|
pub fn enumerable(&self) -> Option<bool> { |
|
|
|
self.attributes.configurable() |
|
|
|
self.enumerable |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set whether the descriptor is configurable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_configurable(&mut self, configurable: bool) { |
|
|
|
pub fn configurable(&self) -> Option<bool> { |
|
|
|
self.attributes.set_configurable(configurable) |
|
|
|
self.configurable |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is enumerable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn enumerable(&self) -> bool { |
|
|
|
pub fn writable(&self) -> Option<bool> { |
|
|
|
self.attributes.enumerable() |
|
|
|
match self.kind { |
|
|
|
|
|
|
|
DescriptorKind::Data { writable, .. } => writable, |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set whether the descriptor is enumerable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_enumerable(&mut self, enumerable: bool) { |
|
|
|
pub fn value(&self) -> Option<&Value> { |
|
|
|
self.attributes.set_enumerable(enumerable) |
|
|
|
match &self.kind { |
|
|
|
|
|
|
|
DescriptorKind::Data { value, .. } => value.as_ref(), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is writable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn writable(&self) -> bool { |
|
|
|
pub fn get(&self) -> Option<&Value> { |
|
|
|
self.attributes.writable() |
|
|
|
match &self.kind { |
|
|
|
|
|
|
|
DescriptorKind::Accessor { get, .. } => get.as_ref(), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set whether the descriptor is writable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_writable(&mut self, writable: bool) { |
|
|
|
pub fn set(&self) -> Option<&Value> { |
|
|
|
self.attributes.set_writable(writable) |
|
|
|
match &self.kind { |
|
|
|
|
|
|
|
DescriptorKind::Accessor { set, .. } => set.as_ref(), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<DataDescriptor> for PropertyDescriptor { |
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
fn from(value: DataDescriptor) -> Self { |
|
|
|
pub fn expect_enumerable(&self) -> bool { |
|
|
|
Self::Data(value) |
|
|
|
if let Some(enumerable) = self.enumerable { |
|
|
|
|
|
|
|
enumerable |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("[[enumerable]] field not in property descriptor") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// An accessor descriptor is a property described by a getter-setter pair of functions.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// More information:
|
|
|
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type
|
|
|
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Trace, Finalize)] |
|
|
|
|
|
|
|
pub struct AccessorDescriptor { |
|
|
|
|
|
|
|
/// The function serving as getter.
|
|
|
|
|
|
|
|
pub(crate) get: Option<GcObject>, |
|
|
|
|
|
|
|
/// The function serving as setter.
|
|
|
|
|
|
|
|
pub(crate) set: Option<GcObject>, |
|
|
|
|
|
|
|
/// The attributes of the accessor descriptor.
|
|
|
|
|
|
|
|
pub(crate) attributes: Attribute, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl AccessorDescriptor { |
|
|
|
|
|
|
|
/// Create a new `AccessorDescriptor`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// If the `attributes` argument contains a `writable` flag, it will be removed so only `enumerable`
|
|
|
|
|
|
|
|
/// and `configurable` remains.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn new(get: Option<GcObject>, set: Option<GcObject>, mut attributes: Attribute) -> Self { |
|
|
|
pub fn expect_configurable(&self) -> bool { |
|
|
|
// Accessors can not have writable attribute.
|
|
|
|
if let Some(configurable) = self.configurable { |
|
|
|
attributes.remove(Attribute::WRITABLE); |
|
|
|
configurable |
|
|
|
Self { |
|
|
|
} else { |
|
|
|
get, |
|
|
|
panic!("[[configurable]] field not in property descriptor") |
|
|
|
set, |
|
|
|
|
|
|
|
attributes, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the getter if it exists.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn getter(&self) -> Option<&GcObject> { |
|
|
|
pub fn expect_writable(&self) -> bool { |
|
|
|
self.get.as_ref() |
|
|
|
if let Some(writable) = self.writable() { |
|
|
|
|
|
|
|
writable |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("[[writable]] field not in property descriptor") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the setter if it exists.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn setter(&self) -> Option<&GcObject> { |
|
|
|
pub fn expect_value(&self) -> &Value { |
|
|
|
self.set.as_ref() |
|
|
|
if let Some(value) = self.value() { |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("[[value]] field not in property descriptor") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set the getter of the accessor descriptor.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_getter(&mut self, get: Option<GcObject>) { |
|
|
|
pub fn expect_get(&self) -> &Value { |
|
|
|
self.get = get; |
|
|
|
if let Some(get) = self.get() { |
|
|
|
|
|
|
|
get |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("[[get]] field not in property descriptor") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set the setter of the accessor descriptor.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_setter(&mut self, set: Option<GcObject>) { |
|
|
|
pub fn expect_set(&self) -> &Value { |
|
|
|
self.set = set; |
|
|
|
if let Some(set) = self.set() { |
|
|
|
|
|
|
|
set |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
panic!("[[set]] field not in property descriptor") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the attributes of the accessor descriptor.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// It is guaranteed to not contain a `writable` flag
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn attributes(&self) -> Attribute { |
|
|
|
pub fn kind(&self) -> &DescriptorKind { |
|
|
|
self.attributes |
|
|
|
&self.kind |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is configurable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn configurable(&self) -> bool { |
|
|
|
pub fn builder() -> PropertyDescriptorBuilder { |
|
|
|
self.attributes.configurable() |
|
|
|
PropertyDescriptorBuilder::new() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set whether the descriptor is configurable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_configurable(&mut self, configurable: bool) { |
|
|
|
pub fn into_accessor_defaulted(mut self) -> Self { |
|
|
|
self.attributes.set_configurable(configurable) |
|
|
|
self.kind = DescriptorKind::Accessor { |
|
|
|
|
|
|
|
get: self.get().cloned(), |
|
|
|
|
|
|
|
set: self.set().cloned(), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
PropertyDescriptorBuilder { inner: self } |
|
|
|
|
|
|
|
.complete_with_defaults() |
|
|
|
|
|
|
|
.build() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is enumerable.
|
|
|
|
pub fn into_data_defaulted(mut self) -> Self { |
|
|
|
#[inline] |
|
|
|
self.kind = DescriptorKind::Data { |
|
|
|
pub fn enumerable(&self) -> bool { |
|
|
|
value: self.value().cloned(), |
|
|
|
self.attributes.enumerable() |
|
|
|
writable: self.writable(), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
PropertyDescriptorBuilder { inner: self } |
|
|
|
|
|
|
|
.complete_with_defaults() |
|
|
|
|
|
|
|
.build() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Set whether the descriptor is enumerable.
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
pub fn set_enumerable(&mut self, enumerable: bool) { |
|
|
|
pub fn complete_property_descriptor(self) -> Self { |
|
|
|
self.attributes.set_enumerable(enumerable) |
|
|
|
PropertyDescriptorBuilder { inner: self } |
|
|
|
|
|
|
|
.complete_with_defaults() |
|
|
|
|
|
|
|
.build() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<AccessorDescriptor> for PropertyDescriptor { |
|
|
|
|
|
|
|
#[inline] |
|
|
|
#[inline] |
|
|
|
fn from(value: AccessorDescriptor) -> Self { |
|
|
|
pub fn fill_with(&mut self, desc: Self) { |
|
|
|
Self::Accessor(value) |
|
|
|
match (&mut self.kind, &desc.kind) { |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
DescriptorKind::Data { value, writable }, |
|
|
|
|
|
|
|
DescriptorKind::Data { |
|
|
|
|
|
|
|
value: desc_value, |
|
|
|
|
|
|
|
writable: desc_writable, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
) => { |
|
|
|
|
|
|
|
if let Some(desc_value) = desc_value { |
|
|
|
|
|
|
|
*value = Some(desc_value.clone()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if let Some(desc_writable) = desc_writable { |
|
|
|
|
|
|
|
*writable = Some(*desc_writable) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
DescriptorKind::Accessor { get, set }, |
|
|
|
|
|
|
|
DescriptorKind::Accessor { |
|
|
|
|
|
|
|
get: desc_get, |
|
|
|
|
|
|
|
set: desc_set, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
) => { |
|
|
|
|
|
|
|
if let Some(desc_get) = desc_get { |
|
|
|
|
|
|
|
*get = Some(desc_get.clone()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if let Some(desc_set) = desc_set { |
|
|
|
|
|
|
|
*set = Some(desc_set.clone()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(_, DescriptorKind::Generic) => {} |
|
|
|
|
|
|
|
_ => panic!("Tried to fill a descriptor with an incompatible descriptor"), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(enumerable) = desc.enumerable { |
|
|
|
|
|
|
|
self.enumerable = Some(enumerable) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if let Some(configurable) = desc.configurable { |
|
|
|
|
|
|
|
self.configurable = Some(configurable) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// This represents a JavaScript Property AKA The Property Descriptor.
|
|
|
|
#[derive(Default, Debug, Clone)] |
|
|
|
///
|
|
|
|
pub struct PropertyDescriptorBuilder { |
|
|
|
/// Property descriptors present in objects come in two main flavors:
|
|
|
|
inner: PropertyDescriptor, |
|
|
|
/// - data descriptors
|
|
|
|
|
|
|
|
/// - accessor descriptors
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// A data descriptor is a property that has a value, which may or may not be writable.
|
|
|
|
|
|
|
|
/// An accessor descriptor is a property described by a getter-setter pair of functions.
|
|
|
|
|
|
|
|
/// A descriptor must be one of these two flavors; it cannot be both.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// More information:
|
|
|
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type
|
|
|
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Trace, Finalize)] |
|
|
|
|
|
|
|
pub enum PropertyDescriptor { |
|
|
|
|
|
|
|
Accessor(AccessorDescriptor), |
|
|
|
|
|
|
|
Data(DataDescriptor), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl PropertyDescriptor { |
|
|
|
impl PropertyDescriptorBuilder { |
|
|
|
/// An accessor Property Descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`.
|
|
|
|
pub fn new() -> Self { |
|
|
|
///
|
|
|
|
Self::default() |
|
|
|
/// More information:
|
|
|
|
} |
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
|
|
|
///
|
|
|
|
pub fn value<V: Into<Value>>(mut self, value: V) -> Self { |
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor
|
|
|
|
match self.inner.kind { |
|
|
|
#[inline] |
|
|
|
DescriptorKind::Data { |
|
|
|
pub fn is_accessor_descriptor(&self) -> bool { |
|
|
|
value: ref mut v, .. |
|
|
|
matches!(self, Self::Accessor(_)) |
|
|
|
} => *v = Some(value.into()), |
|
|
|
|
|
|
|
// TODO: maybe panic when trying to convert accessor to data?
|
|
|
|
|
|
|
|
_ => { |
|
|
|
|
|
|
|
self.inner.kind = DescriptorKind::Data { |
|
|
|
|
|
|
|
value: Some(value.into()), |
|
|
|
|
|
|
|
writable: None, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn writable(mut self, writable: bool) -> Self { |
|
|
|
|
|
|
|
match self.inner.kind { |
|
|
|
|
|
|
|
DescriptorKind::Data { |
|
|
|
|
|
|
|
writable: ref mut w, |
|
|
|
|
|
|
|
.. |
|
|
|
|
|
|
|
} => *w = Some(writable), |
|
|
|
|
|
|
|
// TODO: maybe panic when trying to convert accessor to data?
|
|
|
|
|
|
|
|
_ => { |
|
|
|
|
|
|
|
self.inner.kind = DescriptorKind::Data { |
|
|
|
|
|
|
|
value: None, |
|
|
|
|
|
|
|
writable: Some(writable), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return `Some()` if it is a accessor descriptor, `None` otherwise.
|
|
|
|
pub fn get<V: Into<Value>>(mut self, get: V) -> Self { |
|
|
|
#[inline] |
|
|
|
match self.inner.kind { |
|
|
|
pub fn as_accessor_descriptor(&self) -> Option<&AccessorDescriptor> { |
|
|
|
DescriptorKind::Accessor { get: ref mut g, .. } => *g = Some(get.into()), |
|
|
|
match self { |
|
|
|
// TODO: maybe panic when trying to convert data to accessor?
|
|
|
|
Self::Accessor(ref accessor) => Some(accessor), |
|
|
|
_ => { |
|
|
|
_ => None, |
|
|
|
self.inner.kind = DescriptorKind::Accessor { |
|
|
|
|
|
|
|
get: Some(get.into()), |
|
|
|
|
|
|
|
set: None, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// A data Property Descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`.
|
|
|
|
pub fn set<V: Into<Value>>(mut self, set: V) -> Self { |
|
|
|
///
|
|
|
|
match self.inner.kind { |
|
|
|
/// More information:
|
|
|
|
DescriptorKind::Accessor { set: ref mut s, .. } => *s = Some(set.into()), |
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
// TODO: maybe panic when trying to convert data to accessor?
|
|
|
|
///
|
|
|
|
_ => { |
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor
|
|
|
|
self.inner.kind = DescriptorKind::Accessor { |
|
|
|
#[inline] |
|
|
|
set: Some(set.into()), |
|
|
|
pub fn is_data_descriptor(&self) -> bool { |
|
|
|
get: None, |
|
|
|
matches!(self, Self::Data(_)) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return `Some()` if it is a data descriptor, `None` otherwise.
|
|
|
|
pub fn maybe_enumerable(mut self, enumerable: Option<bool>) -> Self { |
|
|
|
#[inline] |
|
|
|
if let Some(enumerable) = enumerable { |
|
|
|
pub fn as_data_descriptor(&self) -> Option<&DataDescriptor> { |
|
|
|
self = self.enumerable(enumerable); |
|
|
|
match self { |
|
|
|
|
|
|
|
Self::Data(ref data) => Some(data), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is enumerable.
|
|
|
|
pub fn maybe_configurable(mut self, configurable: Option<bool>) -> Self { |
|
|
|
#[inline] |
|
|
|
if let Some(configurable) = configurable { |
|
|
|
pub fn enumerable(&self) -> bool { |
|
|
|
self = self.configurable(configurable); |
|
|
|
match self { |
|
|
|
|
|
|
|
Self::Accessor(ref accessor) => accessor.enumerable(), |
|
|
|
|
|
|
|
Self::Data(ref data) => data.enumerable(), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Check whether the descriptor is configurable.
|
|
|
|
pub fn maybe_value<V: Into<Value>>(mut self, value: Option<V>) -> Self { |
|
|
|
#[inline] |
|
|
|
if let Some(value) = value { |
|
|
|
pub fn configurable(&self) -> bool { |
|
|
|
self = self.value(value); |
|
|
|
match self { |
|
|
|
|
|
|
|
Self::Accessor(ref accessor) => accessor.configurable(), |
|
|
|
|
|
|
|
Self::Data(ref data) => data.configurable(), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Return the attributes of the descriptor.
|
|
|
|
pub fn maybe_writable(mut self, writable: Option<bool>) -> Self { |
|
|
|
#[inline] |
|
|
|
if let Some(writable) = writable { |
|
|
|
pub fn attributes(&self) -> Attribute { |
|
|
|
self = self.writable(writable); |
|
|
|
match self { |
|
|
|
|
|
|
|
Self::Accessor(ref accessor) => accessor.attributes(), |
|
|
|
|
|
|
|
Self::Data(ref data) => data.attributes(), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn maybe_get<V: Into<Value>>(mut self, get: Option<V>) -> Self { |
|
|
|
|
|
|
|
if let Some(get) = get { |
|
|
|
|
|
|
|
self = self.get(get); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn maybe_set<V: Into<Value>>(mut self, set: Option<V>) -> Self { |
|
|
|
|
|
|
|
if let Some(set) = set { |
|
|
|
|
|
|
|
self = self.set(set); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn enumerable(mut self, enumerable: bool) -> Self { |
|
|
|
|
|
|
|
self.inner.enumerable = Some(enumerable); |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pub fn configurable(mut self, configurable: bool) -> Self { |
|
|
|
|
|
|
|
self.inner.configurable = Some(configurable); |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn complete_with_defaults(mut self) -> Self { |
|
|
|
|
|
|
|
match self.inner.kind { |
|
|
|
|
|
|
|
DescriptorKind::Generic => { |
|
|
|
|
|
|
|
self.inner.kind = DescriptorKind::Data { |
|
|
|
|
|
|
|
value: Some(Value::undefined()), |
|
|
|
|
|
|
|
writable: Some(false), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DescriptorKind::Data { |
|
|
|
|
|
|
|
ref mut value, |
|
|
|
|
|
|
|
ref mut writable, |
|
|
|
|
|
|
|
} => { |
|
|
|
|
|
|
|
if value.is_none() { |
|
|
|
|
|
|
|
*value = Some(Value::undefined()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if writable.is_none() { |
|
|
|
|
|
|
|
*writable = Some(false) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DescriptorKind::Accessor { |
|
|
|
|
|
|
|
ref mut set, |
|
|
|
|
|
|
|
ref mut get, |
|
|
|
|
|
|
|
} => { |
|
|
|
|
|
|
|
if set.is_none() { |
|
|
|
|
|
|
|
*set = Some(Value::undefined()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if get.is_none() { |
|
|
|
|
|
|
|
*get = Some(Value::undefined()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if self.inner.configurable.is_none() { |
|
|
|
|
|
|
|
self.inner.configurable = Some(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if self.inner.enumerable.is_none() { |
|
|
|
|
|
|
|
self.inner.enumerable = Some(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn inner(&self) -> &PropertyDescriptor { |
|
|
|
|
|
|
|
&self.inner |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn build(self) -> PropertyDescriptor { |
|
|
|
|
|
|
|
self.inner |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<PropertyDescriptorBuilder> for PropertyDescriptor { |
|
|
|
|
|
|
|
fn from(builder: PropertyDescriptorBuilder) -> Self { |
|
|
|
|
|
|
|
builder.build() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|