luaur_analysis/methods/
frontend_check_queued_modules.rs1use crate::enums::solver_mode::SolverMode;
2use crate::functions::make_type_check_limits::make_type_check_limits;
3use crate::records::build_queue_work_state::{BuildQueueWorkState, Task};
4use crate::records::frontend::Frontend;
5use crate::records::frontend_options::FrontendOptions;
6use crate::type_aliases::module_name_file_resolver::ModuleName;
7use alloc::boxed::Box;
8use alloc::sync::Arc;
9use alloc::vec::Vec;
10use core::cell::Cell;
11use luaur_common::macros::luau_assert::LUAU_ASSERT;
12use luaur_common::records::dense_hash_set::DenseHashSet;
13use std::collections::HashMap;
14use std::sync::{Condvar, Mutex};
15
16fn task_to_fn(task: Task) -> Box<dyn Fn()> {
19 let cell = Cell::new(Some(task));
20 Box::new(move || {
21 if let Some(t) = cell.take() {
22 t();
23 }
24 })
25}
26
27struct ExecutorWrapper {
32 inner: Box<dyn Fn(Vec<Box<dyn Fn()>>)>,
33}
34
35unsafe impl Send for ExecutorWrapper {}
36unsafe impl Sync for ExecutorWrapper {}
37
38impl ExecutorWrapper {
39 fn run(&self, tasks: Vec<Task>) {
40 let adapted: Vec<Box<dyn Fn()>> = tasks.into_iter().map(task_to_fn).collect();
41 (self.inner)(adapted);
42 }
43}
44
45impl Frontend {
46 pub fn check_queued_modules(
47 &mut self,
48 option_override: Option<FrontendOptions>,
49 execute_tasks: Box<dyn Fn(Vec<Box<dyn Fn()>>)>,
50 progress: Box<dyn Fn(usize, usize) -> bool>,
51 ) -> Vec<ModuleName> {
52 let mut frontend_options = option_override.unwrap_or_else(|| self.options.clone());
53 if self.get_luau_solver_mode() == SolverMode::New {
54 frontend_options.for_autocomplete = false;
55 }
56
57 let curr_module_queue: Vec<ModuleName> = core::mem::take(&mut self.module_queue);
59
60 let mut seen: DenseHashSet<ModuleName> = DenseHashSet::new(ModuleName::default());
61
62 let state = Arc::new(BuildQueueWorkState {
63 execute_task_deprecated: None,
64 execute_tasks: None,
65 build_queue_items: Vec::new(),
66 mtx: Mutex::new(()),
67 cv: Condvar::new(),
68 ready_queue_items: Vec::new(),
69 processing: 0,
70 remaining: 0,
71 });
72
73 let state_ptr = Arc::as_ptr(&state) as *mut BuildQueueWorkState;
77
78 for name in &curr_module_queue {
79 if seen.contains(name) {
80 continue;
81 }
82
83 if !self.is_dirty(name, frontend_options.for_autocomplete) {
84 seen.insert(name.clone());
85 continue;
86 }
87
88 let mut queue: Vec<ModuleName> = Vec::new();
89 let cycle_detected = self.parse_graph(
90 &mut queue,
91 name,
92 &make_type_check_limits(&frontend_options),
93 frontend_options.for_autocomplete,
94 );
95
96 {
97 let bqi = unsafe { &mut (*state_ptr).build_queue_items };
98 self.add_build_queue_items(
99 bqi,
100 &queue,
101 cycle_detected,
102 &mut seen,
103 &frontend_options,
104 );
105 }
106 }
107
108 {
109 let bqi = unsafe { &(*state_ptr).build_queue_items };
110 if bqi.is_empty() {
111 return Vec::new();
112 }
113 }
114
115 let mut module_name_to_queue: HashMap<ModuleName, usize> = HashMap::new();
117 {
118 let bqi = unsafe { &(*state_ptr).build_queue_items };
119 for i in 0..bqi.len() {
120 module_name_to_queue.insert(bqi[i].name.clone(), i);
121 }
122 }
123
124 let executor = ExecutorWrapper {
127 inner: execute_tasks,
128 };
129 unsafe {
130 (*state_ptr).execute_tasks = Some(Box::new(move |tasks: Vec<Task>| {
131 executor.run(tasks);
132 }));
133 }
134 {
135 let len = {
136 let bqi = unsafe { &(*state_ptr).build_queue_items };
137 bqi.len()
138 };
139 unsafe {
140 (*state_ptr).remaining = len;
141 }
142 }
143
144 {
146 let count = {
147 let bqi = unsafe { &(*state_ptr).build_queue_items };
148 bqi.len()
149 };
150 for i in 0..count {
151 let deps: Vec<ModuleName> = {
152 let bqi = unsafe { &(*state_ptr).build_queue_items };
153 bqi[i].source_node.require_set.iter().cloned().collect()
154 };
155
156 for dep in deps {
157 if let Some(node) = self.source_nodes.get(&dep) {
158 if node.has_dirty_module(frontend_options.for_autocomplete) {
159 let dep_pos = module_name_to_queue[&dep];
160 let bqi = unsafe { &mut (*state_ptr).build_queue_items };
161 bqi[i].dirty_dependencies += 1;
162 bqi[dep_pos].reverse_deps.push(i);
163 }
164 }
165 }
166 }
167 }
168
169 let mut next_items: Vec<usize> = Vec::new();
170
171 {
173 let bqi = unsafe { &(*state_ptr).build_queue_items };
174 for i in 0..bqi.len() {
175 if bqi[i].dirty_dependencies == 0 {
176 next_items.push(i);
177 }
178 }
179 }
180
181 if !next_items.is_empty() {
182 self.send_queue_item_tasks(state.clone(), core::mem::take(&mut next_items));
183 }
184
185 if unsafe { (*state_ptr).processing } == 0 {
187 self.send_queue_cycle_item_task(state.clone());
188 }
189
190 let mut item_with_exception: Option<usize> = None;
191 let mut cancelled = false;
192
193 while unsafe { (*state_ptr).remaining } != 0 {
194 {
195 let mtx = unsafe { &(*state_ptr).mtx };
196 let cv = unsafe { &(*state_ptr).cv };
197 let guard = mtx.lock().unwrap();
198
199 let _guard = cv
201 .wait_while(guard, |_| {
202 let ready = unsafe { &(*state_ptr).ready_queue_items };
203 ready.is_empty()
204 })
205 .unwrap();
206
207 let ready: Vec<usize> = {
209 let r = unsafe { &(*state_ptr).ready_queue_items };
210 r.clone()
211 };
212 for i in ready.iter().copied() {
213 let (has_exception, is_cancelled) = {
214 let bqi = unsafe { &(*state_ptr).build_queue_items };
215 (bqi[i].exception.is_some(), bqi[i].module.cancelled)
216 };
217 if has_exception {
218 item_with_exception = Some(i);
219 }
220 if is_cancelled {
221 cancelled = true;
222 }
223
224 if item_with_exception.is_some() || cancelled {
225 break;
226 }
227
228 {
229 let bqi = unsafe { &(*state_ptr).build_queue_items };
230 let item_ref = &bqi[i];
231 self.record_item_result(item_ref);
232 }
233
234 let reverse_deps: Vec<usize> = {
236 let bqi = unsafe { &(*state_ptr).build_queue_items };
237 bqi[i].reverse_deps.clone()
238 };
239 for reverse_dep in reverse_deps {
240 let bqi = unsafe { &mut (*state_ptr).build_queue_items };
241 LUAU_ASSERT!(bqi[reverse_dep].dirty_dependencies != 0);
242 bqi[reverse_dep].dirty_dependencies -= 1;
243
244 if !bqi[reverse_dep].processing && bqi[reverse_dep].dirty_dependencies == 0
245 {
246 next_items.push(reverse_dep);
247 }
248 }
249 }
250
251 {
252 let ready_len = {
253 let r = unsafe { &(*state_ptr).ready_queue_items };
254 r.len()
255 };
256 unsafe {
257 LUAU_ASSERT!((*state_ptr).processing >= ready_len);
258 (*state_ptr).processing -= ready_len;
259
260 LUAU_ASSERT!((*state_ptr).remaining >= ready_len);
261 (*state_ptr).remaining -= ready_len;
262 }
263 let r = unsafe { &mut (*state_ptr).ready_queue_items };
264 r.clear();
265 }
266 }
267
268 {
269 let total = {
270 let bqi = unsafe { &(*state_ptr).build_queue_items };
271 bqi.len()
272 };
273 let done = total - unsafe { (*state_ptr).remaining };
274 if !progress(done, total) {
275 cancelled = true;
276 }
277 }
278
279 if !next_items.is_empty() {
281 self.send_queue_item_tasks(state.clone(), core::mem::take(&mut next_items));
282 }
283
284 if unsafe { (*state_ptr).processing } == 0 {
285 if cancelled {
287 return Vec::new();
288 }
289
290 if let Some(idx) = item_with_exception {
292 let bqi = unsafe { &(*state_ptr).build_queue_items };
293 let item_ref = &bqi[idx];
294 self.record_item_result(item_ref);
295 }
296 }
297
298 if unsafe { (*state_ptr).remaining } != 0 && unsafe { (*state_ptr).processing } == 0 {
300 self.send_queue_cycle_item_task(state.clone());
301 }
302 }
303
304 let mut checked_modules: Vec<ModuleName> = Vec::new();
305 {
306 let count = {
307 let bqi = unsafe { &(*state_ptr).build_queue_items };
308 bqi.len()
309 };
310 checked_modules.reserve(count);
311 for i in 0..count {
312 let bqi = unsafe { &mut (*state_ptr).build_queue_items };
313 checked_modules.push(core::mem::take(&mut bqi[i].name));
314 }
315 }
316
317 checked_modules
318 }
319}