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