Skip to main content

boa_ast/expression/
call.rs

1use crate::visitor::{VisitWith, Visitor, VisitorMut};
2use crate::{Span, Spanned, join_nodes};
3use boa_interner::{Interner, ToInternedString};
4use core::ops::ControlFlow;
5
6use super::Expression;
7
8/// Calling the function actually performs the specified actions with the indicated parameters.
9///
10/// Defining a function does not execute it. Defining it simply names the function and
11/// specifies what to do when the function is called. Functions must be in scope when they are
12/// called, but the function declaration can be hoisted. The scope of a function is the
13/// function in which it is declared (or the entire program, if it is declared at the top
14/// level).
15///
16/// More information:
17///  - [ECMAScript reference][spec]
18///  - [MDN documentation][mdn]
19///
20/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
21/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[derive(Clone, Debug, PartialEq)]
25pub struct Call {
26    function: Box<Expression>,
27    args: Box<[Expression]>,
28    span: Span,
29}
30
31impl Call {
32    /// Creates a new `Call` AST Expression.
33    #[inline]
34    #[must_use]
35    pub fn new(function: Expression, args: Box<[Expression]>, span: Span) -> Self {
36        Self {
37            function: Box::new(function),
38            args,
39            span,
40        }
41    }
42
43    /// Gets the target function of this call expression.
44    #[inline]
45    #[must_use]
46    pub const fn function(&self) -> &Expression {
47        &self.function
48    }
49
50    /// Retrieves the arguments passed to the function.
51    #[inline]
52    #[must_use]
53    pub const fn args(&self) -> &[Expression] {
54        &self.args
55    }
56}
57
58impl Spanned for Call {
59    #[inline]
60    fn span(&self) -> Span {
61        self.span
62    }
63}
64
65impl ToInternedString for Call {
66    #[inline]
67    fn to_interned_string(&self, interner: &Interner) -> String {
68        format!(
69            "{}({})",
70            self.function.to_interned_string(interner),
71            join_nodes(interner, &self.args)
72        )
73    }
74}
75
76impl From<Call> for Expression {
77    #[inline]
78    fn from(call: Call) -> Self {
79        Self::Call(call)
80    }
81}
82
83impl VisitWith for Call {
84    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
85    where
86        V: Visitor<'a>,
87    {
88        visitor.visit_expression(&self.function)?;
89        for expr in &*self.args {
90            visitor.visit_expression(expr)?;
91        }
92        ControlFlow::Continue(())
93    }
94
95    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
96    where
97        V: VisitorMut<'a>,
98    {
99        visitor.visit_expression_mut(&mut self.function)?;
100        for expr in &mut *self.args {
101            visitor.visit_expression_mut(expr)?;
102        }
103        ControlFlow::Continue(())
104    }
105}
106
107/// The `super` keyword is used to access and call functions on an object's parent.
108///
109/// More information:
110///  - [ECMAScript reference][spec]
111///  - [MDN documentation][mdn]
112///
113/// [spec]: https://tc39.es/ecma262/#prod-SuperCall
114/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
115#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
117#[derive(Clone, Debug, PartialEq)]
118pub struct SuperCall {
119    args: Box<[Expression]>,
120    span: Span,
121}
122
123impl SuperCall {
124    /// Creates a new `SuperCall` AST node.
125    pub fn new<A>(args: A, span: Span) -> Self
126    where
127        A: Into<Box<[Expression]>>,
128    {
129        Self {
130            args: args.into(),
131            span,
132        }
133    }
134
135    /// Retrieves the arguments of the super call.
136    #[must_use]
137    pub const fn arguments(&self) -> &[Expression] {
138        &self.args
139    }
140}
141
142impl Spanned for SuperCall {
143    #[inline]
144    fn span(&self) -> Span {
145        self.span
146    }
147}
148
149impl ToInternedString for SuperCall {
150    #[inline]
151    fn to_interned_string(&self, interner: &Interner) -> String {
152        format!("super({})", join_nodes(interner, &self.args))
153    }
154}
155
156impl From<SuperCall> for Expression {
157    #[inline]
158    fn from(call: SuperCall) -> Self {
159        Self::SuperCall(call)
160    }
161}
162
163impl VisitWith for SuperCall {
164    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
165    where
166        V: Visitor<'a>,
167    {
168        for expr in &*self.args {
169            visitor.visit_expression(expr)?;
170        }
171        ControlFlow::Continue(())
172    }
173
174    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
175    where
176        V: VisitorMut<'a>,
177    {
178        for expr in &mut *self.args {
179            visitor.visit_expression_mut(expr)?;
180        }
181        ControlFlow::Continue(())
182    }
183}
184
185/// The phase of a dynamic import call.
186///
187/// Determines how the imported module is handled:
188/// - `Evaluation` (default): `import(specifier)` — loads, links, and evaluates the module.
189/// - `Defer`: `import.defer(specifier)` — deferred evaluation of the module.
190/// - `Source`: `import.source(specifier)` — source phase import.
191///
192/// More information:
193///  - [import-defer proposal][defer]
194///  - [source-phase-imports proposal][source]
195///
196/// [defer]: https://github.com/tc39/proposal-defer-import-eval
197/// [source]: https://github.com/tc39/proposal-source-phase-imports
198#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
200#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
201pub enum ImportPhase {
202    /// `import(specifier)` — standard dynamic import.
203    #[default]
204    Evaluation,
205    /// `import.defer(specifier)` — deferred import evaluation.
206    Defer,
207    /// `import.source(specifier)` — source phase import.
208    Source,
209}
210
211/// The `import()` syntax, commonly called dynamic import, is a function-like expression that allows
212/// loading an ECMAScript module asynchronously and dynamically into a potentially non-module
213/// environment.
214///
215/// More information:
216///  - [ECMAScript reference][spec]
217///  - [MDN documentation][mdn]
218///
219/// [spec]: https://tc39.es/ecma262/#prod-ImportCall
220/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
221#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
222#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
223#[derive(Clone, Debug, PartialEq)]
224pub struct ImportCall {
225    specifier: Box<Expression>,
226    options: Option<Box<Expression>>,
227    phase: ImportPhase,
228    span: Span,
229}
230
231impl ImportCall {
232    /// Creates a new `ImportCall` AST node.
233    #[inline]
234    #[must_use]
235    pub fn new<S>(specifier: S, options: Option<Expression>, phase: ImportPhase, span: Span) -> Self
236    where
237        S: Into<Expression>,
238    {
239        Self {
240            specifier: Box::new(specifier.into()),
241            options: options.map(Box::new),
242            phase,
243            span,
244        }
245    }
246
247    /// Retrieves the specifier (first argument) of the import call.
248    #[inline]
249    #[must_use]
250    pub const fn specifier(&self) -> &Expression {
251        &self.specifier
252    }
253
254    /// Retrieves the options (second argument) of the import call, if present.
255    ///
256    /// This is used for import attributes:
257    /// ```js
258    /// import("foo.json", { with: { type: "json" } })
259    /// ```
260    #[inline]
261    #[must_use]
262    pub fn options(&self) -> Option<&Expression> {
263        self.options.as_deref()
264    }
265
266    /// Returns the phase of this import call.
267    #[inline]
268    #[must_use]
269    pub const fn phase(&self) -> ImportPhase {
270        self.phase
271    }
272
273    /// Gets the module specifier of the import call.
274    ///
275    /// This is an alias for [`Self::specifier`] for backwards compatibility.
276    #[inline]
277    #[must_use]
278    #[deprecated(since = "0.21.0", note = "use `specifier` instead")]
279    pub const fn argument(&self) -> &Expression {
280        &self.specifier
281    }
282}
283
284impl Spanned for ImportCall {
285    #[inline]
286    fn span(&self) -> Span {
287        self.span
288    }
289}
290
291impl ToInternedString for ImportCall {
292    #[inline]
293    fn to_interned_string(&self, interner: &Interner) -> String {
294        let phase_str = match self.phase {
295            ImportPhase::Evaluation => "",
296            ImportPhase::Defer => ".defer",
297            ImportPhase::Source => ".source",
298        };
299        if let Some(options) = &self.options {
300            format!(
301                "import{}({}, {})",
302                phase_str,
303                self.specifier.to_interned_string(interner),
304                options.to_interned_string(interner)
305            )
306        } else {
307            format!(
308                "import{}({})",
309                phase_str,
310                self.specifier.to_interned_string(interner)
311            )
312        }
313    }
314}
315
316impl From<ImportCall> for Expression {
317    #[inline]
318    fn from(call: ImportCall) -> Self {
319        Self::ImportCall(call)
320    }
321}
322
323impl VisitWith for ImportCall {
324    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
325    where
326        V: Visitor<'a>,
327    {
328        visitor.visit_expression(&self.specifier)?;
329        if let Some(options) = &self.options {
330            visitor.visit_expression(options)?;
331        }
332        ControlFlow::Continue(())
333    }
334
335    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
336    where
337        V: VisitorMut<'a>,
338    {
339        visitor.visit_expression_mut(&mut self.specifier)?;
340        if let Some(options) = &mut self.options {
341            visitor.visit_expression_mut(options)?;
342        }
343        ControlFlow::Continue(())
344    }
345}