1use 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#[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#[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 Catch(Catch),
41 Finally(Finally),
43 Full(Catch, Finally),
45}
46
47impl Try {
48 #[inline]
50 #[must_use]
51 pub const fn new(block: Block, handler: ErrorHandler) -> Self {
52 Self { block, handler }
53 }
54
55 #[inline]
57 #[must_use]
58 pub const fn block(&self) -> &Block {
59 &self.block
60 }
61
62 #[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 #[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#[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 #[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) = ¶meter {
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 #[inline]
173 #[must_use]
174 pub const fn parameter(&self) -> Option<&Binding> {
175 self.parameter.as_ref()
176 }
177
178 #[inline]
180 #[must_use]
181 pub const fn block(&self) -> &Block {
182 &self.block
183 }
184
185 #[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#[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 #[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}