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

1//! Template literal node.
2
3use super::Node;
4use crate::{builtins::Array, exec::Executable, BoaProfiler, Context, JsResult, JsValue};
5use gc::{Finalize, Trace};
6
7#[cfg(feature = "deser")]
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11#[cfg(test)]
12mod tests;
13
14/// Template literals are string literals allowing embedded expressions.
15///
16/// More information:
17///  - [ECMAScript reference][spec]
18///  - [MDN documentation][mdn]
19///
20/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
21/// [spec]: https://tc39.es/ecma262/#sec-template-literals
22#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
23#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
24pub struct TemplateLit {
25    elements: Vec<TemplateElement>,
26}
27
28impl TemplateLit {
29    pub fn new(elements: Vec<TemplateElement>) -> Self {
30        TemplateLit { elements }
31    }
32}
33
34impl Executable for TemplateLit {
35    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
36        let _timer = BoaProfiler::global().start_event("TemplateLiteral", "exec");
37        let mut result = String::new();
38
39        for element in self.elements.iter() {
40            match element {
41                TemplateElement::String(s) => {
42                    result.push_str(s);
43                }
44                TemplateElement::Expr(node) => {
45                    let value = node.run(context)?;
46                    let s = value.to_string(context)?;
47                    result.push_str(&s);
48                }
49            }
50        }
51        Ok(result.into())
52    }
53}
54
55impl fmt::Display for TemplateLit {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "`")?;
58        for elt in &self.elements {
59            match elt {
60                TemplateElement::String(s) => write!(f, "{}", s)?,
61                TemplateElement::Expr(n) => write!(f, "${{{}}}", n)?,
62            }
63        }
64        write!(f, "`")
65    }
66}
67#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
68#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
69pub struct TaggedTemplate {
70    tag: Box<Node>,
71    raws: Vec<Box<str>>,
72    cookeds: Vec<Option<Box<str>>>,
73    exprs: Vec<Node>,
74}
75
76impl TaggedTemplate {
77    pub fn new(
78        tag: Node,
79        raws: Vec<Box<str>>,
80        cookeds: Vec<Option<Box<str>>>,
81        exprs: Vec<Node>,
82    ) -> Self {
83        Self {
84            tag: Box::new(tag),
85            raws,
86            cookeds,
87            exprs,
88        }
89    }
90}
91
92impl Executable for TaggedTemplate {
93    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
94        let _timer = BoaProfiler::global().start_event("TaggedTemplate", "exec");
95
96        let template_object = Array::new_array(context);
97        let raw_array = Array::new_array(context);
98
99        for (i, raw) in self.raws.iter().enumerate() {
100            raw_array.set_field(i, JsValue::new(raw.as_ref()), false, context)?;
101        }
102
103        for (i, cooked) in self.cookeds.iter().enumerate() {
104            if let Some(cooked) = cooked {
105                template_object.set_field(i, JsValue::new(cooked.as_ref()), false, context)?;
106            } else {
107                template_object.set_field(i, JsValue::undefined(), false, context)?;
108            }
109        }
110        template_object.set_field("raw", raw_array, false, context)?;
111
112        let (this, func) = match *self.tag {
113            Node::GetConstField(ref get_const_field) => {
114                let mut obj = get_const_field.obj().run(context)?;
115                if !obj.is_object() {
116                    obj = JsValue::Object(obj.to_object(context)?);
117                }
118                (
119                    obj.clone(),
120                    obj.get_field(get_const_field.field(), context)?,
121                )
122            }
123            Node::GetField(ref get_field) => {
124                let obj = get_field.obj().run(context)?;
125                let field = get_field.field().run(context)?;
126                (
127                    obj.clone(),
128                    obj.get_field(field.to_property_key(context)?, context)?,
129                )
130            }
131            _ => (context.global_object().into(), self.tag.run(context)?),
132        };
133
134        let mut args = vec![template_object];
135        for expr in self.exprs.iter() {
136            args.push(expr.run(context)?);
137        }
138
139        context.call(&func, &this, &args)
140    }
141}
142
143impl fmt::Display for TaggedTemplate {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{}`", self.tag)?;
146        for (raw, expr) in self.raws.iter().zip(self.exprs.iter()) {
147            write!(f, "{}${{{}}}", raw, expr)?;
148        }
149        write!(f, "`")
150    }
151}
152
153impl From<TaggedTemplate> for Node {
154    fn from(template: TaggedTemplate) -> Self {
155        Node::TaggedTemplate(template)
156    }
157}
158
159#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
160#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
161pub enum TemplateElement {
162    String(Box<str>),
163    Expr(Node),
164}