luaur_analysis/methods/
tarjan_loop.rs1use crate::enums::tarjan_result::TarjanResult;
2use crate::records::tarjan::Tarjan;
3use luaur_common::macros::luau_assert::LUAU_ASSERT;
4
5impl Tarjan {
6 pub fn loop_item(&mut self) -> TarjanResult {
7 while !self.worklist.is_empty() {
8 let (index, mut curr_edge, mut last_edge) = {
9 let top = self.worklist.last().unwrap();
10 (top.index, top.curr_edge, top.last_edge)
11 };
12
13 if curr_edge == -1 {
15 self.child_count += 1;
16 if self.child_limit > 0 && self.child_limit <= self.child_count {
17 return TarjanResult::TooManyChildren;
18 }
19
20 self.stack.push(index);
21
22 self.nodes[index as usize].on_stack = true;
23
24 curr_edge = self.edges_ty.len() as i32;
25
26 let ty = self.nodes[index as usize].ty;
28 if !ty.is_null() {
29 self.visit_children_type_id_i32(ty, index);
30 } else {
31 let tp = self.nodes[index as usize].tp;
32 if !tp.is_null() {
33 self.visit_children_type_pack_id_i32(tp, index);
34 }
35 }
36
37 last_edge = self.edges_ty.len() as i32;
38
39 if let Some(top) = self.worklist.last_mut() {
41 top.curr_edge = curr_edge;
42 top.last_edge = last_edge;
43 }
44 }
45
46 let mut found_fresh = false;
48 while curr_edge < last_edge {
49 let mut child_index: i32 = -1;
50 let mut fresh = false;
51
52 let edge_ty = self.edges_ty[curr_edge as usize];
53 if !edge_ty.is_null() {
54 let (ci, fr) = self.indexify_type_id(edge_ty);
55 child_index = ci;
56 fresh = fr;
57 } else {
58 let edge_tp = self.edges_tp[curr_edge as usize];
59 if !edge_tp.is_null() {
60 let (ci, fr) = self.indexify_type_pack_id(edge_tp);
61 child_index = ci;
62 fresh = fr;
63 } else {
64 LUAU_ASSERT!(false);
65 }
66 }
67
68 if fresh {
69 if let Some(top) = self.worklist.last_mut() {
71 top.curr_edge = curr_edge + 1;
72 }
74 self.worklist.push(
75 crate::records::tarjan_worklist_vertex::TarjanWorklistVertex {
76 index: child_index,
77 curr_edge: -1,
78 last_edge: -1,
79 },
80 );
81 found_fresh = true;
82 break;
83 } else if self.nodes[child_index as usize].on_stack {
84 let ll = self.nodes[index as usize].lowlink;
85 let other = child_index;
86 if other < ll {
87 self.nodes[index as usize].lowlink = other;
88 }
89 }
90
91 self.visit_edge(child_index, index);
92
93 curr_edge += 1;
94 }
95
96 if found_fresh {
97 continue;
98 }
99
100 if self.nodes[index as usize].lowlink == index {
101 self.visit_scc(index);
102
103 while !self.stack.is_empty() {
104 let popped = *self.stack.last().unwrap();
105 self.stack.pop();
106
107 self.nodes[popped as usize].on_stack = false;
108 if popped == index {
109 break;
110 }
111 }
112 }
113
114 self.worklist.pop();
115
116 if !self.worklist.is_empty() {
118 let (parent_index, _parent_curr_edge, parent_end_edge) = {
119 let top = self.worklist.last().unwrap();
120 (top.index, top.curr_edge, top.last_edge)
121 };
122
123 let new_len = parent_end_edge as usize;
125 self.edges_ty.truncate(new_len);
126 self.edges_tp.truncate(new_len);
127
128 let child_lowlink = self.nodes[index as usize].lowlink;
129 let parent_lowlink_ref = &mut self.nodes[parent_index as usize].lowlink;
130 if child_lowlink < *parent_lowlink_ref {
131 *parent_lowlink_ref = child_lowlink;
132 }
133
134 self.visit_edge(index, parent_index);
135 }
136 }
137
138 TarjanResult::Ok
139 }
140}