Skip to main content

boa_ast/function/
parameters.rs

1use crate::{
2    declaration::{Binding, Variable},
3    expression::Expression,
4    operations::bound_names,
5    visitor::{VisitWith, Visitor, VisitorMut},
6};
7use bitflags::bitflags;
8use boa_interner::{Interner, Sym, ToInternedString};
9use core::ops::ControlFlow;
10use rustc_hash::FxHashSet;
11
12/// A list of `FormalParameter`s that describes the parameters of a function, as defined by the [spec].
13///
14/// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[derive(Clone, Debug, Default, PartialEq)]
17pub struct FormalParameterList {
18    parameters: Box<[FormalParameter]>,
19    flags: FormalParameterListFlags,
20    length: u32,
21}
22
23impl FormalParameterList {
24    /// Creates a new empty formal parameter list.
25    #[must_use]
26    pub fn new() -> Self {
27        Self {
28            parameters: Box::new([]),
29            flags: FormalParameterListFlags::default(),
30            length: 0,
31        }
32    }
33
34    /// Creates a `FormalParameterList` from a list of [`FormalParameter`]s.
35    #[must_use]
36    pub fn from_parameters(parameters: Vec<FormalParameter>) -> Self {
37        let mut flags = FormalParameterListFlags::default();
38        let mut length = 0;
39        let mut names = FxHashSet::default();
40
41        for parameter in &parameters {
42            let parameter_names = bound_names(parameter);
43
44            for name in parameter_names {
45                if name == Sym::ARGUMENTS {
46                    flags |= FormalParameterListFlags::HAS_ARGUMENTS;
47                }
48                if names.contains(&name) {
49                    flags |= FormalParameterListFlags::HAS_DUPLICATES;
50                } else {
51                    names.insert(name);
52                }
53            }
54
55            if parameter.is_rest_param() {
56                flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
57            }
58            if parameter.init().is_some() {
59                flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
60            }
61            if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()
62            {
63                flags.remove(FormalParameterListFlags::IS_SIMPLE);
64            }
65            if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
66                || parameter.is_rest_param()
67                || parameter.init().is_some())
68            {
69                length += 1;
70            }
71        }
72
73        Self {
74            parameters: parameters.into(),
75            flags,
76            length,
77        }
78    }
79
80    /// Returns the length of the parameter list.
81    /// Note that this is not equal to the length of the parameters slice.
82    #[must_use]
83    pub const fn length(&self) -> u32 {
84        self.length
85    }
86
87    /// Returns the parameter list flags.
88    #[must_use]
89    pub const fn flags(&self) -> FormalParameterListFlags {
90        self.flags
91    }
92
93    /// Indicates if the parameter list is simple.
94    #[must_use]
95    pub const fn is_simple(&self) -> bool {
96        self.flags.contains(FormalParameterListFlags::IS_SIMPLE)
97    }
98
99    /// Indicates if the parameter list has duplicate parameters.
100    #[must_use]
101    pub const fn has_duplicates(&self) -> bool {
102        self.flags
103            .contains(FormalParameterListFlags::HAS_DUPLICATES)
104    }
105
106    /// Indicates if the parameter list has a rest parameter.
107    #[must_use]
108    pub const fn has_rest_parameter(&self) -> bool {
109        self.flags
110            .contains(FormalParameterListFlags::HAS_REST_PARAMETER)
111    }
112
113    /// Indicates if the parameter list has expressions in it's parameters.
114    #[must_use]
115    pub const fn has_expressions(&self) -> bool {
116        self.flags
117            .contains(FormalParameterListFlags::HAS_EXPRESSIONS)
118    }
119
120    /// Indicates if the parameter list has parameters named 'arguments'.
121    #[must_use]
122    pub const fn has_arguments(&self) -> bool {
123        self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS)
124    }
125}
126
127impl From<Vec<FormalParameter>> for FormalParameterList {
128    fn from(parameters: Vec<FormalParameter>) -> Self {
129        Self::from_parameters(parameters)
130    }
131}
132
133impl From<FormalParameter> for FormalParameterList {
134    fn from(parameter: FormalParameter) -> Self {
135        Self::from_parameters(vec![parameter])
136    }
137}
138
139impl AsRef<[FormalParameter]> for FormalParameterList {
140    fn as_ref(&self) -> &[FormalParameter] {
141        &self.parameters
142    }
143}
144
145impl VisitWith for FormalParameterList {
146    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
147    where
148        V: Visitor<'a>,
149    {
150        for parameter in &*self.parameters {
151            visitor.visit_formal_parameter(parameter)?;
152        }
153
154        ControlFlow::Continue(())
155    }
156
157    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
158    where
159        V: VisitorMut<'a>,
160    {
161        for parameter in &mut *self.parameters {
162            visitor.visit_formal_parameter_mut(parameter)?;
163        }
164
165        // TODO recompute flags
166        ControlFlow::Continue(())
167    }
168}
169
170#[cfg(feature = "arbitrary")]
171impl<'a> arbitrary::Arbitrary<'a> for FormalParameterList {
172    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
173        let params: Vec<FormalParameter> = u.arbitrary()?;
174        Ok(Self::from(params))
175    }
176}
177
178bitflags! {
179    /// Flags for a [`FormalParameterList`].
180    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
181    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
182    pub struct FormalParameterListFlags: u8 {
183        /// Has only identifier parameters with no initialization expressions.
184        const IS_SIMPLE = 0b0000_0001;
185        /// Has any duplicate parameters.
186        const HAS_DUPLICATES = 0b0000_0010;
187        /// Has a rest parameter.
188        const HAS_REST_PARAMETER = 0b0000_0100;
189        /// Has any initialization expression.
190        const HAS_EXPRESSIONS = 0b0000_1000;
191        /// Has an argument with the name `arguments`.
192        const HAS_ARGUMENTS = 0b0001_0000;
193    }
194}
195
196impl Default for FormalParameterListFlags {
197    fn default() -> Self {
198        Self::empty().union(Self::IS_SIMPLE)
199    }
200}
201
202/// "Formal parameter" is a fancy way of saying "function parameter".
203///
204/// In the declaration of a function, the parameters must be identifiers,
205/// not any value like numbers, strings, or objects.
206/// ```text
207/// function foo(formalParameter1, formalParameter2) {
208/// }
209/// ```
210///
211/// More information:
212///  - [ECMAScript reference][spec]
213///  - [MDN documentation][mdn]
214///
215/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter
216/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter
217#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
218#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
219#[derive(Clone, Debug, PartialEq)]
220pub struct FormalParameter {
221    variable: Variable,
222    is_rest_param: bool,
223}
224
225impl FormalParameter {
226    /// Creates a new formal parameter.
227    pub fn new<D>(variable: D, is_rest_param: bool) -> Self
228    where
229        D: Into<Variable>,
230    {
231        Self {
232            variable: variable.into(),
233            is_rest_param,
234        }
235    }
236
237    /// Gets the variable of the formal parameter
238    #[must_use]
239    pub const fn variable(&self) -> &Variable {
240        &self.variable
241    }
242
243    /// Gets the initialization node of the formal parameter, if any.
244    #[must_use]
245    pub const fn init(&self) -> Option<&Expression> {
246        self.variable.init()
247    }
248
249    /// Returns `true` if the parameter is a rest parameter.
250    #[must_use]
251    pub const fn is_rest_param(&self) -> bool {
252        self.is_rest_param
253    }
254
255    /// Returns `true` if the parameter is an identifier.
256    #[must_use]
257    pub const fn is_identifier(&self) -> bool {
258        matches!(&self.variable.binding(), Binding::Identifier(_))
259    }
260}
261
262impl ToInternedString for FormalParameter {
263    fn to_interned_string(&self, interner: &Interner) -> String {
264        let mut buf = if self.is_rest_param {
265            "...".to_owned()
266        } else {
267            String::new()
268        };
269        buf.push_str(&self.variable.to_interned_string(interner));
270        buf
271    }
272}
273
274impl VisitWith for FormalParameter {
275    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
276    where
277        V: Visitor<'a>,
278    {
279        visitor.visit_variable(&self.variable)
280    }
281
282    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
283    where
284        V: VisitorMut<'a>,
285    {
286        visitor.visit_variable_mut(&mut self.variable)
287    }
288}