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