boa/syntax/ast/node/try_node/
mod.rs

1use crate::{
2    environment::{
3        declarative_environment_record::DeclarativeEnvironmentRecord,
4        lexical_environment::VariableScope,
5    },
6    exec::Executable,
7    gc::{Finalize, Trace},
8    syntax::ast::node::{Block, Identifier, Node},
9    BoaProfiler, Context, JsResult, JsValue,
10};
11use std::fmt;
12
13#[cfg(feature = "deser")]
14use serde::{Deserialize, Serialize};
15
16#[cfg(test)]
17mod tests;
18
19/// The `try...catch` statement marks a block of statements to try and specifies a response
20/// should an exception be thrown.
21///
22/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}`
23/// must always be used, even for single statements. At least one `catch`-block, or a
24/// `finally`-block, must be present.
25///
26/// More information:
27///  - [ECMAScript reference][spec]
28///  - [MDN documentation][mdn]
29///
30/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
31/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
32#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
33#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
34pub struct Try {
35    block: Block,
36    catch: Option<Catch>,
37    finally: Option<Finally>,
38}
39
40impl Try {
41    /// Creates a new `Try` AST node.
42    pub(in crate::syntax) fn new<B>(
43        block: B,
44        catch: Option<Catch>,
45        finally: Option<Finally>,
46    ) -> Self
47    where
48        B: Into<Block>,
49    {
50        assert!(
51            catch.is_some() || finally.is_some(),
52            "one of catch or finally must be pressent"
53        );
54
55        Self {
56            block: block.into(),
57            catch,
58            finally,
59        }
60    }
61
62    /// Gets the `try` block.
63    pub fn block(&self) -> &Block {
64        &self.block
65    }
66
67    /// Gets the `catch` block, if any.
68    pub fn catch(&self) -> Option<&Catch> {
69        self.catch.as_ref()
70    }
71
72    /// Gets the `finally` block, if any.
73    pub fn finally(&self) -> Option<&Block> {
74        self.finally.as_ref().map(Finally::block)
75    }
76
77    /// Implements the display formatting with indentation.
78    pub(in crate::syntax::ast::node) fn display(
79        &self,
80        f: &mut fmt::Formatter<'_>,
81        indentation: usize,
82    ) -> fmt::Result {
83        write!(f, "{}try ", "    ".repeat(indentation))?;
84        self.block.display(f, indentation)?;
85
86        if let Some(ref catch) = self.catch {
87            catch.display(f, indentation)?;
88        }
89
90        if let Some(ref finally) = self.finally {
91            finally.display(f, indentation)?;
92        }
93        Ok(())
94    }
95}
96
97impl Executable for Try {
98    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
99        let _timer = BoaProfiler::global().start_event("Try", "exec");
100        let res = self.block().run(context).map_or_else(
101            |err| {
102                if let Some(catch) = self.catch() {
103                    {
104                        let env = context.get_current_environment();
105                        context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
106
107                        if let Some(param) = catch.parameter() {
108                            context.create_mutable_binding(param, false, VariableScope::Block)?;
109                            context.initialize_binding(param, err)?;
110                        }
111                    }
112
113                    let res = catch.block().run(context);
114
115                    // pop the block env
116                    let _ = context.pop_environment();
117
118                    res
119                } else {
120                    Err(err)
121                }
122            },
123            Ok,
124        );
125
126        if let Some(finally) = self.finally() {
127            finally.run(context)?;
128        }
129
130        res
131    }
132}
133
134impl fmt::Display for Try {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        self.display(f, 0)
137    }
138}
139
140impl From<Try> for Node {
141    fn from(try_catch: Try) -> Self {
142        Self::Try(try_catch)
143    }
144}
145
146/// Catch block.
147#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
148#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
149pub struct Catch {
150    parameter: Option<Identifier>,
151    block: Block,
152}
153
154impl Catch {
155    /// Creates a new catch block.
156    pub(in crate::syntax) fn new<OI, I, B>(parameter: OI, block: B) -> Self
157    where
158        OI: Into<Option<I>>,
159        I: Into<Identifier>,
160        B: Into<Block>,
161    {
162        Self {
163            parameter: parameter.into().map(I::into),
164            block: block.into(),
165        }
166    }
167
168    /// Gets the parameter of the catch block.
169    pub fn parameter(&self) -> Option<&str> {
170        self.parameter.as_ref().map(Identifier::as_ref)
171    }
172
173    /// Retrieves the catch execution block.
174    pub fn block(&self) -> &Block {
175        &self.block
176    }
177
178    /// Implements the display formatting with indentation.
179    pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
180        f.write_str(" catch")?;
181        if let Some(ref param) = self.parameter {
182            write!(f, "({})", param)?;
183        }
184        f.write_str(" ")?;
185        self.block.display(f, indentation)
186    }
187}
188
189impl fmt::Display for Catch {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        self.display(f, 0)
192    }
193}
194
195/// Finally block.
196#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
197#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
198pub struct Finally {
199    block: Block,
200}
201
202impl Finally {
203    /// Gets the finally block.
204    pub fn block(&self) -> &Block {
205        &self.block
206    }
207
208    /// Implements the display formatting with indentation.
209    pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
210        f.write_str(" finally ")?;
211        self.block.display(f, indentation)
212    }
213}
214
215impl<T> From<T> for Finally
216where
217    T: Into<Block>,
218{
219    fn from(block: T) -> Self {
220        Self {
221            block: block.into(),
222        }
223    }
224}