boa_ast/function/
generator.rs

1use super::{FormalParameterList, FunctionBody};
2use crate::{
3    Declaration, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,
4    expression::{Expression, Identifier},
5    join_nodes,
6    operations::{ContainsSymbol, contains},
7    scope::{FunctionScopes, Scope},
8    visitor::{VisitWith, Visitor, VisitorMut},
9};
10use boa_interner::{Interner, ToIndentedString};
11use core::{fmt::Write as _, ops::ControlFlow};
12
13/// A generator declaration.
14///
15/// More information:
16///  - [ECMAScript reference][spec]
17///  - [MDN documentation][mdn]
18///
19/// [spec]: https://tc39.es/ecma262/#prod-GeneratorDeclaration
20/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23#[derive(Clone, Debug, PartialEq)]
24pub struct GeneratorDeclaration {
25    name: Identifier,
26    pub(crate) parameters: FormalParameterList,
27    pub(crate) body: FunctionBody,
28    pub(crate) contains_direct_eval: bool,
29
30    #[cfg_attr(feature = "serde", serde(skip))]
31    pub(crate) scopes: FunctionScopes,
32    linear_span: LinearSpanIgnoreEq,
33}
34
35impl GeneratorDeclaration {
36    /// Creates a new generator declaration.
37    #[inline]
38    #[must_use]
39    pub fn new(
40        name: Identifier,
41        parameters: FormalParameterList,
42        body: FunctionBody,
43        linear_span: LinearSpan,
44    ) -> Self {
45        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
46            || contains(&body, ContainsSymbol::DirectEval);
47        Self {
48            name,
49            parameters,
50            body,
51            contains_direct_eval,
52            scopes: FunctionScopes::default(),
53            linear_span: linear_span.into(),
54        }
55    }
56
57    /// Gets the name of the generator declaration.
58    #[inline]
59    #[must_use]
60    pub const fn name(&self) -> Identifier {
61        self.name
62    }
63
64    /// Gets the list of parameters of the generator declaration.
65    #[inline]
66    #[must_use]
67    pub const fn parameters(&self) -> &FormalParameterList {
68        &self.parameters
69    }
70
71    /// Gets the body of the generator declaration.
72    #[inline]
73    #[must_use]
74    pub const fn body(&self) -> &FunctionBody {
75        &self.body
76    }
77
78    /// Returns the scopes of the generator declaration.
79    #[inline]
80    #[must_use]
81    pub const fn scopes(&self) -> &FunctionScopes {
82        &self.scopes
83    }
84
85    /// Gets linear span of the function declaration.
86    #[inline]
87    #[must_use]
88    pub const fn linear_span(&self) -> LinearSpan {
89        self.linear_span.0
90    }
91
92    /// Returns `true` if the generator declaration contains a direct call to `eval`.
93    #[inline]
94    #[must_use]
95    pub const fn contains_direct_eval(&self) -> bool {
96        self.contains_direct_eval
97    }
98}
99
100impl ToIndentedString for GeneratorDeclaration {
101    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
102        format!(
103            "function* {}({}) {}",
104            interner.resolve_expect(self.name.sym()),
105            join_nodes(interner, self.parameters.as_ref()),
106            block_to_string(&self.body.statements, interner, indentation)
107        )
108    }
109}
110
111impl VisitWith for GeneratorDeclaration {
112    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
113    where
114        V: Visitor<'a>,
115    {
116        visitor.visit_identifier(&self.name)?;
117        visitor.visit_formal_parameter_list(&self.parameters)?;
118        visitor.visit_function_body(&self.body)
119    }
120
121    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
122    where
123        V: VisitorMut<'a>,
124    {
125        visitor.visit_identifier_mut(&mut self.name)?;
126        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
127        visitor.visit_function_body_mut(&mut self.body)
128    }
129}
130
131impl From<GeneratorDeclaration> for Declaration {
132    #[inline]
133    fn from(f: GeneratorDeclaration) -> Self {
134        Self::GeneratorDeclaration(f)
135    }
136}
137
138/// A generator expression.
139///
140/// More information:
141///  - [ECMAScript reference][spec]
142///  - [MDN documentation][mdn]
143///
144/// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression
145/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
148#[derive(Clone, Debug, PartialEq)]
149pub struct GeneratorExpression {
150    pub(crate) name: Option<Identifier>,
151    pub(crate) parameters: FormalParameterList,
152    pub(crate) body: FunctionBody,
153    pub(crate) has_binding_identifier: bool,
154    pub(crate) contains_direct_eval: bool,
155
156    #[cfg_attr(feature = "serde", serde(skip))]
157    pub(crate) name_scope: Option<Scope>,
158
159    #[cfg_attr(feature = "serde", serde(skip))]
160    pub(crate) scopes: FunctionScopes,
161    linear_span: LinearSpanIgnoreEq,
162
163    span: Span,
164}
165
166impl GeneratorExpression {
167    /// Creates a new generator expression.
168    #[inline]
169    #[must_use]
170    pub fn new(
171        name: Option<Identifier>,
172        parameters: FormalParameterList,
173        body: FunctionBody,
174        linear_span: LinearSpan,
175        has_binding_identifier: bool,
176        span: Span,
177    ) -> Self {
178        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
179            || contains(&body, ContainsSymbol::DirectEval);
180        Self {
181            name,
182            parameters,
183            body,
184            has_binding_identifier,
185            name_scope: None,
186            contains_direct_eval,
187            scopes: FunctionScopes::default(),
188            linear_span: linear_span.into(),
189            span,
190        }
191    }
192
193    /// Gets the name of the generator expression.
194    #[inline]
195    #[must_use]
196    pub const fn name(&self) -> Option<Identifier> {
197        self.name
198    }
199
200    /// Gets the list of parameters of the generator expression.
201    #[inline]
202    #[must_use]
203    pub const fn parameters(&self) -> &FormalParameterList {
204        &self.parameters
205    }
206
207    /// Gets the body of the generator expression.
208    #[inline]
209    #[must_use]
210    pub const fn body(&self) -> &FunctionBody {
211        &self.body
212    }
213
214    /// Returns whether the generator expression has a binding identifier.
215    #[inline]
216    #[must_use]
217    pub const fn has_binding_identifier(&self) -> bool {
218        self.has_binding_identifier
219    }
220
221    /// Gets the name scope of the generator expression.
222    #[inline]
223    #[must_use]
224    pub const fn name_scope(&self) -> Option<&Scope> {
225        self.name_scope.as_ref()
226    }
227
228    /// Gets the scopes of the generator expression.
229    #[inline]
230    #[must_use]
231    pub const fn scopes(&self) -> &FunctionScopes {
232        &self.scopes
233    }
234
235    /// Gets linear span of the function declaration.
236    #[inline]
237    #[must_use]
238    pub const fn linear_span(&self) -> LinearSpan {
239        self.linear_span.0
240    }
241
242    /// Returns `true` if the generator expression contains a direct call to `eval`.
243    #[inline]
244    #[must_use]
245    pub const fn contains_direct_eval(&self) -> bool {
246        self.contains_direct_eval
247    }
248}
249
250impl Spanned for GeneratorExpression {
251    #[inline]
252    fn span(&self) -> Span {
253        self.span
254    }
255}
256
257impl ToIndentedString for GeneratorExpression {
258    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
259        let mut buf = "function*".to_owned();
260        if self.has_binding_identifier
261            && let Some(name) = self.name
262        {
263            let _ = write!(buf, " {}", interner.resolve_expect(name.sym()));
264        }
265        let _ = write!(
266            buf,
267            "({}) {}",
268            join_nodes(interner, self.parameters.as_ref()),
269            block_to_string(&self.body.statements, interner, indentation)
270        );
271
272        buf
273    }
274}
275
276impl From<GeneratorExpression> for Expression {
277    #[inline]
278    fn from(expr: GeneratorExpression) -> Self {
279        Self::GeneratorExpression(expr)
280    }
281}
282
283impl VisitWith for GeneratorExpression {
284    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
285    where
286        V: Visitor<'a>,
287    {
288        if let Some(ident) = &self.name {
289            visitor.visit_identifier(ident)?;
290        }
291        visitor.visit_formal_parameter_list(&self.parameters)?;
292        visitor.visit_function_body(&self.body)
293    }
294
295    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
296    where
297        V: VisitorMut<'a>,
298    {
299        if let Some(ident) = &mut self.name {
300            visitor.visit_identifier_mut(ident)?;
301        }
302        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
303        visitor.visit_function_body_mut(&mut self.body)
304    }
305}