use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
lexical_environment::VariableScope,
},
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{Block, Identifier, Node},
BoaProfiler, Context, JsResult, JsValue,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Try {
block: Block,
catch: Option<Catch>,
finally: Option<Finally>,
}
impl Try {
pub(in crate::syntax) fn new<B>(
block: B,
catch: Option<Catch>,
finally: Option<Finally>,
) -> Self
where
B: Into<Block>,
{
assert!(
catch.is_some() || finally.is_some(),
"one of catch or finally must be pressent"
);
Self {
block: block.into(),
catch,
finally,
}
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn catch(&self) -> Option<&Catch> {
self.catch.as_ref()
}
pub fn finally(&self) -> Option<&Block> {
self.finally.as_ref().map(Finally::block)
}
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "{}try ", " ".repeat(indentation))?;
self.block.display(f, indentation)?;
if let Some(ref catch) = self.catch {
catch.display(f, indentation)?;
}
if let Some(ref finally) = self.finally {
finally.display(f, indentation)?;
}
Ok(())
}
}
impl Executable for Try {
fn run(&self, context: &mut Context) -> JsResult<JsValue> {
let _timer = BoaProfiler::global().start_event("Try", "exec");
let res = self.block().run(context).map_or_else(
|err| {
if let Some(catch) = self.catch() {
{
let env = context.get_current_environment();
context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
if let Some(param) = catch.parameter() {
context.create_mutable_binding(param, false, VariableScope::Block)?;
context.initialize_binding(param, err)?;
}
}
let res = catch.block().run(context);
let _ = context.pop_environment();
res
} else {
Err(err)
}
},
Ok,
);
if let Some(finally) = self.finally() {
finally.run(context)?;
}
res
}
}
impl fmt::Display for Try {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<Try> for Node {
fn from(try_catch: Try) -> Self {
Self::Try(try_catch)
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Catch {
parameter: Option<Identifier>,
block: Block,
}
impl Catch {
pub(in crate::syntax) fn new<OI, I, B>(parameter: OI, block: B) -> Self
where
OI: Into<Option<I>>,
I: Into<Identifier>,
B: Into<Block>,
{
Self {
parameter: parameter.into().map(I::into),
block: block.into(),
}
}
pub fn parameter(&self) -> Option<&str> {
self.parameter.as_ref().map(Identifier::as_ref)
}
pub fn block(&self) -> &Block {
&self.block
}
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
f.write_str(" catch")?;
if let Some(ref param) = self.parameter {
write!(f, "({})", param)?;
}
f.write_str(" ")?;
self.block.display(f, indentation)
}
}
impl fmt::Display for Catch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Finally {
block: Block,
}
impl Finally {
pub fn block(&self) -> &Block {
&self.block
}
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
f.write_str(" finally ")?;
self.block.display(f, indentation)
}
}
impl<T> From<T> for Finally
where
T: Into<Block>,
{
fn from(block: T) -> Self {
Self {
block: block.into(),
}
}
}