boa_ast/expression/literal/
array.rs

1//! Array declaration Expression.
2
3use crate::expression::operator::assign::{AssignOp, AssignTarget};
4use crate::expression::Expression;
5use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};
6use crate::try_break;
7use crate::visitor::{VisitWith, Visitor, VisitorMut};
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}
34
35impl ArrayLiteral {
36    /// Creates a new array literal.
37    pub fn new<A>(array: A, has_trailing_comma_spread: bool) -> Self
38    where
39        A: Into<Box<[Option<Expression>]>>,
40    {
41        Self {
42            arr: array.into(),
43            has_trailing_comma_spread,
44        }
45    }
46
47    /// Indicates if a spread operator in the array literal has a trailing comma.
48    /// This is a syntax error in some cases.
49    #[must_use]
50    pub const fn has_trailing_comma_spread(&self) -> bool {
51        self.has_trailing_comma_spread
52    }
53
54    /// Converts this `ArrayLiteral` into an [`ArrayPattern`].
55    #[must_use]
56    pub fn to_pattern(&self, strict: bool) -> Option<ArrayPattern> {
57        if self.has_trailing_comma_spread() {
58            return None;
59        }
60
61        let mut bindings = Vec::new();
62        for (i, expr) in self.arr.iter().enumerate() {
63            let Some(expr) = expr else {
64                bindings.push(ArrayPatternElement::Elision);
65                continue;
66            };
67            match expr {
68                Expression::Identifier(ident) => {
69                    if strict && *ident == Sym::ARGUMENTS {
70                        return None;
71                    }
72
73                    bindings.push(ArrayPatternElement::SingleName {
74                        ident: *ident,
75                        default_init: None,
76                    });
77                }
78                Expression::Spread(spread) => {
79                    match spread.target() {
80                        Expression::Identifier(ident) => {
81                            bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident });
82                        }
83                        Expression::PropertyAccess(access) => {
84                            bindings.push(ArrayPatternElement::PropertyAccessRest {
85                                access: access.clone(),
86                            });
87                        }
88                        Expression::ArrayLiteral(array) => {
89                            let pattern = array.to_pattern(strict)?.into();
90                            bindings.push(ArrayPatternElement::PatternRest { pattern });
91                        }
92                        Expression::ObjectLiteral(object) => {
93                            let pattern = object.to_pattern(strict)?.into();
94                            bindings.push(ArrayPatternElement::PatternRest { pattern });
95                        }
96                        _ => return None,
97                    }
98                    if i + 1 != self.arr.len() {
99                        return None;
100                    }
101                }
102                Expression::Assign(assign) => {
103                    if assign.op() != AssignOp::Assign {
104                        return None;
105                    }
106                    match assign.lhs() {
107                        AssignTarget::Identifier(ident) => {
108                            let mut init = assign.rhs().clone();
109                            init.set_anonymous_function_definition_name(ident);
110                            bindings.push(ArrayPatternElement::SingleName {
111                                ident: *ident,
112                                default_init: Some(init),
113                            });
114                        }
115                        AssignTarget::Access(access) => {
116                            bindings.push(ArrayPatternElement::PropertyAccess {
117                                access: access.clone(),
118                                default_init: Some(assign.rhs().clone()),
119                            });
120                        }
121                        AssignTarget::Pattern(pattern) => match pattern {
122                            Pattern::Object(pattern) => {
123                                bindings.push(ArrayPatternElement::Pattern {
124                                    pattern: Pattern::Object(pattern.clone()),
125                                    default_init: Some(assign.rhs().clone()),
126                                });
127                            }
128                            Pattern::Array(pattern) => {
129                                bindings.push(ArrayPatternElement::Pattern {
130                                    pattern: Pattern::Array(pattern.clone()),
131                                    default_init: Some(assign.rhs().clone()),
132                                });
133                            }
134                        },
135                    }
136                }
137                Expression::ArrayLiteral(array) => {
138                    let pattern = array.to_pattern(strict)?.into();
139                    bindings.push(ArrayPatternElement::Pattern {
140                        pattern,
141                        default_init: None,
142                    });
143                }
144                Expression::ObjectLiteral(object) => {
145                    let pattern = object.to_pattern(strict)?.into();
146                    bindings.push(ArrayPatternElement::Pattern {
147                        pattern,
148                        default_init: None,
149                    });
150                }
151                Expression::PropertyAccess(access) => {
152                    bindings.push(ArrayPatternElement::PropertyAccess {
153                        access: access.clone(),
154                        default_init: None,
155                    });
156                }
157                _ => return None,
158            }
159        }
160        Some(ArrayPattern::new(bindings.into()))
161    }
162}
163
164impl AsRef<[Option<Expression>]> for ArrayLiteral {
165    #[inline]
166    fn as_ref(&self) -> &[Option<Expression>] {
167        &self.arr
168    }
169}
170
171impl AsMut<[Option<Expression>]> for ArrayLiteral {
172    #[inline]
173    fn as_mut(&mut self) -> &mut [Option<Expression>] {
174        &mut self.arr
175    }
176}
177
178impl<T> From<T> for ArrayLiteral
179where
180    T: Into<Box<[Option<Expression>]>>,
181{
182    fn from(decl: T) -> Self {
183        Self {
184            arr: decl.into(),
185            has_trailing_comma_spread: false,
186        }
187    }
188}
189
190impl ToInternedString for ArrayLiteral {
191    #[inline]
192    fn to_interned_string(&self, interner: &Interner) -> String {
193        let mut buf = String::from("[");
194        let mut elements = self.arr.iter().peekable();
195
196        while let Some(element) = elements.next() {
197            if let Some(e) = element {
198                buf.push_str(&e.to_interned_string(interner));
199                if elements.peek().is_some() {
200                    buf.push_str(", ");
201                }
202            } else if elements.peek().is_some() {
203                buf.push_str(", ");
204            } else {
205                buf.push(',');
206            }
207        }
208        buf.push(']');
209        buf
210    }
211}
212
213impl From<ArrayLiteral> for Expression {
214    #[inline]
215    fn from(arr: ArrayLiteral) -> Self {
216        Self::ArrayLiteral(arr)
217    }
218}
219
220impl VisitWith for ArrayLiteral {
221    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
222    where
223        V: Visitor<'a>,
224    {
225        for expr in self.arr.iter().flatten() {
226            try_break!(visitor.visit_expression(expr));
227        }
228        ControlFlow::Continue(())
229    }
230
231    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
232    where
233        V: VisitorMut<'a>,
234    {
235        for expr in self.arr.iter_mut().flatten() {
236            try_break!(visitor.visit_expression_mut(expr));
237        }
238        ControlFlow::Continue(())
239    }
240}