Skip to main content

boa_ast/statement/
try.rs

1//! Error handling statements
2
3use crate::operations::{ContainsSymbol, contains};
4use crate::scope::Scope;
5use crate::visitor::{VisitWith, Visitor, VisitorMut};
6use crate::{
7    declaration::Binding,
8    statement::{Block, Statement},
9};
10use boa_interner::{Interner, ToIndentedString, ToInternedString};
11use core::{fmt::Write as _, ops::ControlFlow};
12
13/// The `try...catch` statement marks a block of statements to try and specifies a response
14/// should an exception be thrown.
15///
16/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}`
17/// must always be used, even for single statements. At least one `catch`-block, or a
18/// `finally`-block, must be present.
19///
20/// More information:
21///  - [ECMAScript reference][spec]
22///  - [MDN documentation][mdn]
23///
24/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
25/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
28#[derive(Clone, Debug, PartialEq)]
29pub struct Try {
30    block: Block,
31    handler: ErrorHandler,
32}
33
34/// The type of error handler in a [`Try`] statement.
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
37#[derive(Clone, Debug, PartialEq)]
38pub enum ErrorHandler {
39    /// A [`Catch`] error handler.
40    Catch(Catch),
41    /// A [`Finally`] error handler.
42    Finally(Finally),
43    /// A [`Catch`] and [`Finally`] error handler.
44    Full(Catch, Finally),
45}
46
47impl Try {
48    /// Creates a new `Try` AST node.
49    #[inline]
50    #[must_use]
51    pub const fn new(block: Block, handler: ErrorHandler) -> Self {
52        Self { block, handler }
53    }
54
55    /// Gets the `try` block.
56    #[inline]
57    #[must_use]
58    pub const fn block(&self) -> &Block {
59        &self.block
60    }
61
62    /// Gets the `catch` block, if any.
63    #[inline]
64    #[must_use]
65    pub const fn catch(&self) -> Option<&Catch> {
66        match &self.handler {
67            ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c),
68            ErrorHandler::Finally(_) => None,
69        }
70    }
71
72    /// Gets the `finally` block, if any.
73    #[inline]
74    #[must_use]
75    pub const fn finally(&self) -> Option<&Finally> {
76        match &self.handler {
77            ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f),
78            ErrorHandler::Catch(_) => None,
79        }
80    }
81}
82
83impl ToIndentedString for Try {
84    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
85        let mut buf = format!(
86            "{}try {}",
87            "    ".repeat(indentation),
88            self.block.to_indented_string(interner, indentation)
89        );
90
91        if let Some(catch) = self.catch() {
92            buf.push_str(&catch.to_indented_string(interner, indentation));
93        }
94
95        if let Some(finally) = self.finally() {
96            buf.push_str(&finally.to_indented_string(interner, indentation));
97        }
98        buf
99    }
100}
101
102impl From<Try> for Statement {
103    #[inline]
104    fn from(try_catch: Try) -> Self {
105        Self::Try(try_catch)
106    }
107}
108
109impl VisitWith for Try {
110    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
111    where
112        V: Visitor<'a>,
113    {
114        visitor.visit_block(&self.block)?;
115        if let Some(catch) = &self.catch() {
116            visitor.visit_catch(catch)?;
117        }
118        if let Some(finally) = &self.finally() {
119            visitor.visit_finally(finally)?;
120        }
121        ControlFlow::Continue(())
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_block_mut(&mut self.block)?;
129        match &mut self.handler {
130            ErrorHandler::Catch(c) => visitor.visit_catch_mut(c)?,
131            ErrorHandler::Finally(f) => visitor.visit_finally_mut(f)?,
132            ErrorHandler::Full(c, f) => {
133                visitor.visit_catch_mut(c)?;
134                visitor.visit_finally_mut(f)?;
135            }
136        }
137        ControlFlow::Continue(())
138    }
139}
140
141/// Catch block.
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
144#[derive(Clone, Debug, PartialEq)]
145pub struct Catch {
146    pub(crate) parameter: Option<Binding>,
147    pub(crate) block: Block,
148    pub(crate) contains_direct_eval: bool,
149
150    #[cfg_attr(feature = "serde", serde(skip))]
151    pub(crate) scope: Scope,
152}
153
154impl Catch {
155    /// Creates a new catch block.
156    #[inline]
157    #[must_use]
158    pub fn new(parameter: Option<Binding>, block: Block) -> Self {
159        let mut contains_direct_eval = contains(&block, ContainsSymbol::DirectEval);
160        if let Some(param) = &parameter {
161            contains_direct_eval |= contains(param, ContainsSymbol::DirectEval);
162        }
163        Self {
164            parameter,
165            block,
166            contains_direct_eval,
167            scope: Scope::default(),
168        }
169    }
170
171    /// Gets the parameter of the catch block.
172    #[inline]
173    #[must_use]
174    pub const fn parameter(&self) -> Option<&Binding> {
175        self.parameter.as_ref()
176    }
177
178    /// Retrieves the catch execution block.
179    #[inline]
180    #[must_use]
181    pub const fn block(&self) -> &Block {
182        &self.block
183    }
184
185    /// Returns the scope of the catch block.
186    #[inline]
187    #[must_use]
188    pub const fn scope(&self) -> &Scope {
189        &self.scope
190    }
191}
192
193impl ToIndentedString for Catch {
194    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
195        let mut buf = " catch".to_owned();
196        if let Some(param) = &self.parameter {
197            let _ = write!(buf, "({})", param.to_interned_string(interner));
198        }
199        let _ = write!(
200            buf,
201            " {}",
202            self.block.to_indented_string(interner, indentation)
203        );
204
205        buf
206    }
207}
208
209impl VisitWith for Catch {
210    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
211    where
212        V: Visitor<'a>,
213    {
214        if let Some(binding) = &self.parameter {
215            visitor.visit_binding(binding)?;
216        }
217        visitor.visit_block(&self.block)
218    }
219
220    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
221    where
222        V: VisitorMut<'a>,
223    {
224        if let Some(binding) = &mut self.parameter {
225            visitor.visit_binding_mut(binding)?;
226        }
227        visitor.visit_block_mut(&mut self.block)
228    }
229}
230
231/// Finally block.
232#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
233#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
234#[derive(Clone, Debug, PartialEq)]
235pub struct Finally {
236    block: Block,
237}
238
239impl Finally {
240    /// Gets the finally block.
241    #[inline]
242    #[must_use]
243    pub const fn block(&self) -> &Block {
244        &self.block
245    }
246}
247
248impl ToIndentedString for Finally {
249    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
250        format!(
251            " finally {}",
252            self.block.to_indented_string(interner, indentation)
253        )
254    }
255}
256
257impl From<Block> for Finally {
258    #[inline]
259    fn from(block: Block) -> Self {
260        Self { block }
261    }
262}
263
264impl VisitWith for Finally {
265    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
266    where
267        V: Visitor<'a>,
268    {
269        visitor.visit_block(&self.block)
270    }
271
272    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
273    where
274        V: VisitorMut<'a>,
275    {
276        visitor.visit_block_mut(&mut self.block)
277    }
278}