swc_ecma_quote_macros/
ctxt.rs1#![allow(unused)]
2
3use std::cell::RefCell;
4
5use rustc_hash::FxHashMap;
6use swc_macros_common::call_site;
7use syn::{parse_quote, punctuated::Punctuated, ExprPath, ExprReference, Ident, Token};
8
9use crate::{ast::ToCode, input::QuoteVar};
10
11#[derive(Debug)]
12pub(crate) struct Ctx {
13 pub(crate) vars: FxHashMap<VarPos, Vars>,
14}
15
16impl Ctx {
17 pub fn var(&self, ty: VarPos, var_name: &str) -> Option<&VarData> {
18 self.vars.get(&ty)?.get(var_name)
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub enum VarPos {
24 Ident,
25 Expr,
26 Pat,
27 AssignTarget,
28 Str,
29}
30
31#[derive(Debug)]
32pub struct VarData {
33 pos: VarPos,
34 is_counting: bool,
35
36 clone: RefCell<usize>,
39
40 ident: syn::Ident,
41}
42
43impl VarData {
44 pub fn get_expr(&self) -> syn::Expr {
45 if self.is_counting {
46 *self.clone.borrow_mut() += 1;
47 return self.expr_for_var_ref();
48 }
49
50 let use_clone = {
51 let mut b = self.clone.borrow_mut();
52 let val = *b;
53 if val > 0 {
54 *b -= 1;
55 val != 1
56 } else {
57 false
58 }
59 };
60
61 if use_clone {
62 let var_ref_expr = self.expr_for_var_ref();
63
64 parse_quote!(swc_core::quote::ImplicitClone::clone_quote_var(&#var_ref_expr))
65 } else {
66 self.expr_for_var_ref()
67 }
68 }
69
70 fn expr_for_var_ref(&self) -> syn::Expr {
71 syn::Expr::Path(ExprPath {
72 attrs: Default::default(),
73 qself: Default::default(),
74 path: self.ident.clone().into(),
75 })
76 }
77}
78
79pub type Vars = FxHashMap<String, VarData>;
80
81pub(super) fn prepare_vars(
82 src: &dyn ToCode,
83 vars: Punctuated<QuoteVar, Token![,]>,
84) -> (Vec<syn::Stmt>, FxHashMap<VarPos, Vars>) {
85 let mut stmts = Vec::new();
86 let mut init_map = FxHashMap::<_, Vars>::default();
87
88 for var in vars {
89 let value = var.value;
90
91 let ident = var.name.clone();
92 let ident_str = ident.to_string();
93
94 let pos = match var.ty {
95 Some(syn::Type::Path(syn::TypePath {
96 qself: None,
97 path:
98 syn::Path {
99 leading_colon: None,
100 segments,
101 },
102 })) => {
103 let segment = segments.first().unwrap();
104 match segment.ident.to_string().as_str() {
105 "Ident" => VarPos::Ident,
106 "Expr" => VarPos::Expr,
107 "Pat" => VarPos::Pat,
108 "Str" => VarPos::Str,
109 "AssignTarget" => VarPos::AssignTarget,
110 _ => panic!("Invalid type: {:?}", segment.ident),
111 }
112 }
113 None => VarPos::Ident,
114 _ => {
115 panic!(
116 "Var type should be one of: Ident, Expr, Pat; got {:?}",
117 var.ty
118 )
119 }
120 };
121
122 let var_ident = syn::Ident::new(&format!("quote_var_{ident}"), ident.span());
123
124 let old = init_map.entry(pos).or_default().insert(
125 ident_str.clone(),
126 VarData {
127 pos,
128 is_counting: true,
129 clone: Default::default(),
130 ident: var_ident.clone(),
131 },
132 );
133
134 if let Some(old) = old {
135 panic!("Duplicate variable name: {ident_str}");
136 }
137
138 let type_name = Ident::new(
139 match pos {
140 VarPos::Ident => "Ident",
141 VarPos::Expr => "Expr",
142 VarPos::Pat => "Pat",
143 VarPos::AssignTarget => "AssignTarget",
144 VarPos::Str => "Str",
145 },
146 call_site(),
147 );
148 stmts.push(parse_quote! {
149 let #var_ident: swc_core::ecma::ast::#type_name = #value;
150 });
151 }
152
153 let mut cx = Ctx { vars: init_map };
155
156 src.to_code(&cx);
157
158 cx.vars
160 .iter_mut()
161 .for_each(|(k, v)| v.iter_mut().for_each(|(_, v)| v.is_counting = false));
162
163 (stmts, cx.vars)
164}