boa/syntax/ast/node/template/
mod.rs1use 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#[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}