Skip to content

Commit 6882d82

Browse files
authored
refactor/fix: exhaustive logic (#45)
1 parent dbeb60d commit 6882d82

File tree

5 files changed

+88
-71
lines changed

5 files changed

+88
-71
lines changed

crates/tree_shaker/src/builtins/react/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ fn create_react_context_provider_impl<'a>(
9898
let dep = data.dep;
9999

100100
let should_consume =
101-
analyzer.request_exhaustive_callbacks(true, ExhaustiveDepId::Object(object_id));
101+
analyzer.request_exhaustive_callbacks(ExhaustiveDepId::Object(object_id));
102102

103103
if should_consume {
104104
analyzer.consume(context_id);

crates/tree_shaker/src/scope/cf_scope.rs

+16-24
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,14 @@ use crate::{
55
};
66
use oxc::{ast::ast::LabeledStatement, span::Atom};
77
use oxc_index::define_index_type;
8-
use rustc_hash::FxHashSet;
98
use std::mem;
109

11-
use super::exhaustive::ExhaustiveDepId;
10+
use super::exhaustive::{ExhaustiveData, ExhaustiveDepId};
1211

1312
define_index_type! {
1413
pub struct CfScopeId = u32;
1514
}
1615

17-
#[derive(Debug, Default)]
18-
pub struct ExhaustiveData {
19-
pub clean: bool,
20-
pub deps: FxHashSet<ExhaustiveDepId>,
21-
}
22-
2316
#[derive(Debug)]
2417
pub enum CfScopeKind<'a> {
2518
Root,
@@ -117,33 +110,32 @@ impl<'a> CfScope<'a> {
117110
}
118111
}
119112

120-
pub fn mark_exhaustive_read(&mut self, id: ExhaustiveDepId) {
121-
if let Some(data) = self.exhaustive_data_mut() {
122-
if data.clean {
123-
data.deps.insert(id);
124-
}
125-
}
126-
}
127-
128113
pub fn mark_exhaustive_write(&mut self, id: ExhaustiveDepId) -> bool {
129114
if let Some(data) = self.exhaustive_data_mut() {
130-
if data.clean && data.deps.contains(&id) {
131-
data.clean = false;
115+
if data.clean {
116+
if let Some(temp_deps) = &data.temp_deps {
117+
if temp_deps.contains(&id) {
118+
data.clean = false;
119+
}
120+
}
132121
}
133122
true
134123
} else {
135124
false
136125
}
137126
}
138127

139-
pub fn iterate_exhaustively(&mut self) -> bool {
128+
pub fn post_exhaustive_iterate(&mut self) -> bool {
140129
let exited = self.must_exited();
141130
let data = self.exhaustive_data_mut().unwrap();
142-
let clean = data.clean;
143-
data.clean = true;
144-
if !clean && !exited {
145-
data.deps.clear();
146-
true
131+
if !data.clean && !exited {
132+
if let Some(temp_deps) = &mut data.temp_deps {
133+
temp_deps.clear();
134+
data.clean = true;
135+
true
136+
} else {
137+
false
138+
}
147139
} else {
148140
false
149141
}

crates/tree_shaker/src/scope/exhaustive.rs

+66-41
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@ pub enum ExhaustiveDepId {
1919
ObjectProperty(ObjectId, ObjectPropertyId),
2020
}
2121

22+
#[derive(Debug)]
23+
pub struct ExhaustiveData {
24+
pub clean: bool,
25+
pub temp_deps: Option<FxHashSet<ExhaustiveDepId>>,
26+
pub register_deps: Option<FxHashSet<ExhaustiveDepId>>,
27+
}
28+
2229
#[derive(Clone)]
2330
pub struct ExhaustiveCallback<'a> {
2431
pub handler: Rc<dyn Fn(&mut Analyzer<'a>) + 'a>,
25-
pub once: bool,
32+
pub drain: bool,
2633
}
2734
impl<'a> PartialEq for ExhaustiveCallback<'a> {
2835
fn eq(&self, other: &Self) -> bool {
29-
self.once == other.once && Rc::ptr_eq(&self.handler, &other.handler)
36+
self.drain == other.drain && Rc::ptr_eq(&self.handler, &other.handler)
3037
}
3138
}
3239
impl<'a> Eq for ExhaustiveCallback<'a> {}
@@ -40,7 +47,7 @@ impl<'a> Analyzer<'a> {
4047
pub fn exec_loop(&mut self, runner: impl Fn(&mut Analyzer<'a>) + 'a) {
4148
let runner = Rc::new(runner);
4249

43-
self.exec_exhaustively("loop", runner.clone(), false);
50+
self.exec_exhaustively("loop", true, false, runner.clone());
4451

4552
let cf_scope = self.cf_scope();
4653
if cf_scope.referred_state != ReferredState::ReferredClean && cf_scope.deps.may_not_referred() {
@@ -66,51 +73,55 @@ impl<'a> Analyzer<'a> {
6673
}
6774
analyzer.pop_cf_scope();
6875
});
69-
let deps = self.exec_exhaustively(kind, runner.clone(), false);
70-
self.register_exhaustive_callbacks(false, runner, deps);
76+
self.exec_exhaustively(kind, true, true, runner);
7177
}
7278

7379
pub fn exec_async_or_generator_fn(&mut self, runner: impl Fn(&mut Analyzer<'a>) + 'a) {
74-
let runner = Rc::new(runner);
75-
let deps = self.exec_exhaustively("async/generator", runner.clone(), true);
76-
self.register_exhaustive_callbacks(true, runner, deps);
80+
self.exec_exhaustively("async/generator", false, true, Rc::new(runner));
7781
}
7882

7983
fn exec_exhaustively(
8084
&mut self,
8185
_kind: &str,
86+
drain: bool,
87+
register: bool,
8288
runner: Rc<dyn Fn(&mut Analyzer<'a>) + 'a>,
83-
once: bool,
84-
) -> FxHashSet<ExhaustiveDepId> {
85-
self.push_cf_scope(CfScopeKind::Exhaustive(Default::default()), Some(false));
89+
) {
90+
self.push_cf_scope(
91+
CfScopeKind::Exhaustive(ExhaustiveData {
92+
clean: true,
93+
temp_deps: drain.then(FxHashSet::default),
94+
register_deps: register.then(Default::default),
95+
}),
96+
Some(false),
97+
);
8698
let mut round_counter = 0;
87-
while self.cf_scope_mut().iterate_exhaustively() {
99+
loop {
88100
#[cfg(feature = "flame")]
89101
let _scope_guard = flame::start_guard(format!(
90102
"!{_kind}@{:06X} x{}",
91103
(Rc::as_ptr(&runner) as *const () as usize) & 0xFFFFFF,
92104
round_counter
93105
));
94-
95106
runner(self);
96107
round_counter += 1;
97-
if once {
98-
let data = self.cf_scope_mut().exhaustive_data_mut().unwrap();
99-
data.clean = true;
100-
break;
101-
}
102108
if round_counter > 1000 {
103109
unreachable!("Exhaustive loop is too deep");
104110
}
111+
if !self.cf_scope_mut().post_exhaustive_iterate() {
112+
break;
113+
}
105114
}
106115
let id = self.pop_cf_scope();
107116
let data = self.scoping.cf.get_mut(id).exhaustive_data_mut().unwrap();
108-
mem::take(&mut data.deps)
117+
if let Some(register_deps) = data.register_deps.take() {
118+
self.register_exhaustive_callbacks(drain, runner, register_deps);
119+
}
109120
}
110121

111122
fn register_exhaustive_callbacks(
112123
&mut self,
113-
once: bool,
124+
drain: bool,
114125
handler: Rc<dyn Fn(&mut Analyzer<'a>) + 'a>,
115126
deps: FxHashSet<ExhaustiveDepId>,
116127
) {
@@ -119,43 +130,58 @@ impl<'a> Analyzer<'a> {
119130
.exhaustive_callbacks
120131
.entry(id)
121132
.or_default()
122-
.insert(ExhaustiveCallback { handler: handler.clone(), once });
133+
.insert(ExhaustiveCallback { handler: handler.clone(), drain });
123134
}
124135
}
125136

126137
pub fn mark_exhaustive_read(&mut self, id: ExhaustiveDepId, target: usize) {
127-
for depth in target..self.scoping.cf.stack.len() {
128-
self.scoping.cf.get_mut_from_depth(depth).mark_exhaustive_read(id);
138+
let mut registered = false;
139+
for depth in (target..self.scoping.cf.stack.len()).rev() {
140+
let scope = self.scoping.cf.get_mut_from_depth(depth);
141+
if let Some(data) = scope.exhaustive_data_mut() {
142+
if data.clean {
143+
if let Some(temp_deps) = data.temp_deps.as_mut() {
144+
temp_deps.insert(id);
145+
}
146+
}
147+
if !registered {
148+
if let Some(register_deps) = data.register_deps.as_mut() {
149+
registered = true;
150+
register_deps.insert(id);
151+
}
152+
}
153+
}
129154
}
130155
}
131156

132157
pub fn mark_exhaustive_write(&mut self, id: ExhaustiveDepId, target: usize) -> (bool, bool) {
133-
let mut should_consume = false;
158+
let mut exhaustive = false;
134159
let mut indeterminate = false;
160+
let mut need_mark = true;
135161
for depth in target..self.scoping.cf.stack.len() {
136162
let scope = self.scoping.cf.get_mut_from_depth(depth);
137-
if !should_consume {
138-
should_consume |= scope.mark_exhaustive_write(id);
139-
}
140163
indeterminate |= scope.is_indeterminate();
164+
if let Some(data) = scope.exhaustive_data_mut() {
165+
exhaustive = true;
166+
if (need_mark || data.register_deps.is_some()) && data.clean {
167+
if let Some(temp_deps) = &data.temp_deps {
168+
if temp_deps.contains(&id) {
169+
data.clean = false;
170+
}
171+
need_mark = false;
172+
}
173+
}
174+
}
141175
}
142-
(should_consume, indeterminate)
176+
(exhaustive, indeterminate)
143177
}
144178

145-
pub fn request_exhaustive_callbacks(
146-
&mut self,
147-
should_consume: bool,
148-
id: ExhaustiveDepId,
149-
) -> bool {
179+
pub fn request_exhaustive_callbacks(&mut self, id: ExhaustiveDepId) -> bool {
150180
if let Some(runners) = self.exhaustive_callbacks.get_mut(&id) {
151181
if runners.is_empty() {
152182
false
153183
} else {
154-
if should_consume {
155-
self.pending_deps.extend(runners.drain());
156-
} else {
157-
self.pending_deps.extend(runners.iter().cloned());
158-
}
184+
self.pending_deps.extend(runners.drain());
159185
true
160186
}
161187
} else {
@@ -171,9 +197,8 @@ impl<'a> Analyzer<'a> {
171197
let runners = mem::take(&mut self.pending_deps);
172198
for runner in runners {
173199
// let old_count = self.referred_deps.debug_count();
174-
let ExhaustiveCallback { handler: runner, once } = runner;
175-
let deps = self.exec_exhaustively("dep", runner.clone(), once);
176-
self.register_exhaustive_callbacks(once, runner, deps);
200+
let ExhaustiveCallback { handler: runner, drain } = runner;
201+
self.exec_exhaustively("dep", drain, true, runner.clone());
177202
// let new_count = self.referred_deps.debug_count();
178203
// self.debug += 1;
179204
}

crates/tree_shaker/src/scope/utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl<'a> Analyzer<'a> {
3030
}
3131
}
3232

33-
self.request_exhaustive_callbacks(true, ExhaustiveDepId::Object(object_id));
33+
self.request_exhaustive_callbacks(ExhaustiveDepId::Object(object_id));
3434

3535
(has_exhaustive, indeterminate, exec_deps)
3636
}
@@ -50,6 +50,6 @@ impl<'a> Analyzer<'a> {
5050
}
5151
mem::take(&mut scope.deps).consume_all(self);
5252
}
53-
self.request_exhaustive_callbacks(true, ExhaustiveDepId::Object(object_id));
53+
self.request_exhaustive_callbacks(ExhaustiveDepId::Object(object_id));
5454
}
5555
}

crates/tree_shaker/src/scope/variable_scope.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl<'a> Analyzer<'a> {
103103
}));
104104
self.scoping.variable.get_mut(id).variables.insert(symbol, variable);
105105
if has_fn_value {
106-
self.request_exhaustive_callbacks(false, ExhaustiveDepId::Variable(id, symbol));
106+
self.request_exhaustive_callbacks(ExhaustiveDepId::Variable(id, symbol));
107107
}
108108
}
109109
}
@@ -130,7 +130,7 @@ impl<'a> Analyzer<'a> {
130130
} else {
131131
variable.value =
132132
Some(self.factory.computed(value.unwrap_or(self.factory.undefined), init_node));
133-
self.request_exhaustive_callbacks(false, ExhaustiveDepId::Variable(id, symbol));
133+
self.request_exhaustive_callbacks(ExhaustiveDepId::Variable(id, symbol));
134134
}
135135
}
136136

@@ -226,7 +226,7 @@ impl<'a> Analyzer<'a> {
226226
};
227227
drop(variable_ref);
228228

229-
self.request_exhaustive_callbacks(should_consume, ExhaustiveDepId::Variable(id, symbol));
229+
self.request_exhaustive_callbacks(ExhaustiveDepId::Variable(id, symbol));
230230
}
231231
}
232232
true

0 commit comments

Comments
 (0)