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 scope_analyzer::{
9 analyze_binding_escapes, collect_bindings, optimize_scope_indices_function_constructor,
10 },
11 visitor::{VisitWith, Visitor, VisitorMut},
12};
13use boa_interner::{Interner, ToIndentedString};
14use core::{fmt::Write as _, ops::ControlFlow};
15
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[derive(Clone, Debug, PartialEq)]
27pub struct FunctionDeclaration {
28 name: Identifier,
29 pub(crate) parameters: FormalParameterList,
30 pub(crate) body: FunctionBody,
31 pub(crate) contains_direct_eval: bool,
32
33 #[cfg_attr(feature = "serde", serde(skip))]
34 pub(crate) scopes: FunctionScopes,
35 linear_span: LinearSpanIgnoreEq,
36}
37
38impl FunctionDeclaration {
39 #[inline]
41 #[must_use]
42 pub fn new(
43 name: Identifier,
44 parameters: FormalParameterList,
45 body: FunctionBody,
46 linear_span: LinearSpan,
47 ) -> Self {
48 let contains_direct_eval = contains(¶meters, ContainsSymbol::DirectEval)
49 || contains(&body, ContainsSymbol::DirectEval);
50 Self {
51 name,
52 parameters,
53 body,
54 contains_direct_eval,
55 scopes: FunctionScopes::default(),
56 linear_span: linear_span.into(),
57 }
58 }
59
60 #[inline]
62 #[must_use]
63 pub const fn name(&self) -> Identifier {
64 self.name
65 }
66
67 #[inline]
69 #[must_use]
70 pub const fn parameters(&self) -> &FormalParameterList {
71 &self.parameters
72 }
73
74 #[inline]
76 #[must_use]
77 pub const fn body(&self) -> &FunctionBody {
78 &self.body
79 }
80
81 #[inline]
83 #[must_use]
84 pub const fn scopes(&self) -> &FunctionScopes {
85 &self.scopes
86 }
87
88 #[inline]
90 #[must_use]
91 pub const fn linear_span(&self) -> LinearSpan {
92 self.linear_span.0
93 }
94
95 #[inline]
97 #[must_use]
98 pub const fn contains_direct_eval(&self) -> bool {
99 self.contains_direct_eval
100 }
101}
102
103impl ToIndentedString for FunctionDeclaration {
104 fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
105 format!(
106 "function {}({}) {}",
107 interner.resolve_expect(self.name.sym()),
108 join_nodes(interner, self.parameters.as_ref()),
109 block_to_string(&self.body.statements, interner, indentation)
110 )
111 }
112}
113
114impl VisitWith for FunctionDeclaration {
115 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
116 where
117 V: Visitor<'a>,
118 {
119 visitor.visit_identifier(&self.name)?;
120 visitor.visit_formal_parameter_list(&self.parameters)?;
121 visitor.visit_function_body(&self.body)
122 }
123
124 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
125 where
126 V: VisitorMut<'a>,
127 {
128 visitor.visit_identifier_mut(&mut self.name)?;
129 visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
130 visitor.visit_function_body_mut(&mut self.body)
131 }
132}
133
134impl From<FunctionDeclaration> for Declaration {
135 #[inline]
136 fn from(f: FunctionDeclaration) -> Self {
137 Self::FunctionDeclaration(f)
138 }
139}
140
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
150#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
151#[derive(Clone, Debug)]
152pub struct FunctionExpression {
153 pub(crate) name: Option<Identifier>,
154 pub(crate) parameters: FormalParameterList,
155 pub(crate) body: FunctionBody,
156 pub(crate) has_binding_identifier: bool,
157 pub(crate) contains_direct_eval: bool,
158
159 #[cfg_attr(feature = "serde", serde(skip))]
160 pub(crate) name_scope: Option<Scope>,
161
162 #[cfg_attr(feature = "serde", serde(skip))]
163 pub(crate) scopes: FunctionScopes,
164
165 span: Span,
166
167 linear_span: Option<LinearSpan>,
168}
169
170impl PartialEq for FunctionExpression {
171 fn eq(&self, other: &Self) -> bool {
172 self.name == other.name
174 && self.parameters == other.parameters
175 && self.body == other.body
176 && self.has_binding_identifier == other.has_binding_identifier
177 && self.contains_direct_eval == other.contains_direct_eval
178 && self.name_scope == other.name_scope
179 && self.scopes == other.scopes
180 && self.span == other.span
181 }
182}
183
184impl Spanned for FunctionExpression {
185 #[inline]
186 fn span(&self) -> Span {
187 self.span
188 }
189}
190
191impl FunctionExpression {
192 #[inline]
194 #[must_use]
195 pub fn new(
196 name: Option<Identifier>,
197 parameters: FormalParameterList,
198 body: FunctionBody,
199 linear_span: Option<LinearSpan>,
200 has_binding_identifier: bool,
201 span: Span,
202 ) -> Self {
203 let contains_direct_eval = contains(¶meters, ContainsSymbol::DirectEval)
204 || contains(&body, ContainsSymbol::DirectEval);
205 Self {
206 name,
207 parameters,
208 body,
209 has_binding_identifier,
210 name_scope: None,
211 contains_direct_eval,
212 scopes: FunctionScopes::default(),
213 #[allow(clippy::redundant_closure_for_method_calls)]
214 linear_span,
215 span,
216 }
217 }
218
219 #[inline]
221 #[must_use]
222 pub const fn name(&self) -> Option<Identifier> {
223 self.name
224 }
225
226 #[inline]
228 #[must_use]
229 pub const fn parameters(&self) -> &FormalParameterList {
230 &self.parameters
231 }
232
233 #[inline]
235 #[must_use]
236 pub const fn body(&self) -> &FunctionBody {
237 &self.body
238 }
239
240 #[inline]
242 #[must_use]
243 pub const fn has_binding_identifier(&self) -> bool {
244 self.has_binding_identifier
245 }
246
247 #[inline]
249 #[must_use]
250 pub const fn name_scope(&self) -> Option<&Scope> {
251 self.name_scope.as_ref()
252 }
253
254 #[inline]
256 #[must_use]
257 pub const fn scopes(&self) -> &FunctionScopes {
258 &self.scopes
259 }
260
261 #[inline]
263 #[must_use]
264 pub const fn linear_span(&self) -> Option<LinearSpan> {
265 self.linear_span
266 }
267
268 #[inline]
270 #[must_use]
271 pub const fn contains_direct_eval(&self) -> bool {
272 self.contains_direct_eval
273 }
274
275 pub fn analyze_scope(
280 &mut self,
281 strict: bool,
282 scope: &Scope,
283 interner: &Interner,
284 ) -> Result<(), &'static str> {
285 collect_bindings(self, strict, false, scope, interner)?;
286 analyze_binding_escapes(self, false, scope.clone(), interner)?;
287 optimize_scope_indices_function_constructor(self, scope);
288 Ok(())
289 }
290}
291
292impl ToIndentedString for FunctionExpression {
293 fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
294 let mut buf = "function".to_owned();
295 if self.has_binding_identifier
296 && let Some(name) = self.name
297 {
298 let _ = write!(buf, " {}", interner.resolve_expect(name.sym()));
299 }
300 let _ = write!(
301 buf,
302 "({}) {}",
303 join_nodes(interner, self.parameters.as_ref()),
304 block_to_string(&self.body.statements, interner, indentation)
305 );
306
307 buf
308 }
309}
310
311impl From<FunctionExpression> for Expression {
312 #[inline]
313 fn from(expr: FunctionExpression) -> Self {
314 Self::FunctionExpression(expr)
315 }
316}
317
318impl VisitWith for FunctionExpression {
319 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
320 where
321 V: Visitor<'a>,
322 {
323 if let Some(ident) = &self.name {
324 visitor.visit_identifier(ident)?;
325 }
326 visitor.visit_formal_parameter_list(&self.parameters)?;
327 visitor.visit_function_body(&self.body)
328 }
329
330 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
331 where
332 V: VisitorMut<'a>,
333 {
334 if let Some(ident) = &mut self.name {
335 visitor.visit_identifier_mut(ident)?;
336 }
337 visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
338 visitor.visit_function_body_mut(&mut self.body)
339 }
340}