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 In {
156 left: BumpBox<'a, JsValue<'a>>,
157 right: BumpBox<'a, JsValue<'a>>,
158 ast_path: BumpBox<'a, [AstParentKind]>,
159 span: Span,
160 },
161 ImportedBinding {
163 esm_reference_index: usize,
164 export: Option<RcStr>,
165 ast_path: BumpBox<'a, [AstParentKind]>,
166 span: Span,
167 },
168 FreeVar {
170 var: Atom,
171 ast_path: BumpBox<'a, [AstParentKind]>,
172 span: Span,
173 },
174 TypeOf {
176 arg: BumpBox<'a, JsValue<'a>>,
177 ast_path: BumpBox<'a, [AstParentKind]>,
178 span: Span,
179 },
180 ImportMeta {
183 ast_path: BumpBox<'a, [AstParentKind]>,
184 span: Span,
185 },
186 DynamicImport {
196 args: BumpVec<'a, EffectArg<'a>>,
197 ast_path: BumpBox<'a, [AstParentKind]>,
198 span: Span,
199 in_try: bool,
200 export_usage: ExportUsage,
202 },
203 Unreachable {
205 start_ast_path: BumpBox<'a, [AstParentKind]>,
206 },
207}
208
209impl<'a> Effect<'a> {
210 pub fn normalize(&mut self, arena: &'a Bump) {
212 match self {
213 Effect::Conditional {
214 condition, kind, ..
215 } => {
216 condition.normalize(arena);
217 kind.normalize(arena);
218 }
219 Effect::Call { func, args, .. } => {
220 func.normalize(arena);
221 for arg in args.iter_mut() {
222 arg.normalize(arena);
223 }
224 }
225 Effect::MemberCall {
226 obj, prop, args, ..
227 } => {
228 obj.normalize(arena);
229 prop.normalize(arena);
230 for arg in args.iter_mut() {
231 arg.normalize(arena);
232 }
233 }
234 Effect::Member { obj, prop, .. } => {
235 obj.normalize(arena);
236 prop.normalize(arena);
237 }
238 Effect::In { left, right, .. } => {
239 left.normalize(arena);
240 right.normalize(arena);
241 }
242 Effect::DynamicImport { args, .. } => {
243 for arg in args.iter_mut() {
244 arg.normalize(arena);
245 }
246 }
247 Effect::ImportedBinding { .. } => {}
248 Effect::TypeOf { arg, .. } => {
249 arg.normalize(arena);
250 }
251 Effect::FreeVar { .. } => {}
252 Effect::ImportMeta { .. } => {}
253 Effect::Unreachable { .. } => {}
254 }
255 }
256}
257
258#[derive(Debug)]
259pub enum AssignmentScope {
260 ModuleEval,
262 Function,
264}
265
266#[derive(Debug, Copy, Clone, PartialEq, Eq)]
269pub enum AssignmentScopes {
270 AllInModuleEvalScope,
272 AllInFunctionScopes,
274 Mixed,
276}
277impl AssignmentScopes {
278 pub fn new(initial: AssignmentScope) -> Self {
279 match initial {
280 AssignmentScope::ModuleEval => AssignmentScopes::AllInModuleEvalScope,
281 AssignmentScope::Function => AssignmentScopes::AllInFunctionScopes,
282 }
283 }
284
285 pub fn merge(self, other: AssignmentScope) -> Self {
286 if self == Self::new(other) {
288 self
289 } else {
290 AssignmentScopes::Mixed
291 }
292 }
293}