Skip to main content

boa_ast/expression/
access.rs

1//! Property access expressions, as defined by the [spec].
2//!
3//! [Property access expressions][access] provide two ways to access properties of an object: *dot notation*
4//! and *bracket notation*.
5//! - *Dot notation* is mostly used when the name of the property is static, and a valid Javascript
6//!   identifier e.g. `obj.prop`, `arr.$val`.
7//! - *Bracket notation* is used when the name of the property is either variable, not a valid
8//!   identifier or a symbol e.g. `arr[var]`, `arr[5]`, `arr[Symbol.iterator]`.
9//!
10//! A property access expression can be represented by a [`SimplePropertyAccess`] (`x.y`), a
11//! [`PrivatePropertyAccess`] (`x.#y`) or a [`SuperPropertyAccess`] (`super["y"]`), each of them with
12//! slightly different semantics overall.
13//!
14//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors
15//! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
16
17use crate::expression::Expression;
18use crate::function::PrivateName;
19use crate::visitor::{VisitWith, Visitor, VisitorMut};
20use crate::{Span, Spanned};
21use boa_interner::{Interner, ToInternedString};
22use core::ops::ControlFlow;
23
24use super::Identifier;
25
26/// A property access field.
27///
28/// See the [module level documentation][self] for more information.
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
31#[derive(Clone, Debug, PartialEq)]
32pub enum PropertyAccessField {
33    /// A constant property field, such as `x.prop`.
34    Const(Identifier),
35    /// An expression property field, such as `x["val"]`.
36    Expr(Box<Expression>),
37}
38
39impl Spanned for PropertyAccessField {
40    #[inline]
41    fn span(&self) -> Span {
42        match self {
43            Self::Const(identifier) => identifier.span(),
44            Self::Expr(expression) => expression.span(),
45        }
46    }
47}
48
49impl From<Identifier> for PropertyAccessField {
50    #[inline]
51    fn from(id: Identifier) -> Self {
52        Self::Const(id)
53    }
54}
55
56impl From<Expression> for PropertyAccessField {
57    #[inline]
58    fn from(expr: Expression) -> Self {
59        Self::Expr(Box::new(expr))
60    }
61}
62
63impl VisitWith for PropertyAccessField {
64    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
65    where
66        V: Visitor<'a>,
67    {
68        match self {
69            Self::Const(sym) => visitor.visit_sym(sym.sym_ref()),
70            Self::Expr(expr) => visitor.visit_expression(expr),
71        }
72    }
73
74    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
75    where
76        V: VisitorMut<'a>,
77    {
78        match self {
79            Self::Const(sym) => visitor.visit_sym_mut(sym.sym_mut()),
80            Self::Expr(expr) => visitor.visit_expression_mut(&mut *expr),
81        }
82    }
83}
84
85/// A property access expression.
86///
87/// See the [module level documentation][self] for more information.
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
90#[derive(Clone, Debug, PartialEq)]
91pub enum PropertyAccess {
92    /// A simple property access (`x.prop`).
93    Simple(SimplePropertyAccess),
94    /// A property access of a private property (`x.#priv`).
95    Private(PrivatePropertyAccess),
96    /// A property access of a `super` reference. (`super["prop"]`).
97    Super(SuperPropertyAccess),
98}
99
100impl Spanned for PropertyAccess {
101    #[inline]
102    fn span(&self) -> Span {
103        match self {
104            Self::Simple(access) => access.span(),
105            Self::Private(access) => access.span(),
106            Self::Super(access) => access.span(),
107        }
108    }
109}
110
111impl ToInternedString for PropertyAccess {
112    #[inline]
113    fn to_interned_string(&self, interner: &Interner) -> String {
114        match self {
115            Self::Simple(s) => s.to_interned_string(interner),
116            Self::Private(p) => p.to_interned_string(interner),
117            Self::Super(s) => s.to_interned_string(interner),
118        }
119    }
120}
121
122impl From<PropertyAccess> for Expression {
123    #[inline]
124    fn from(access: PropertyAccess) -> Self {
125        Self::PropertyAccess(access)
126    }
127}
128
129impl VisitWith for PropertyAccess {
130    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
131    where
132        V: Visitor<'a>,
133    {
134        match self {
135            Self::Simple(spa) => visitor.visit_simple_property_access(spa),
136            Self::Private(ppa) => visitor.visit_private_property_access(ppa),
137            Self::Super(supa) => visitor.visit_super_property_access(supa),
138        }
139    }
140
141    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
142    where
143        V: VisitorMut<'a>,
144    {
145        match self {
146            Self::Simple(spa) => visitor.visit_simple_property_access_mut(spa),
147            Self::Private(ppa) => visitor.visit_private_property_access_mut(ppa),
148            Self::Super(supa) => visitor.visit_super_property_access_mut(supa),
149        }
150    }
151}
152
153/// A simple property access, where the target object is an [`Expression`].
154#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
155#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
156#[derive(Clone, Debug, PartialEq)]
157pub struct SimplePropertyAccess {
158    target: Box<Expression>,
159    field: PropertyAccessField,
160}
161
162impl SimplePropertyAccess {
163    /// Gets the target object of the property access.
164    #[inline]
165    #[must_use]
166    pub const fn target(&self) -> &Expression {
167        &self.target
168    }
169
170    /// Gets the accessed field of the target object.
171    #[inline]
172    #[must_use]
173    pub const fn field(&self) -> &PropertyAccessField {
174        &self.field
175    }
176
177    /// Creates a `PropertyAccess` AST Expression.
178    pub fn new<F>(target: Expression, field: F) -> Self
179    where
180        F: Into<PropertyAccessField>,
181    {
182        Self {
183            target: target.into(),
184            field: field.into(),
185        }
186    }
187}
188
189impl Spanned for SimplePropertyAccess {
190    #[inline]
191    fn span(&self) -> Span {
192        Span::new(self.target.span().start(), self.field.span().end())
193    }
194}
195
196impl ToInternedString for SimplePropertyAccess {
197    #[inline]
198    fn to_interned_string(&self, interner: &Interner) -> String {
199        let target = self.target.to_interned_string(interner);
200        match self.field {
201            PropertyAccessField::Const(ident) => {
202                format!("{target}.{}", interner.resolve_expect(ident.sym()))
203            }
204            PropertyAccessField::Expr(ref expr) => {
205                format!("{target}[{}]", expr.to_interned_string(interner))
206            }
207        }
208    }
209}
210
211impl From<SimplePropertyAccess> for PropertyAccess {
212    #[inline]
213    fn from(access: SimplePropertyAccess) -> Self {
214        Self::Simple(access)
215    }
216}
217
218impl VisitWith for SimplePropertyAccess {
219    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
220    where
221        V: Visitor<'a>,
222    {
223        visitor.visit_expression(&self.target)?;
224        visitor.visit_property_access_field(&self.field)
225    }
226
227    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
228    where
229        V: VisitorMut<'a>,
230    {
231        visitor.visit_expression_mut(&mut self.target)?;
232        visitor.visit_property_access_field_mut(&mut self.field)
233    }
234}
235
236/// An access expression to a class object's [private fields][mdn].
237///
238/// Private property accesses differ slightly from plain property accesses, since the accessed
239/// property must be prefixed by `#`, and the bracket notation is not allowed. For example,
240/// `this.#a` is a valid private property access.
241///
242/// This expression corresponds to the [`MemberExpression.PrivateIdentifier`][spec] production.
243///
244/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
245/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
246#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
247#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
248#[derive(Clone, Debug, PartialEq)]
249pub struct PrivatePropertyAccess {
250    target: Box<Expression>,
251    field: PrivateName,
252    span: Span,
253}
254
255impl PrivatePropertyAccess {
256    /// Creates a `GetPrivateField` AST Expression.
257    #[inline]
258    #[must_use]
259    pub fn new(value: Expression, field: PrivateName, span: Span) -> Self {
260        Self {
261            target: value.into(),
262            field,
263            span,
264        }
265    }
266
267    /// Gets the original object from where to get the field from.
268    #[inline]
269    #[must_use]
270    pub const fn target(&self) -> &Expression {
271        &self.target
272    }
273
274    /// Gets the name of the field to retrieve.
275    #[inline]
276    #[must_use]
277    pub const fn field(&self) -> PrivateName {
278        self.field
279    }
280}
281
282impl Spanned for PrivatePropertyAccess {
283    #[inline]
284    fn span(&self) -> Span {
285        self.span
286    }
287}
288
289impl ToInternedString for PrivatePropertyAccess {
290    #[inline]
291    fn to_interned_string(&self, interner: &Interner) -> String {
292        format!(
293            "{}.#{}",
294            self.target.to_interned_string(interner),
295            interner.resolve_expect(self.field.description())
296        )
297    }
298}
299
300impl From<PrivatePropertyAccess> for PropertyAccess {
301    #[inline]
302    fn from(access: PrivatePropertyAccess) -> Self {
303        Self::Private(access)
304    }
305}
306
307impl VisitWith for PrivatePropertyAccess {
308    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
309    where
310        V: Visitor<'a>,
311    {
312        visitor.visit_expression(&self.target)?;
313        visitor.visit_private_name(&self.field)
314    }
315
316    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
317    where
318        V: VisitorMut<'a>,
319    {
320        visitor.visit_expression_mut(&mut self.target)?;
321        visitor.visit_private_name_mut(&mut self.field)
322    }
323}
324
325/// A property access of an object's parent, as defined by the [spec].
326///
327/// A `SuperPropertyAccess` is much like a regular [`PropertyAccess`], but where its `target` object
328/// is not a regular object, but a reference to the parent object of the current object ([`super`][mdn]).
329///
330/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty
331/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
332#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
333#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
334#[derive(Clone, Debug, PartialEq)]
335pub struct SuperPropertyAccess {
336    field: PropertyAccessField,
337    span: Span,
338}
339
340impl SuperPropertyAccess {
341    /// Creates a new property access field node.
342    #[must_use]
343    pub const fn new(field: PropertyAccessField, span: Span) -> Self {
344        Self { field, span }
345    }
346
347    /// Gets the name of the field to retrieve.
348    #[inline]
349    #[must_use]
350    pub const fn field(&self) -> &PropertyAccessField {
351        &self.field
352    }
353}
354
355impl Spanned for SuperPropertyAccess {
356    #[inline]
357    fn span(&self) -> Span {
358        self.span
359    }
360}
361
362impl ToInternedString for SuperPropertyAccess {
363    #[inline]
364    fn to_interned_string(&self, interner: &Interner) -> String {
365        match &self.field {
366            PropertyAccessField::Const(field) => {
367                format!("super.{}", interner.resolve_expect(field.sym()))
368            }
369            PropertyAccessField::Expr(field) => {
370                format!("super[{}]", field.to_interned_string(interner))
371            }
372        }
373    }
374}
375
376impl From<SuperPropertyAccess> for PropertyAccess {
377    #[inline]
378    fn from(access: SuperPropertyAccess) -> Self {
379        Self::Super(access)
380    }
381}
382
383impl VisitWith for SuperPropertyAccess {
384    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
385    where
386        V: Visitor<'a>,
387    {
388        visitor.visit_property_access_field(&self.field)
389    }
390
391    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
392    where
393        V: VisitorMut<'a>,
394    {
395        visitor.visit_property_access_field_mut(&mut self.field)
396    }
397}