1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4 base::SwcComments,
5 ecma::{
6 ast::{
7 BlockStmt, CallExpr, Expr, Lit, MemberExpr, ModuleDecl, ModuleItem, Pat, Program, Prop,
8 SimpleAssignTarget, Stmt, Str, SwitchCase,
9 },
10 visit::AstParentKind,
11 },
12};
13use turbo_rcstr::RcStr;
14use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, debug::ValueDebugFormat, trace::TraceRawVcs};
15use turbopack_core::{chunk::ChunkingContext, reference::ModuleReference};
16
17use crate::{
18 ScopeHoistingContext,
19 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
20 references::{
21 AstPath,
22 amd::AmdDefineWithDependenciesCodeGen,
23 cjs::{
24 CjsRequireAssetReferenceCodeGen, CjsRequireCacheAccess,
25 CjsRequireResolveAssetReferenceCodeGen,
26 },
27 constant_condition::ConstantConditionCodeGen,
28 constant_value::ConstantValueCodeGen,
29 dynamic_expression::DynamicExpression,
30 esm::{
31 EsmBinding, EsmModuleItem, ImportMetaBinding, ImportMetaRef,
32 dynamic::EsmAsyncAssetReferenceCodeGen, module_id::EsmModuleIdAssetReferenceCodeGen,
33 url::UrlAssetReferenceCodeGen,
34 },
35 exports_info::{ExportsInfoBinding, ExportsInfoRef},
36 ident::IdentReplacement,
37 member::MemberReplacement,
38 require_context::RequireContextAssetReferenceCodeGen,
39 unreachable::Unreachable,
40 worker::WorkerAssetReferenceCodeGen,
41 },
42};
43
44#[derive(Default)]
45pub struct CodeGeneration {
46 pub visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>,
48 pub hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
49 pub early_hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
50 pub late_stmts: Vec<CodeGenerationHoistedStmt>,
51 pub early_late_stmts: Vec<CodeGenerationHoistedStmt>,
52 pub comments: Option<SwcComments>,
53}
54
55impl CodeGeneration {
56 pub fn empty() -> Self {
57 CodeGeneration {
58 ..Default::default()
59 }
60 }
61
62 pub fn new(
63 visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>,
64 hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
65 early_hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
66 late_stmts: Vec<CodeGenerationHoistedStmt>,
67 early_late_stmts: Vec<CodeGenerationHoistedStmt>,
68 ) -> Self {
69 CodeGeneration {
70 visitors,
71 hoisted_stmts,
72 early_hoisted_stmts,
73 late_stmts,
74 early_late_stmts,
75 ..Default::default()
76 }
77 }
78
79 pub fn visitors_with_comments(
80 visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>,
81 comments: SwcComments,
82 ) -> Self {
83 CodeGeneration {
84 visitors,
85 comments: Some(comments),
86 ..Default::default()
87 }
88 }
89
90 pub fn visitors(visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>) -> Self {
91 CodeGeneration {
92 visitors,
93 ..Default::default()
94 }
95 }
96
97 pub fn hoisted_stmt(key: RcStr, stmt: Stmt) -> Self {
98 CodeGeneration {
99 hoisted_stmts: vec![CodeGenerationHoistedStmt::new(key, stmt)],
100 ..Default::default()
101 }
102 }
103
104 pub fn hoisted_stmts(hoisted_stmts: Vec<CodeGenerationHoistedStmt>) -> Self {
105 CodeGeneration {
106 hoisted_stmts,
107 ..Default::default()
108 }
109 }
110}
111
112#[derive(Clone)]
113pub struct CodeGenerationHoistedStmt {
114 pub key: RcStr,
115 pub stmt: Stmt,
116}
117
118impl CodeGenerationHoistedStmt {
119 pub fn new(key: RcStr, stmt: Stmt) -> Self {
120 CodeGenerationHoistedStmt { key, stmt }
121 }
122}
123
124macro_rules! method {
125 ($name:ident, $T:ty) => {
126 fn $name(&self, _node: &mut $T) {}
127 };
128}
129
130pub trait AstModifier: Send + Sync {
131 method!(visit_mut_prop, Prop);
132 method!(visit_mut_simple_assign_target, SimpleAssignTarget);
133 method!(visit_mut_expr, Expr);
134 method!(visit_mut_member_expr, MemberExpr);
135 method!(visit_mut_pat, Pat);
136 method!(visit_mut_stmt, Stmt);
137 method!(visit_mut_module_decl, ModuleDecl);
138 method!(visit_mut_module_item, ModuleItem);
139 method!(visit_mut_call_expr, CallExpr);
140 method!(visit_mut_lit, Lit);
141 method!(visit_mut_str, Str);
142 method!(visit_mut_block_stmt, BlockStmt);
143 method!(visit_mut_switch_case, SwitchCase);
144 method!(visit_mut_program, Program);
145}
146
147pub trait ModifiableAst {
148 fn modify(&mut self, modifier: &dyn AstModifier);
149}
150
151macro_rules! impl_modify {
152 ($visit_mut_name:ident, $T:ty) => {
153 impl ModifiableAst for $T {
154 fn modify(&mut self, modifier: &dyn AstModifier) {
155 modifier.$visit_mut_name(self)
156 }
157 }
158 };
159}
160
161impl_modify!(visit_mut_prop, Prop);
162impl_modify!(visit_mut_simple_assign_target, SimpleAssignTarget);
163impl_modify!(visit_mut_expr, Expr);
164impl_modify!(visit_mut_member_expr, MemberExpr);
165impl_modify!(visit_mut_pat, Pat);
166impl_modify!(visit_mut_stmt, Stmt);
167impl_modify!(visit_mut_module_decl, ModuleDecl);
168impl_modify!(visit_mut_module_item, ModuleItem);
169impl_modify!(visit_mut_call_expr, CallExpr);
170impl_modify!(visit_mut_lit, Lit);
171impl_modify!(visit_mut_str, Str);
172impl_modify!(visit_mut_block_stmt, BlockStmt);
173impl_modify!(visit_mut_switch_case, SwitchCase);
174impl_modify!(visit_mut_program, Program);
175
176#[derive(
177 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
178)]
179pub enum CodeGen {
180 AmdDefineWithDependenciesCodeGen(Box<AmdDefineWithDependenciesCodeGen>),
182 CjsRequireCacheAccess(CjsRequireCacheAccess),
183 ConstantConditionCodeGen(ConstantConditionCodeGen),
184 ConstantValueCodeGen(ConstantValueCodeGen),
185 DynamicExpression(DynamicExpression),
186 EsmBinding(EsmBinding),
187 EsmModuleItem(EsmModuleItem),
188 ExportsInfoBinding(ExportsInfoBinding),
189 ExportsInfoRef(ExportsInfoRef),
190 IdentReplacement(IdentReplacement),
191 ImportMetaBinding(ImportMetaBinding),
192 ImportMetaRef(ImportMetaRef),
193 MemberReplacement(MemberReplacement),
194 Unreachable(Unreachable),
195 CjsRequireAssetReferenceCodeGen(CjsRequireAssetReferenceCodeGen),
196 CjsRequireResolveAssetReferenceCodeGen(CjsRequireResolveAssetReferenceCodeGen),
197 EsmAsyncAssetReferenceCodeGen(EsmAsyncAssetReferenceCodeGen),
198 EsmModuleIdAssetReferenceCodeGen(EsmModuleIdAssetReferenceCodeGen),
199 RequireContextAssetReferenceCodeGen(RequireContextAssetReferenceCodeGen),
200 UrlAssetReferenceCodeGen(UrlAssetReferenceCodeGen),
201 WorkerAssetReferenceCodeGen(WorkerAssetReferenceCodeGen),
202}
203
204impl CodeGen {
205 pub async fn code_generation(
206 &self,
207 ctx: Vc<Box<dyn ChunkingContext>>,
208 scope_hoisting_context: ScopeHoistingContext<'_>,
209 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
210 exports: ResolvedVc<EcmascriptExports>,
211 ) -> Result<CodeGeneration> {
212 match self {
213 Self::AmdDefineWithDependenciesCodeGen(v) => v.code_generation(ctx).await,
214 Self::CjsRequireCacheAccess(v) => v.code_generation(ctx).await,
215 Self::ConstantConditionCodeGen(v) => v.code_generation(ctx).await,
216 Self::ConstantValueCodeGen(v) => v.code_generation(ctx).await,
217 Self::DynamicExpression(v) => v.code_generation(ctx).await,
218 Self::EsmBinding(v) => v.code_generation(ctx, scope_hoisting_context).await,
219 Self::EsmModuleItem(v) => v.code_generation(ctx).await,
220 Self::ExportsInfoBinding(v) => v.code_generation(ctx, module, exports).await,
221 Self::ExportsInfoRef(v) => v.code_generation(ctx).await,
222 Self::IdentReplacement(v) => v.code_generation(ctx).await,
223 Self::ImportMetaBinding(v) => v.code_generation(ctx).await,
224 Self::ImportMetaRef(v) => v.code_generation(ctx).await,
225 Self::MemberReplacement(v) => v.code_generation(ctx).await,
226 Self::Unreachable(v) => v.code_generation(ctx).await,
227 Self::CjsRequireAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
228 Self::CjsRequireResolveAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
229 Self::EsmAsyncAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
230 Self::EsmModuleIdAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
231 Self::RequireContextAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
232 Self::UrlAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
233 Self::WorkerAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
234 }
235 }
236}
237
238#[turbo_tasks::value(transparent)]
239pub struct CodeGens(Vec<CodeGen>);
240
241#[turbo_tasks::value_impl]
242impl CodeGens {
243 #[turbo_tasks::function]
244 pub fn empty() -> Vc<Self> {
245 Vc::cell(Vec::new())
246 }
247}
248
249pub trait IntoCodeGenReference {
250 fn into_code_gen_reference(
251 self,
252 path: AstPath,
253 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen);
254}
255
256pub fn path_to(
257 path: &[AstParentKind],
258 f: impl FnMut(&AstParentKind) -> bool,
259) -> Vec<AstParentKind> {
260 if let Some(pos) = path.iter().rev().position(f) {
261 let index = path.len() - pos - 1;
262 path[..index].to_vec()
263 } else {
264 path.to_vec()
265 }
266}
267
268#[macro_export]
278macro_rules! create_visitor {
279 (exact, $ast_path:expr, $name:ident, |$arg:ident: &mut $ty:ident| $b:block) => {
280 $crate::create_visitor!(__ $ast_path.to_vec(), $name, |$arg: &mut $ty| $b)
281 };
282 ($ast_path:expr, $name:ident, |$arg:ident: &mut $ty:ident| $b:block) => {
283 $crate::create_visitor!(__ $crate::code_gen::path_to(&$ast_path, |n| {
284 matches!(n, swc_core::ecma::visit::AstParentKind::$ty(_))
285 }), $name, |$arg: &mut $ty| $b)
286 };
287 (__ $ast_path:expr, $name:ident, |$arg:ident: &mut $ty:ident| $b:block) => {{
288 struct Visitor<T: Fn(&mut swc_core::ecma::ast::$ty) + Send + Sync> {
289 $name: T,
290 }
291
292 impl<T: Fn(&mut swc_core::ecma::ast::$ty) + Send + Sync> $crate::code_gen::AstModifier
293 for Visitor<T>
294 {
295 fn $name(&self, $arg: &mut swc_core::ecma::ast::$ty) {
296 (self.$name)($arg);
297 }
298 }
299
300 {
301 #[cfg(debug_assertions)]
302 if $ast_path.is_empty() {
303 unreachable!("if the path is empty, the visitor should be a root visitor");
304 }
305
306 (
307 $ast_path,
308 Box::new(Visitor {
309 $name: move |$arg: &mut swc_core::ecma::ast::$ty| $b,
310 }) as Box<dyn $crate::code_gen::AstModifier>,
311 )
312 }
313 }};
314}