@@ -269,145 +269,167 @@ impl GenerateStage<'_> {
269269 // guaranteed.
270270 return ;
271271 }
272- chunk_graph
273- . chunk_table
274- . iter_mut ( )
275- . filter ( |chunk| matches ! ( chunk. kind, ChunkKind :: EntryPoint { .. } ) )
276- . for_each ( |chunk| {
277- let ChunkKind :: EntryPoint { module : entry_module, .. } = & chunk. kind else {
278- return ;
279- } ;
280- // After modules in chunk is sorted, it is always sorted by execution order whatever the
281- // `chunk_modules_order` is `exec_order` or `module_id`. Because for `module_id` we only sort
282- // by `module_id` for side effects free leaf modules, those should always execute first and
283- // has no wrapping.
284- let mut wrapped_modules = vec ! [ ] ;
285- // If a none wrapped module has higher execution order than a wrapped module
286- // we called the none wrapped module depended on the wrapped module(e.g. the none wrapped
287- // module may depended on a global variable initialization in the wrapped module, however
288- // the wrapped module are usually lazy evaluate). So we need to adjust the initialization
289- // order
290- // manually.
291- let imported_symbol_owner_from_other_chunk = chunk
292- . imports_from_other_chunks
293- . iter ( )
294- . flat_map ( |( _, import_items) | {
295- import_items
296- . iter ( )
297- . map ( |item| self . link_output . symbol_db . canonical_ref_for ( item. import_ref ) . owner )
298- } )
299- . filter_map ( |idx| {
300- let module = self . link_output . module_table [ idx] . as_normal ( ) ?;
301- ( !self . link_output . metas [ module. idx ] . original_wrap_kind ( ) . is_none ( ) ) . then_some ( idx)
302- } )
303- . collect :: < FxHashSet < _ > > ( ) ;
304- let chunk_module_to_exec_order = chunk
305- . modules
306- . iter ( )
307- . chain ( imported_symbol_owner_from_other_chunk. iter ( ) )
308- . map ( |idx| ( * idx, self . link_output . module_table [ * idx] . exec_order ( ) ) )
309- . collect :: < FxHashMap < _ , _ > > ( ) ;
310-
311- // the key is the module_idx of none wrapped module
312- // the value is the how many wrapped modules did the none wrapped module depends on.
313- // when getting all depended wrapped modules, just use wrapped_modules[0..none_wrapped_module_to_wrapped_dependency_length[none_wrap_module_idx]].
314- let mut none_wrapped_module_to_wrapped_dependency_length = FxHashMap :: default ( ) ;
315- let js_import_order = self . js_import_order ( * entry_module, & chunk_module_to_exec_order) ;
316- for idx in js_import_order {
317- match self . link_output . metas [ idx] . original_wrap_kind ( ) {
318- WrapKind :: None => {
319- if !wrapped_modules. is_empty ( ) {
320- none_wrapped_module_to_wrapped_dependency_length. insert ( idx, wrapped_modules. len ( ) ) ;
321- }
322- }
323- WrapKind :: Cjs | WrapKind :: Esm => {
324- wrapped_modules. push ( idx) ;
272+ chunk_graph. chunk_table . iter_mut ( ) . for_each ( |chunk| {
273+ // Determine DFS roots based on chunk kind.
274+ // For entry chunks, the root is the entry module.
275+ // For common chunks, roots are modules not imported by any other module in the chunk.
276+ let roots: Vec < ModuleIdx > = match & chunk. kind {
277+ ChunkKind :: EntryPoint { module, .. } => vec ! [ * module] ,
278+ ChunkKind :: Common => {
279+ let chunk_modules_set: FxHashSet < ModuleIdx > = chunk. modules . iter ( ) . copied ( ) . collect ( ) ;
280+ let imported_in_chunk: FxHashSet < ModuleIdx > = chunk
281+ . modules
282+ . iter ( )
283+ . filter_map ( |& idx| self . link_output . module_table [ idx] . as_normal ( ) )
284+ . flat_map ( |normal| & normal. import_records )
285+ . filter_map ( |rec| {
286+ let resolved_module = rec. resolved_module ?;
287+ ( rec. kind == ImportKind :: Import && chunk_modules_set. contains ( & resolved_module) )
288+ . then_some ( resolved_module)
289+ } )
290+ . collect ( ) ;
291+ let mut roots: Vec < ModuleIdx > =
292+ chunk. modules . iter ( ) . filter ( |idx| !imported_in_chunk. contains ( idx) ) . copied ( ) . collect ( ) ;
293+ roots. sort_unstable_by_key ( |idx| self . link_output . module_table [ * idx] . exec_order ( ) ) ;
294+ roots
295+ }
296+ } ;
297+
298+ if roots. is_empty ( ) {
299+ return ;
300+ }
301+
302+ // After modules in chunk is sorted, it is always sorted by execution order whatever the
303+ // `chunk_modules_order` is `exec_order` or `module_id`. Because for `module_id` we only sort
304+ // by `module_id` for side effects free leaf modules, those should always execute first and
305+ // has no wrapping.
306+ let mut wrapped_modules = vec ! [ ] ;
307+ // If a none wrapped module has higher execution order than a wrapped module
308+ // we called the none wrapped module depended on the wrapped module(e.g. the none wrapped
309+ // module may depended on a global variable initialization in the wrapped module, however
310+ // the wrapped module are usually lazy evaluate). So we need to adjust the initialization
311+ // order
312+ // manually.
313+ let imported_symbol_owner_from_other_chunk = chunk
314+ . imports_from_other_chunks
315+ . iter ( )
316+ . flat_map ( |( _, import_items) | {
317+ import_items
318+ . iter ( )
319+ . map ( |item| self . link_output . symbol_db . canonical_ref_for ( item. import_ref ) . owner )
320+ } )
321+ . filter_map ( |idx| {
322+ let module = self . link_output . module_table [ idx] . as_normal ( ) ?;
323+ ( !self . link_output . metas [ module. idx ] . original_wrap_kind ( ) . is_none ( ) ) . then_some ( idx)
324+ } )
325+ . collect :: < FxHashSet < _ > > ( ) ;
326+ let chunk_module_to_exec_order = chunk
327+ . modules
328+ . iter ( )
329+ . chain ( imported_symbol_owner_from_other_chunk. iter ( ) )
330+ . map ( |idx| ( * idx, self . link_output . module_table [ * idx] . exec_order ( ) ) )
331+ . collect :: < FxHashMap < _ , _ > > ( ) ;
332+
333+ // the key is the module_idx of none wrapped module
334+ // the value is the how many wrapped modules did the none wrapped module depends on.
335+ // when getting all depended wrapped modules, just use wrapped_modules[0..none_wrapped_module_to_wrapped_dependency_length[none_wrap_module_idx]].
336+ let mut none_wrapped_module_to_wrapped_dependency_length = FxHashMap :: default ( ) ;
337+ let js_import_order = self . js_import_order ( & roots, & chunk_module_to_exec_order) ;
338+ for idx in js_import_order {
339+ match self . link_output . metas [ idx] . original_wrap_kind ( ) {
340+ WrapKind :: None => {
341+ if !wrapped_modules. is_empty ( ) {
342+ none_wrapped_module_to_wrapped_dependency_length. insert ( idx, wrapped_modules. len ( ) ) ;
325343 }
326344 }
345+ WrapKind :: Cjs | WrapKind :: Esm => {
346+ wrapped_modules. push ( idx) ;
347+ }
327348 }
328- // All modules that we need to ensure the initialization order.
329- let mut modules_need_to_check: FxHashSet < ModuleIdx > = FxHashSet :: default ( ) ;
330- let mut max_length = 0 ;
331- for ( none_wrapped, dep_length) in & none_wrapped_module_to_wrapped_dependency_length {
332- modules_need_to_check. insert ( * none_wrapped) ;
333- max_length = max_length. max ( * dep_length) ;
334- }
335- modules_need_to_check. extend ( & wrapped_modules[ 0 ..max_length] ) ;
349+ }
350+ // All modules that we need to ensure the initialization order.
351+ let mut modules_need_to_check: FxHashSet < ModuleIdx > = FxHashSet :: default ( ) ;
352+ let mut max_length = 0 ;
353+ for ( none_wrapped, dep_length) in & none_wrapped_module_to_wrapped_dependency_length {
354+ modules_need_to_check. insert ( * none_wrapped) ;
355+ max_length = max_length. max ( * dep_length) ;
356+ }
357+ modules_need_to_check. extend ( & wrapped_modules[ 0 ..max_length] ) ;
336358
337- if modules_need_to_check. is_empty ( ) {
338- // No wrapped modules or none wrapped modules that depends on wrapped modules, so we can
339- // skip the initialization order check.
340- return ;
341- }
359+ if modules_need_to_check. is_empty ( ) {
360+ // No wrapped modules or none wrapped modules that depends on wrapped modules, so we can
361+ // skip the initialization order check.
362+ return ;
363+ }
342364
343- // Record each module in `modules_need_to_check` first init position.
344- let mut module_init_position = FxIndexMap :: default ( ) ;
365+ // Record each module in `modules_need_to_check` first init position.
366+ let mut module_init_position = FxIndexMap :: default ( ) ;
345367
346- for idx in & chunk. modules {
347- let Some ( module) = self . link_output . module_table [ * idx] . as_normal ( ) else {
348- continue ;
349- } ;
350- module
351- . import_records
352- . iter_enumerated ( )
353- . filter_map ( |( rec_idx, rec) | {
354- rec. resolved_module . map ( |module_idx| ( rec_idx, rec, module_idx) )
355- } )
356- . for_each ( |( rec_idx, rec, module_idx) | {
357- if rec. kind == ImportKind :: Import && modules_need_to_check. contains ( & module_idx) {
358- module_init_position. entry ( module_idx) . or_insert ( ( * idx, rec_idx) ) ;
359- }
360- } ) ;
361- if module_init_position. len ( ) == modules_need_to_check. len ( ) {
362- break ;
363- }
368+ for idx in & chunk. modules {
369+ let Some ( module) = self . link_output . module_table [ * idx] . as_normal ( ) else {
370+ continue ;
371+ } ;
372+ module
373+ . import_records
374+ . iter_enumerated ( )
375+ . filter_map ( |( rec_idx, rec) | {
376+ rec. resolved_module . map ( |module_idx| ( rec_idx, rec, module_idx) )
377+ } )
378+ . for_each ( |( rec_idx, rec, module_idx) | {
379+ if rec. kind == ImportKind :: Import && modules_need_to_check. contains ( & module_idx) {
380+ module_init_position. entry ( module_idx) . or_insert ( ( * idx, rec_idx) ) ;
381+ }
382+ } ) ;
383+ if module_init_position. len ( ) == modules_need_to_check. len ( ) {
384+ break ;
364385 }
386+ }
365387
366- let mut module_init_position = module_init_position. into_iter ( ) . collect_vec ( ) ;
367- module_init_position. sort_by_cached_key ( |( idx, _) | chunk_module_to_exec_order[ idx] ) ;
368-
369- let mut pending_transfer = vec ! [ ] ;
370- let mut insert_map: FxHashMap < ModuleIdx , Vec < ( ModuleIdx , ImportRecordIdx ) > > =
371- FxHashMap :: default ( ) ;
372- let mut remove_map: FxHashMap < ModuleIdx , Vec < ImportRecordIdx > > = FxHashMap :: default ( ) ;
373- for ( module_idx, ( importer_idx, rec_idx) ) in module_init_position {
374- match self . link_output . metas [ module_idx] . original_wrap_kind ( ) {
375- WrapKind :: None => {
376- if let Some ( deps_length) =
377- none_wrapped_module_to_wrapped_dependency_length. get ( & module_idx)
378- {
379- let transfer_item = pending_transfer
380- . extract_if ( 0 .., |( midx, _, _) | wrapped_modules[ 0 ..* deps_length] . contains ( midx) ) ;
381- for ( _midx, iidx, ridx) in transfer_item {
382- // Should always avoid transfer any initialization from a low execution order module to a high execution order module.
383- if chunk_module_to_exec_order[ & iidx] <= chunk_module_to_exec_order[ & module_idx] {
384- // If the module is the same, we can skip the transfer.
385- continue ;
386- }
387- insert_map. entry ( module_idx) . or_default ( ) . push ( ( iidx, ridx) ) ;
388- remove_map. entry ( iidx) . or_default ( ) . push ( ridx) ;
388+ let mut module_init_position = module_init_position. into_iter ( ) . collect_vec ( ) ;
389+ module_init_position. sort_by_cached_key ( |( idx, _) | chunk_module_to_exec_order[ idx] ) ;
390+
391+ let mut pending_transfer = vec ! [ ] ;
392+ let mut insert_map: FxHashMap < ModuleIdx , Vec < ( ModuleIdx , ImportRecordIdx ) > > =
393+ FxHashMap :: default ( ) ;
394+ let mut remove_map: FxHashMap < ModuleIdx , Vec < ImportRecordIdx > > = FxHashMap :: default ( ) ;
395+ for ( module_idx, ( importer_idx, rec_idx) ) in module_init_position {
396+ match self . link_output . metas [ module_idx] . original_wrap_kind ( ) {
397+ WrapKind :: None => {
398+ if let Some ( deps_length) =
399+ none_wrapped_module_to_wrapped_dependency_length. get ( & module_idx)
400+ {
401+ let transfer_item = pending_transfer
402+ . extract_if ( 0 .., |( midx, _, _) | wrapped_modules[ 0 ..* deps_length] . contains ( midx) ) ;
403+ for ( _midx, iidx, ridx) in transfer_item {
404+ // Should always avoid transfer any initialization from a low execution order module to a high execution order module.
405+ if chunk_module_to_exec_order[ & iidx] <= chunk_module_to_exec_order[ & module_idx] {
406+ // If the module is the same, we can skip the transfer.
407+ continue ;
389408 }
409+ insert_map. entry ( module_idx) . or_default ( ) . push ( ( iidx, ridx) ) ;
410+ remove_map. entry ( iidx) . or_default ( ) . push ( ridx) ;
390411 }
391412 }
392- WrapKind :: Cjs | WrapKind :: Esm => {
393- pending_transfer . push ( ( module_idx , importer_idx , rec_idx ) ) ;
394- }
413+ }
414+ WrapKind :: Cjs | WrapKind :: Esm => {
415+ pending_transfer . push ( ( module_idx , importer_idx , rec_idx ) ) ;
395416 }
396417 }
397- chunk. insert_map = insert_map;
398- chunk. remove_map = remove_map;
399- } ) ;
418+ }
419+ chunk. insert_map = insert_map;
420+ chunk. remove_map = remove_map;
421+ } ) ;
400422 }
401423
402424 /// Only considering module eager initialization order, both `require()` and `import()` are lazy
403425 /// initialization.
404426 fn js_import_order (
405427 & self ,
406- entry : ModuleIdx ,
428+ roots : & [ ModuleIdx ] ,
407429 chunk_modules_map : & FxHashMap < ModuleIdx , u32 > ,
408430 ) -> Vec < ModuleIdx > {
409431 // traverse module graph with depth-first search to determine the order of JS imports
410- let mut stack = vec ! [ entry ] ;
432+ let mut stack: Vec < ModuleIdx > = roots . iter ( ) . copied ( ) . rev ( ) . collect ( ) ;
411433 let mut visited = FxHashSet :: default ( ) ;
412434 let mut js_import_order = vec ! [ ] ;
413435 while let Some ( module_idx) = stack. pop ( ) {
0 commit comments