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 in_truthiness_context: bool,
156 },
157 DestructuredMember {
159 obj: BumpBox<'a, JsValue<'a>>,
160 prop: BumpBox<'a, JsValue<'a>>,
161 span: Span,
162 },
163 In {
165 left: BumpBox<'a, JsValue<'a>>,
166 right: BumpBox<'a, JsValue<'a>>,
167 ast_path: BumpBox<'a, [AstParentKind]>,
168 span: Span,
169 },
170 ImportedBinding {
172 esm_reference_index: usize,
173 export: Option<RcStr>,
174 member: Option<RcStr>,
176 ast_path: BumpBox<'a, [AstParentKind]>,
177 span: Span,
178 },
179 FreeVar {
181 var: Atom,
182 ast_path: BumpBox<'a, [AstParentKind]>,
183 span: Span,
184 },
185 TypeOf {
187 arg: BumpBox<'a, JsValue<'a>>,
188 ast_path: BumpBox<'a, [AstParentKind]>,
189 span: Span,
190 },
191 ImportMeta {
194 ast_path: BumpBox<'a, [AstParentKind]>,
195 span: Span,
196 },
197 DynamicImport {
207 args: BumpVec<'a, EffectArg<'a>>,
208 ast_path: BumpBox<'a, [AstParentKind]>,
209 span: Span,
210 in_try: bool,
211 export_usage: ExportUsage,
213 },
214 Unreachable {
216 start_ast_path: BumpBox<'a, [AstParentKind]>,
217 },
218}
219
220impl<'a> Effect<'a> {
221 pub fn normalize(&mut self, arena: &'a Bump) {
223 match self {
224 Effect::Conditional {
225 condition, kind, ..
226 } => {
227 condition.normalize(arena);
228 kind.normalize(arena);
229 }
230 Effect::Call { func, args, .. } => {
231 func.normalize(arena);
232 for arg in args.iter_mut() {
233 arg.normalize(arena);
234 }
235 }
236 Effect::MemberCall {
237 obj, prop, args, ..
238 } => {
239 obj.normalize(arena);
240 prop.normalize(arena);
241 for arg in args.iter_mut() {
242 arg.normalize(arena);
243 }
244 }
245 Effect::Member { obj, prop, .. } => {
246 obj.normalize(arena);
247 prop.normalize(arena);
248 }
249 Effect::DestructuredMember { obj, prop, .. } => {
250 obj.normalize(arena);
251 prop.normalize(arena);
252 }
253 Effect::In { left, right, .. } => {
254 left.normalize(arena);
255 right.normalize(arena);
256 }
257 Effect::DynamicImport { args, .. } => {
258 for arg in args.iter_mut() {
259 arg.normalize(arena);
260 }
261 }
262 Effect::ImportedBinding { .. } => {}
263 Effect::TypeOf { arg, .. } => {
264 arg.normalize(arena);
265 }
266 Effect::FreeVar { .. } => {}
267 Effect::ImportMeta { .. } => {}
268 Effect::Unreachable { .. } => {}
269 }
270 }
271}
272
273#[derive(Debug)]
274pub enum AssignmentScope {
275 ModuleEval,
277 Function,
279}
280
281#[derive(Debug, Copy, Clone, PartialEq, Eq)]
284pub enum AssignmentScopes {
285 AllInModuleEvalScope,
287 AllInFunctionScopes,
289 Mixed,
291}
292impl AssignmentScopes {
293 pub fn new(initial: AssignmentScope) -> Self {
294 match initial {
295 AssignmentScope::ModuleEval => AssignmentScopes::AllInModuleEvalScope,
296 AssignmentScope::Function => AssignmentScopes::AllInFunctionScopes,
297 }
298 }
299
300 pub fn merge(self, other: AssignmentScope) -> Self {
301 if self == Self::new(other) {
303 self
304 } else {
305 AssignmentScopes::Mixed
306 }
307 }
308}