turbopack_ecmascript/analyzer/graph/
effects.rs1use bumpalo::boxed::Box as BumpBox;
2use swc_core::{atoms::Atom, common::Span, ecma::visit::fields::*};
3use turbo_rcstr::RcStr;
4use turbopack_core::resolve::ExportUsage;
5
6use crate::{
7 analyzer::{Bump, BumpVec, JsValue},
8 utils::AstPathRange,
9};
10
11#[derive(Debug)]
12pub struct EffectsBlock<'a> {
13 pub effects: BumpBox<'a, [Effect<'a>]>,
14 pub range: AstPathRange,
15}
16
17impl EffectsBlock<'_> {
18 pub fn is_empty(&self) -> bool {
19 self.effects.is_empty()
20 }
21}
22
23#[derive(Debug)]
24pub enum ConditionalKind<'a> {
25 If { then: EffectsBlock<'a> },
27 IfElse {
29 then: EffectsBlock<'a>,
30 r#else: EffectsBlock<'a>,
31 },
32 Else { r#else: EffectsBlock<'a> },
34 IfElseMultiple {
37 then: BumpBox<'a, [EffectsBlock<'a>]>,
38 r#else: BumpBox<'a, [EffectsBlock<'a>]>,
39 },
40 Ternary {
42 then: EffectsBlock<'a>,
43 r#else: EffectsBlock<'a>,
44 },
45 And { expr: EffectsBlock<'a> },
47 Or { expr: EffectsBlock<'a> },
49 NullishCoalescing { expr: EffectsBlock<'a> },
51 Labeled { body: EffectsBlock<'a> },
53}
54
55impl<'a> ConditionalKind<'a> {
56 pub fn normalize(&mut self, arena: &'a Bump) {
58 match self {
59 ConditionalKind::If { then: block }
60 | ConditionalKind::Else { r#else: block }
61 | ConditionalKind::And { expr: block, .. }
62 | ConditionalKind::Or { expr: block, .. }
63 | ConditionalKind::NullishCoalescing { expr: block, .. } => {
64 for effect in block.effects.iter_mut() {
65 effect.normalize(arena);
66 }
67 }
68 ConditionalKind::IfElse { then, r#else, .. }
69 | ConditionalKind::Ternary { then, r#else, .. } => {
70 for effect in then.effects.iter_mut() {
71 effect.normalize(arena);
72 }
73 for effect in r#else.effects.iter_mut() {
74 effect.normalize(arena);
75 }
76 }
77 ConditionalKind::IfElseMultiple { then, r#else, .. } => {
78 for block in then.iter_mut().chain(r#else.iter_mut()) {
79 for effect in block.effects.iter_mut() {
80 effect.normalize(arena);
81 }
82 }
83 }
84 ConditionalKind::Labeled { body } => {
85 for effect in body.effects.iter_mut() {
86 effect.normalize(arena);
87 }
88 }
89 }
90 }
91}
92
93#[derive(Debug)]
94pub enum EffectArg<'a> {
95 Value(JsValue<'a>),
96 Closure(JsValue<'a>, BumpBox<'a, EffectsBlock<'a>>),
97 Spread,
98}
99
100impl<'a> EffectArg<'a> {
101 pub fn normalize(&mut self, arena: &'a Bump) {
103 match self {
104 EffectArg::Value(value) => value.normalize(arena),
105 EffectArg::Closure(value, effects) => {
106 value.normalize(arena);
107 for effect in effects.effects.iter_mut() {
108 effect.normalize(arena);
109 }
110 }
111 EffectArg::Spread => {}
112 }
113 }
114}
115
116#[derive(Debug)]
117pub enum Effect<'a> {
118 Conditional {
122 condition: BumpBox<'a, JsValue<'a>>,
123 kind: BumpBox<'a, ConditionalKind<'a>>,
124 ast_path: BumpBox<'a, [AstParentKind]>,
126 span: Span,
127 },
128 Call {
130 func: BumpBox<'a, JsValue<'a>>,
131 args: BumpVec<'a, EffectArg<'a>>,
132 ast_path: BumpBox<'a, [AstParentKind]>,
133 span: Span,
134 in_try: bool,
135 new: bool,
136 },
137 MemberCall {
139 obj: BumpBox<'a, JsValue<'a>>,
140 prop: BumpBox<'a, JsValue<'a>>,
141 args: BumpVec<'a, EffectArg<'a>>,
142 ast_path: BumpBox<'a, [AstParentKind]>,
143 span: Span,
144 in_try: bool,
145 new: bool,
146 },
147 Member {
149 obj: BumpBox<'a, JsValue<'a>>,
150 prop: BumpBox<'a, JsValue<'a>>,
151 ast_path: BumpBox<'a, [AstParentKind]>,
152 span: Span,
153 },
154 DestructuredMember {
156 obj: BumpBox<'a, JsValue<'a>>,
157 prop: BumpBox<'a, JsValue<'a>>,
158 span: Span,
159 },
160 In {
162 left: BumpBox<'a, JsValue<'a>>,
163 right: BumpBox<'a, JsValue<'a>>,
164 ast_path: BumpBox<'a, [AstParentKind]>,
165 span: Span,
166 },
167 ImportedBinding {
169 esm_reference_index: usize,
170 export: Option<RcStr>,
171 ast_path: BumpBox<'a, [AstParentKind]>,
172 span: Span,
173 },
174 FreeVar {
176 var: Atom,
177 ast_path: BumpBox<'a, [AstParentKind]>,
178 span: Span,
179 },
180 TypeOf {
182 arg: BumpBox<'a, JsValue<'a>>,
183 ast_path: BumpBox<'a, [AstParentKind]>,
184 span: Span,
185 },
186 ImportMeta {
189 ast_path: BumpBox<'a, [AstParentKind]>,
190 span: Span,
191 },
192 DynamicImport {
202 args: BumpVec<'a, EffectArg<'a>>,
203 ast_path: BumpBox<'a, [AstParentKind]>,
204 span: Span,
205 in_try: bool,
206 export_usage: ExportUsage,
208 },
209 Unreachable {
211 start_ast_path: BumpBox<'a, [AstParentKind]>,
212 },
213}
214
215impl<'a> Effect<'a> {
216 pub fn normalize(&mut self, arena: &'a Bump) {
218 match self {
219 Effect::Conditional {
220 condition, kind, ..
221 } => {
222 condition.normalize(arena);
223 kind.normalize(arena);
224 }
225 Effect::Call { func, args, .. } => {
226 func.normalize(arena);
227 for arg in args.iter_mut() {
228 arg.normalize(arena);
229 }
230 }
231 Effect::MemberCall {
232 obj, prop, args, ..
233 } => {
234 obj.normalize(arena);
235 prop.normalize(arena);
236 for arg in args.iter_mut() {
237 arg.normalize(arena);
238 }
239 }
240 Effect::Member { obj, prop, .. } => {
241 obj.normalize(arena);
242 prop.normalize(arena);
243 }
244 Effect::DestructuredMember { obj, prop, .. } => {
245 obj.normalize(arena);
246 prop.normalize(arena);
247 }
248 Effect::In { left, right, .. } => {
249 left.normalize(arena);
250 right.normalize(arena);
251 }
252 Effect::DynamicImport { args, .. } => {
253 for arg in args.iter_mut() {
254 arg.normalize(arena);
255 }
256 }
257 Effect::ImportedBinding { .. } => {}
258 Effect::TypeOf { arg, .. } => {
259 arg.normalize(arena);
260 }
261 Effect::FreeVar { .. } => {}
262 Effect::ImportMeta { .. } => {}
263 Effect::Unreachable { .. } => {}
264 }
265 }
266}
267
268#[derive(Debug)]
269pub enum AssignmentScope {
270 ModuleEval,
272 Function,
274}
275
276#[derive(Debug, Copy, Clone, PartialEq, Eq)]
279pub enum AssignmentScopes {
280 AllInModuleEvalScope,
282 AllInFunctionScopes,
284 Mixed,
286}
287impl AssignmentScopes {
288 pub fn new(initial: AssignmentScope) -> Self {
289 match initial {
290 AssignmentScope::ModuleEval => AssignmentScopes::AllInModuleEvalScope,
291 AssignmentScope::Function => AssignmentScopes::AllInFunctionScopes,
292 }
293 }
294
295 pub fn merge(self, other: AssignmentScope) -> Self {
296 if self == Self::new(other) {
298 self
299 } else {
300 AssignmentScopes::Mixed
301 }
302 }
303}