use super::*;
impl JsParser {
pub(super) fn eval_tagged_template(&mut self, tagged: &TaggedTpl) -> NodeId {
let tag = self.eval_expr(&tagged.tag);
let call_name = self
.graph
.node(tag)
.and_then(|n| n.alias.as_ref())
.cloned()
.unwrap_or_else(|| self.name(tag));
let id = self.graph.add_node(NodeKind::Call, call_name.clone(), None);
if let Some(node) = self.graph.node_mut(id) {
node.alias = Some(call_name);
}
self.graph.add_edge(tag, id, EdgeKind::Call);
self.graph.add_edge(tag, id, EdgeKind::Assignment);
for expr in &tagged.tpl.exprs {
let value = self.eval_expr(expr);
self.graph.add_edge(value, id, EdgeKind::Argument);
}
id
}
pub(super) fn eval_array_literal(&mut self, array: &ArrayLit) -> NodeId {
let id = self.graph.add_node(NodeKind::Variable, "<array>".into(), None);
let element_any = self.graph.add_node(NodeKind::Variable, "<array>[]".into(), None);
let mut bindings = vec![("[]".to_string(), element_any)];
for (index, element) in array.elems.iter().enumerate() {
let Some(element) = element else {
continue;
};
let value = self.eval_expr(&element.expr);
let element_node = self
.graph
.add_node(NodeKind::Variable, format!("<array>[{}]", index), None);
self.flow(value, element_node);
self.flow(element_node, element_any);
self.flow(element_node, id);
bindings.push((format!("[{}]", index), element_node));
}
self.flow(element_any, id);
self.composite_bindings.insert(id, bindings);
id
}
pub(super) fn eval_object_literal(&mut self, object: &ObjectLit) -> NodeId {
let id = self.graph.add_node(NodeKind::Variable, "<object>".into(), None);
let mut bindings = Vec::new();
for prop in &object.props {
let value = match prop {
PropOrSpread::Prop(prop) => match &**prop {
Prop::KeyValue(kv) => {
let value = self.eval_expr(&kv.value);
if let Some(key) = prop_name(&kv.key) {
let prop_node = self.graph.add_node(
NodeKind::Variable,
format!("<object>.{}", key),
None,
);
self.flow(value, prop_node);
self.flow(prop_node, id);
bindings.push((format!(".{}", key), prop_node));
if let Some(children) = self.composite_bindings.get(&value).cloned() {
for (suffix, node_id) in children {
bindings.push((format!(".{}{}", key, suffix), node_id));
}
}
continue;
}
value
}
Prop::Shorthand(i) => {
let value = self.eval_expr(&Expr::Ident(i.clone()));
let prop_node = self.graph.add_node(
NodeKind::Variable,
format!("<object>.{}", i.sym),
None,
);
self.flow(value, prop_node);
self.flow(prop_node, id);
bindings.push((format!(".{}", i.sym), prop_node));
if let Some(children) = self.composite_bindings.get(&value).cloned() {
for (suffix, node_id) in children {
bindings.push((format!(".{}{}", i.sym, suffix), node_id));
}
}
continue;
}
Prop::Assign(a) => {
let value = self.eval_expr(&a.value);
let prop_node = self.graph.add_node(
NodeKind::Variable,
format!("<object>.{}", a.key.sym),
None,
);
self.flow(value, prop_node);
self.flow(prop_node, id);
bindings.push((format!(".{}", a.key.sym), prop_node));
if let Some(children) = self.composite_bindings.get(&value).cloned() {
for (suffix, node_id) in children {
bindings.push((format!(".{}{}", a.key.sym, suffix), node_id));
}
}
continue;
}
Prop::Method(m) => {
let value = self.eval_fn(&FnExpr {
ident: prop_ident(&m.key),
function: m.function.clone(),
});
if let Some(key) = prop_name(&m.key) {
let prop_node = self.graph.add_node(
NodeKind::Variable,
format!("<object>.{}", key),
None,
);
self.flow(value, prop_node);
self.flow(prop_node, id);
bindings.push((format!(".{}", key), prop_node));
continue;
}
value
}
Prop::Getter(g) => {
let name = prop_ident(&g.key)
.map(|i| i.sym.to_string())
.unwrap_or_else(|| "<getter>".to_string());
let getter = self.graph.add_node(NodeKind::Variable, name, None);
let prev_fn = self.current_function_node.replace(getter);
if let Some(body) = &g.body {
self.scopes.push(Scope::new());
for stmt in &body.stmts {
self.visit_stmt(stmt);
}
self.scopes.pop();
}
self.current_function_node = prev_fn;
if let Some(key) = prop_name(&g.key) {
let prop_node = self.graph.add_node(
NodeKind::Variable,
format!("<object>.{}", key),
None,
);
self.flow(getter, prop_node);
self.flow(prop_node, id);
bindings.push((format!(".{}", key), prop_node));
continue;
}
getter
}
Prop::Setter(s) => {
let name = prop_ident(&s.key)
.map(|i| i.sym.to_string())
.unwrap_or_else(|| "<setter>".to_string());
let setter = self.graph.add_node(NodeKind::Variable, name, None);
self.scopes.push(Scope::new());
self.bind_pat(&s.param);
if let Some(body) = &s.body {
for stmt in &body.stmts {
self.visit_stmt(stmt);
}
}
self.scopes.pop();
if let Some(key) = prop_name(&s.key) {
let prop_node = self.graph.add_node(
NodeKind::Variable,
format!("<object>.{}", key),
None,
);
self.flow(setter, prop_node);
self.flow(prop_node, id);
bindings.push((format!(".{}", key), prop_node));
continue;
}
setter
}
},
PropOrSpread::Spread(s) => self.eval_expr(&s.expr),
};
self.flow(value, id);
}
self.composite_bindings.insert(id, bindings);
id
}
pub(crate) fn eval_fn(&mut self, f: &FnExpr) -> NodeId {
let name = f
.ident
.as_ref()
.map(|i| i.sym.to_string())
.unwrap_or_else(|| "<anon>".to_string());
let id = self.graph.add_node(NodeKind::Variable, name, None);
let prev_fn = self.current_function_node.replace(id);
self.scopes.push(Scope::new());
let pending_sources = self
.pending_callback_param_sources
.take()
.unwrap_or_default();
let mut params = Vec::new();
for (index, param) in f.function.params.iter().enumerate() {
let source = pending_sources.get(index).copied().flatten();
params.extend(self.bind_pat_with_source(¶m.pat, source));
}
self.last_fn_params = Some(params.clone());
let is_outermost = self.fn_expr_depth == 0;
self.fn_expr_depth += 1;
if is_outermost {
self.fn_expr_params_at_depth_0 = Some(params.clone());
}
self.node_to_params.insert(id, params.clone());
if let Some(promise_node) = self.pending_promise_node {
if let Some(resolve_param) = params.first() {
self.promise_resolvers.push((*resolve_param, promise_node));
}
if let Some(reject_param) = params.get(1) {
self.promise_resolvers.push((*reject_param, promise_node));
}
self.pending_promise_node = None;
}
if let Some(body) = &f.function.body {
for stmt in &body.stmts {
self.visit_stmt(stmt);
}
}
self.fn_expr_depth -= 1;
self.scopes.pop();
self.current_function_node = prev_fn;
id
}
pub(crate) fn eval_arrow(&mut self, a: &ArrowExpr) -> NodeId {
let id = self.graph.add_node(NodeKind::Variable, "<arrow>".into(), None);
let prev_fn = self.current_function_node.replace(id);
self.scopes.push(Scope::new());
let pending_sources = self
.pending_callback_param_sources
.take()
.unwrap_or_default();
let mut params = Vec::new();
for (index, param) in a.params.iter().enumerate() {
let source = pending_sources.get(index).copied().flatten();
params.extend(self.bind_pat_with_source(param, source));
}
self.last_fn_params = Some(params.clone());
let is_outermost = self.fn_expr_depth == 0;
self.fn_expr_depth += 1;
if is_outermost {
self.fn_expr_params_at_depth_0 = Some(params.clone());
}
self.node_to_params.insert(id, params.clone());
if let Some(promise_node) = self.pending_promise_node {
if let Some(resolve_param) = params.first() {
self.promise_resolvers.push((*resolve_param, promise_node));
}
if let Some(reject_param) = params.get(1) {
self.promise_resolvers.push((*reject_param, promise_node));
}
self.pending_promise_node = None;
}
match &*a.body {
BlockStmtOrExpr::BlockStmt(b) => {
for stmt in &b.stmts {
self.visit_stmt(stmt);
}
}
BlockStmtOrExpr::Expr(e) => {
let result = self.eval_expr(e);
self.graph.add_edge(result, id, EdgeKind::Return);
}
}
self.fn_expr_depth -= 1;
self.scopes.pop();
self.current_function_node = prev_fn;
id
}
}