Skip to main content

boa_ast/expression/literal/
array.rs

1//! Array declaration Expression.
2
3use crate::expression::Expression;
4use crate::expression::operator::assign::{AssignOp, AssignTarget};
5use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};
6use crate::visitor::{VisitWith, Visitor, VisitorMut};
7use crate::{Span, Spanned};
8use boa_interner::{Interner, Sym, ToInternedString};
9use core::ops::ControlFlow;
10
11/// An array is an ordered collection of data (either primitive or object depending upon the
12/// language).
13///
14/// Arrays are used to store multiple values in a single variable.
15/// This is compared to a variable that can store only one value.
16///
17/// Each item in an array has a number attached to it, called a numeric index, that allows you
18/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various
19/// methods.
20///
21/// More information:
22///  - [ECMAScript reference][spec]
23///  - [MDN documentation][mdn]
24///
25/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
26/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[derive(Clone, Debug, PartialEq)]
30pub struct ArrayLiteral {
31    arr: Box<[Option<Expression>]>,
32    has_trailing_comma_spread: bool,
33    span: Span,
34}
35
36impl ArrayLiteral {
37    /// Creates a new array literal.
38    pub fn new<A>(array: A, has_trailing_comma_spread: bool, span: Span) -> Self
39    where
40        A: Into<Box<[Option<Expression>]>>,
41    {
42        Self {
43            arr: array.into(),
44            has_trailing_comma_spread,
45            span,
46        }
47    }
48
49    /// Indicates if a spread operator in the array literal has a trailing comma.
50    /// This is a syntax error in some cases.
51    #[must_use]
52    pub const fn has_trailing_comma_spread(&self) -> bool {
53        self.has_trailing_comma_spread
54    }
55
56    /// Converts this `ArrayLiteral` into an [`ArrayPattern`].
57    #[must_use]
58    pub fn to_pattern(&self, strict: bool) -> Option<ArrayPattern> {
59        if self.has_trailing_comma_spread() {
60            return None;
61        }
62
63        let mut bindings = Vec::new();
64        for (i, expr) in self.arr.iter().enumerate() {
65            let Some(expr) = expr else {
66                bindings.push(ArrayPatternElement::Elision);
67                continue;
68            };
69            match expr {
70                Expression::Identifier(ident) => {
71                    if strict && *ident == Sym::ARGUMENTS {
72                        return None;
73                    }
74
75                    bindings.push(ArrayPatternElement::SingleName {
76                        ident: *ident,
77                        default_init: None,
78                    });
79                }
80                Expression::Spread(spread) => {
81                    match spread.target() {
82                        Expression::Identifier(ident) => {
83                            bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident });
84                        }
85                        Expression::PropertyAccess(access) => {
86                            bindings.push(ArrayPatternElement::PropertyAccessRest {
87                                access: access.clone(),
88                            });
89                        }
90                        Expression::ArrayLiteral(array) => {
91                            let pattern = array.to_pattern(strict)?.into();
92                            bindings.push(ArrayPatternElement::PatternRest { pattern });
93                        }
94                        Expression::ObjectLiteral(object) => {
95                            let pattern = object.to_pattern(strict)?.into();
96                            bindings.push(ArrayPatternElement::PatternRest { pattern });
97                        }
98                        _ => return None,
99                    }
100                    if i + 1 != self.arr.len() {
101                        return None;
102                    }
103                }
104                Expression::Assign(assign) => {
105                    if assign.op() != AssignOp::Assign {
106                        return None;
107                    }
108                    match assign.lhs() {
109                        AssignTarget::Identifier(ident) => {
110                            let mut init = assign.rhs().clone();
111                            init.set_anonymous_function_definition_name(ident);
112                            bindings.push(ArrayPatternElement::SingleName {
113                                ident: *ident,
114                                default_init: Some(init),
115                            });
116                        }
117                        AssignTarget::Access(access) => {
118                            bindings.push(ArrayPatternElement::PropertyAccess {
119                                access: access.clone(),
120                                default_init: Some(assign.rhs().clone()),
121                            });
122                        }
123                        AssignTarget::Pattern(pattern) => match pattern {
124                            Pattern::Object(pattern) => {
125                                bindings.push(ArrayPatternElement::Pattern {
126                                    pattern: Pattern::Object(pattern.clone()),
127                                    default_init: Some(assign.rhs().clone()),
128                                });
129                            }
130                            Pattern::Array(pattern) => {
131                                bindings.push(ArrayPatternElement::Pattern {
132                                    pattern: Pattern::Array(pattern.clone()),
133                                    default_init: Some(assign.rhs().clone()),
134                                });
135                            }
136                        },
137                    }
138                }
139                Expression::ArrayLiteral(array) => {
140                    let pattern = array.to_pattern(strict)?.into();
141                    bindings.push(ArrayPatternElement::Pattern {
142                        pattern,
143                        default_init: None,
144                    });
145                }
146                Expression::ObjectLiteral(object) => {
147                    let pattern = object.to_pattern(strict)?.into();
148                    bindings.push(ArrayPatternElement::Pattern {
149                        pattern,
150                        default_init: None,
151                    });
152                }
153                Expression::PropertyAccess(access) => {
154                    bindings.push(ArrayPatternElement::PropertyAccess {
155                        access: access.clone(),
156                        default_init: None,
157                    });
158                }
159                _ => return None,
160            }
161        }
162        Some(ArrayPattern::new(bindings.into(), self.span))
163    }
164}
165
166impl Spanned for ArrayLiteral {
167    #[inline]
168    fn span(&self) -> Span {
169        self.span
170    }
171}
172
173impl AsRef<[Option<Expression>]> for ArrayLiteral {
174    #[inline]
175    fn as_ref(&self) -> &[Option<Expression>] {
176        &self.arr
177    }
178}
179
180impl AsMut<[Option<Expression>]> for ArrayLiteral {
181    #[inline]
182    fn as_mut(&mut self) -> &mut [Option<Expression>] {
183        &mut self.arr
184    }
185}
186
187impl ToInternedString for ArrayLiteral {
188    #[inline]
189    fn to_interned_string(&self, interner: &Interner) -> String {
190        let mut buf = String::from("[");
191        let mut elements = self.arr.iter().peekable();
192
193        while let Some(element) = elements.next() {
194            if let Some(e) = element {
195                buf.push_str(&e.to_interned_string(interner));
196                if elements.peek().is_some() {
197                    buf.push_str(", ");
198                }
199            } else if elements.peek().is_some() {
200                buf.push_str(", ");
201            } else {
202                buf.push(',');
203            }
204        }
205        buf.push(']');
206        buf
207    }
208}
209
210impl From<ArrayLiteral> for Expression {
211    #[inline]
212    fn from(arr: ArrayLiteral) -> Self {
213        Self::ArrayLiteral(arr)
214    }
215}
216
217impl VisitWith for ArrayLiteral {
218    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
219    where
220        V: Visitor<'a>,
221    {
222        for expr in self.arr.iter().flatten() {
223            visitor.visit_expression(expr)?;
224        }
225        ControlFlow::Continue(())
226    }
227
228    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
229    where
230        V: VisitorMut<'a>,
231    {
232        for expr in self.arr.iter_mut().flatten() {
233            visitor.visit_expression_mut(expr)?;
234        }
235        ControlFlow::Continue(())
236    }
237}