Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c092e0b

Browse files
authored
feat(rust): collect circular dependencies while sorting modules (#830)
1 parent f5aab3a commit c092e0b

1 file changed

Lines changed: 60 additions & 20 deletions

File tree

crates/rolldown/src/stages/link_stage/sort_modules.rs

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,105 @@
1+
use std::iter;
2+
13
use rolldown_common::ModuleId;
2-
use rustc_hash::FxHashSet;
4+
use rustc_hash::{FxHashMap, FxHashSet};
35

46
use super::LinkStage;
57

6-
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
7-
enum Action {
8-
Enter(ModuleId),
9-
Exit(ModuleId),
8+
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
9+
enum Status {
10+
ToBeExecuted(ModuleId),
11+
WaitForExit(ModuleId),
1012
}
1113

1214
impl<'a> LinkStage<'a> {
1315
pub fn sort_modules(&mut self) {
14-
let mut stack = self
16+
// The runtime module should always be the first module to be executed
17+
let mut execution_stack = self
1518
.entries
1619
.iter()
17-
.map(|entry_point| Action::Enter(entry_point.id.into()))
1820
.rev()
21+
.map(|entry| Status::ToBeExecuted(entry.id.into()))
22+
.chain(iter::once(Status::ToBeExecuted(self.runtime.id().into())))
1923
.collect::<Vec<_>>();
20-
// The runtime module should always be the first module to be executed
21-
stack.push(Action::Enter(self.runtime.id().into()));
22-
let mut entered_ids = FxHashSet::default();
23-
entered_ids
24+
25+
let mut stack_indexes_of_executing_id = FxHashMap::default();
26+
let mut executed_ids = FxHashSet::default();
27+
executed_ids
2428
.shrink_to(self.module_table.normal_modules.len() + self.module_table.external_modules.len());
29+
2530
let mut sorted_modules = Vec::with_capacity(self.module_table.normal_modules.len());
2631
let mut next_exec_order = 0;
27-
while let Some(action) = stack.pop() {
28-
match action {
29-
Action::Enter(id) => {
30-
if !entered_ids.contains(&id) {
31-
entered_ids.insert(id);
32-
stack.push(Action::Exit(id));
32+
let mut circular_dependencies = FxHashSet::default();
33+
while let Some(status) = execution_stack.pop() {
34+
match status {
35+
Status::ToBeExecuted(id) => {
36+
if executed_ids.contains(&id) {
37+
if let Some(index) = stack_indexes_of_executing_id.get(&id).copied() {
38+
// Executing
39+
let cycles = execution_stack[index..]
40+
.iter()
41+
.filter_map(|action| match action {
42+
// Only modules with `Status::WaitForExit` are on the execution chain
43+
Status::ToBeExecuted(_) => None,
44+
Status::WaitForExit(id) => Some(*id),
45+
})
46+
.chain(iter::once(id))
47+
.collect::<Box<[_]>>();
48+
circular_dependencies.insert(cycles);
49+
} else {
50+
// It's already executed in other import chain, no need to execute again
51+
}
52+
} else {
53+
executed_ids.insert(id);
54+
execution_stack.push(Status::WaitForExit(id));
55+
debug_assert!(
56+
!stack_indexes_of_executing_id.contains_key(&id),
57+
"A module should not be executing the same module twice"
58+
);
59+
stack_indexes_of_executing_id.insert(id, execution_stack.len() - 1);
60+
3361
if let ModuleId::Normal(module_id) = id {
3462
let module = &self.module_table.normal_modules[module_id];
35-
stack.extend(
63+
execution_stack.extend(
3664
module
3765
.import_records
3866
.iter()
3967
.filter(|rec| rec.kind.is_static())
4068
.map(|rec| rec.resolved_module)
4169
.rev()
42-
.map(Action::Enter),
70+
.map(Status::ToBeExecuted),
4371
);
4472
}
4573
}
4674
}
47-
Action::Exit(id) => {
75+
Status::WaitForExit(id) => {
76+
executed_ids.insert(id);
4877
match id {
4978
ModuleId::Normal(id) => {
5079
let module = &mut self.module_table.normal_modules[id];
80+
debug_assert!(module.exec_order == u32::MAX);
5181
module.exec_order = next_exec_order;
5282
sorted_modules.push(id);
5383
}
5484
ModuleId::External(id) => {
5585
let module = &mut self.module_table.external_modules[id];
86+
debug_assert!(module.exec_order == u32::MAX);
5687
module.exec_order = next_exec_order;
5788
}
5889
}
5990
next_exec_order += 1;
91+
debug_assert!(stack_indexes_of_executing_id.contains_key(&id));
92+
stack_indexes_of_executing_id.remove(&id);
6093
}
6194
}
6295
}
96+
97+
if !circular_dependencies.is_empty() {
98+
let mut cycles = circular_dependencies.into_iter().collect::<Vec<_>>();
99+
cycles.sort();
100+
// TODO: emit warning
101+
}
102+
63103
self.sorted_modules = sorted_modules;
64104
debug_assert_eq!(
65105
self.sorted_modules.first().copied(),

0 commit comments

Comments
 (0)