349 lines
11 KiB
Rust
349 lines
11 KiB
Rust
//! Property access expressions, as defined by the [spec].
|
|
//!
|
|
//! [Property access expressions][access] provide two ways to access properties of an object: *dot notation*
|
|
//! and *bracket notation*.
|
|
//! - *Dot notation* is mostly used when the name of the property is static, and a valid Javascript
|
|
//! identifier e.g. `obj.prop`, `arr.$val`.
|
|
//! - *Bracket notation* is used when the name of the property is either variable, not a valid
|
|
//! identifier or a symbol e.g. `arr[var]`, `arr[5]`, `arr[Symbol.iterator]`.
|
|
//!
|
|
//! A property access expression can be represented by a [`SimplePropertyAccess`] (`x.y`), a
|
|
//! [`PrivatePropertyAccess`] (`x.#y`) or a [`SuperPropertyAccess`] (`super["y"]`), each of them with
|
|
//! slightly different semantics overall.
|
|
//!
|
|
//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors
|
|
//! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
|
|
|
|
use crate::expression::Expression;
|
|
use crate::function::PrivateName;
|
|
use crate::try_break;
|
|
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
|
use boa_interner::{Interner, Sym, ToInternedString};
|
|
use core::ops::ControlFlow;
|
|
|
|
/// A property access field.
|
|
///
|
|
/// See the [module level documentation][self] for more information.
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum PropertyAccessField {
|
|
/// A constant property field, such as `x.prop`.
|
|
Const(Sym),
|
|
/// An expression property field, such as `x["val"]`.
|
|
Expr(Box<Expression>),
|
|
}
|
|
|
|
impl From<Sym> for PropertyAccessField {
|
|
#[inline]
|
|
fn from(id: Sym) -> Self {
|
|
Self::Const(id)
|
|
}
|
|
}
|
|
|
|
impl From<Expression> for PropertyAccessField {
|
|
#[inline]
|
|
fn from(expr: Expression) -> Self {
|
|
Self::Expr(Box::new(expr))
|
|
}
|
|
}
|
|
|
|
impl VisitWith for PropertyAccessField {
|
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: Visitor<'a>,
|
|
{
|
|
match self {
|
|
Self::Const(sym) => visitor.visit_sym(sym),
|
|
Self::Expr(expr) => visitor.visit_expression(expr),
|
|
}
|
|
}
|
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: VisitorMut<'a>,
|
|
{
|
|
match self {
|
|
Self::Const(sym) => visitor.visit_sym_mut(sym),
|
|
Self::Expr(expr) => visitor.visit_expression_mut(&mut *expr),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A property access expression.
|
|
///
|
|
/// See the [module level documentation][self] for more information.
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum PropertyAccess {
|
|
/// A simple property access (`x.prop`).
|
|
Simple(SimplePropertyAccess),
|
|
/// A property access of a private property (`x.#priv`).
|
|
Private(PrivatePropertyAccess),
|
|
/// A property access of a `super` reference. (`super["prop"]`).
|
|
Super(SuperPropertyAccess),
|
|
}
|
|
|
|
impl ToInternedString for PropertyAccess {
|
|
#[inline]
|
|
fn to_interned_string(&self, interner: &Interner) -> String {
|
|
match self {
|
|
Self::Simple(s) => s.to_interned_string(interner),
|
|
Self::Private(p) => p.to_interned_string(interner),
|
|
Self::Super(s) => s.to_interned_string(interner),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PropertyAccess> for Expression {
|
|
#[inline]
|
|
fn from(access: PropertyAccess) -> Self {
|
|
Self::PropertyAccess(access)
|
|
}
|
|
}
|
|
|
|
impl VisitWith for PropertyAccess {
|
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: Visitor<'a>,
|
|
{
|
|
match self {
|
|
Self::Simple(spa) => visitor.visit_simple_property_access(spa),
|
|
Self::Private(ppa) => visitor.visit_private_property_access(ppa),
|
|
Self::Super(supa) => visitor.visit_super_property_access(supa),
|
|
}
|
|
}
|
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: VisitorMut<'a>,
|
|
{
|
|
match self {
|
|
Self::Simple(spa) => visitor.visit_simple_property_access_mut(spa),
|
|
Self::Private(ppa) => visitor.visit_private_property_access_mut(ppa),
|
|
Self::Super(supa) => visitor.visit_super_property_access_mut(supa),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A simple property access, where the target object is an [`Expression`].
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct SimplePropertyAccess {
|
|
target: Box<Expression>,
|
|
field: PropertyAccessField,
|
|
}
|
|
|
|
impl SimplePropertyAccess {
|
|
/// Gets the target object of the property access.
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn target(&self) -> &Expression {
|
|
&self.target
|
|
}
|
|
|
|
/// Gets the accessed field of the target object.
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn field(&self) -> &PropertyAccessField {
|
|
&self.field
|
|
}
|
|
|
|
/// Creates a `PropertyAccess` AST Expression.
|
|
pub fn new<F>(target: Expression, field: F) -> Self
|
|
where
|
|
F: Into<PropertyAccessField>,
|
|
{
|
|
Self {
|
|
target: target.into(),
|
|
field: field.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToInternedString for SimplePropertyAccess {
|
|
#[inline]
|
|
fn to_interned_string(&self, interner: &Interner) -> String {
|
|
let target = self.target.to_interned_string(interner);
|
|
match self.field {
|
|
PropertyAccessField::Const(sym) => format!("{target}.{}", interner.resolve_expect(sym)),
|
|
PropertyAccessField::Expr(ref expr) => {
|
|
format!("{target}[{}]", expr.to_interned_string(interner))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<SimplePropertyAccess> for PropertyAccess {
|
|
#[inline]
|
|
fn from(access: SimplePropertyAccess) -> Self {
|
|
Self::Simple(access)
|
|
}
|
|
}
|
|
|
|
impl VisitWith for SimplePropertyAccess {
|
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: Visitor<'a>,
|
|
{
|
|
try_break!(visitor.visit_expression(&self.target));
|
|
visitor.visit_property_access_field(&self.field)
|
|
}
|
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: VisitorMut<'a>,
|
|
{
|
|
try_break!(visitor.visit_expression_mut(&mut self.target));
|
|
visitor.visit_property_access_field_mut(&mut self.field)
|
|
}
|
|
}
|
|
|
|
/// An access expression to a class object's [private fields][mdn].
|
|
///
|
|
/// Private property accesses differ slightly from plain property accesses, since the accessed
|
|
/// property must be prefixed by `#`, and the bracket notation is not allowed. For example,
|
|
/// `this.#a` is a valid private property access.
|
|
///
|
|
/// This expression corresponds to the [`MemberExpression.PrivateIdentifier`][spec] production.
|
|
///
|
|
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct PrivatePropertyAccess {
|
|
target: Box<Expression>,
|
|
field: PrivateName,
|
|
}
|
|
|
|
impl PrivatePropertyAccess {
|
|
/// Creates a `GetPrivateField` AST Expression.
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn new(value: Expression, field: PrivateName) -> Self {
|
|
Self {
|
|
target: value.into(),
|
|
field,
|
|
}
|
|
}
|
|
|
|
/// Gets the original object from where to get the field from.
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn target(&self) -> &Expression {
|
|
&self.target
|
|
}
|
|
|
|
/// Gets the name of the field to retrieve.
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn field(&self) -> PrivateName {
|
|
self.field
|
|
}
|
|
}
|
|
|
|
impl ToInternedString for PrivatePropertyAccess {
|
|
#[inline]
|
|
fn to_interned_string(&self, interner: &Interner) -> String {
|
|
format!(
|
|
"{}.#{}",
|
|
self.target.to_interned_string(interner),
|
|
interner.resolve_expect(self.field.description())
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<PrivatePropertyAccess> for PropertyAccess {
|
|
#[inline]
|
|
fn from(access: PrivatePropertyAccess) -> Self {
|
|
Self::Private(access)
|
|
}
|
|
}
|
|
|
|
impl VisitWith for PrivatePropertyAccess {
|
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: Visitor<'a>,
|
|
{
|
|
try_break!(visitor.visit_expression(&self.target));
|
|
visitor.visit_private_name(&self.field)
|
|
}
|
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: VisitorMut<'a>,
|
|
{
|
|
try_break!(visitor.visit_expression_mut(&mut self.target));
|
|
visitor.visit_private_name_mut(&mut self.field)
|
|
}
|
|
}
|
|
|
|
/// A property access of an object's parent, as defined by the [spec].
|
|
///
|
|
/// A `SuperPropertyAccess` is much like a regular [`PropertyAccess`], but where its `target` object
|
|
/// is not a regular object, but a reference to the parent object of the current object ([`super`][mdn]).
|
|
///
|
|
/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct SuperPropertyAccess {
|
|
field: PropertyAccessField,
|
|
}
|
|
|
|
impl SuperPropertyAccess {
|
|
/// Creates a new property access field node.
|
|
#[must_use]
|
|
pub const fn new(field: PropertyAccessField) -> Self {
|
|
Self { field }
|
|
}
|
|
|
|
/// Gets the name of the field to retrieve.
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn field(&self) -> &PropertyAccessField {
|
|
&self.field
|
|
}
|
|
}
|
|
|
|
impl ToInternedString for SuperPropertyAccess {
|
|
#[inline]
|
|
fn to_interned_string(&self, interner: &Interner) -> String {
|
|
match &self.field {
|
|
PropertyAccessField::Const(field) => {
|
|
format!("super.{}", interner.resolve_expect(*field))
|
|
}
|
|
PropertyAccessField::Expr(field) => {
|
|
format!("super[{}]", field.to_interned_string(interner))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<SuperPropertyAccess> for PropertyAccess {
|
|
#[inline]
|
|
fn from(access: SuperPropertyAccess) -> Self {
|
|
Self::Super(access)
|
|
}
|
|
}
|
|
|
|
impl VisitWith for SuperPropertyAccess {
|
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: Visitor<'a>,
|
|
{
|
|
visitor.visit_property_access_field(&self.field)
|
|
}
|
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
|
where
|
|
V: VisitorMut<'a>,
|
|
{
|
|
visitor.visit_property_access_field_mut(&mut self.field)
|
|
}
|
|
}
|