@@ -4,7 +4,6 @@ use std::borrow::Cow;
4
4
use std:: collections:: BTreeMap ;
5
5
use std:: collections:: BTreeSet ;
6
6
use std:: collections:: HashMap ;
7
- use std:: collections:: HashSet ;
8
7
use std:: collections:: VecDeque ;
9
8
use std:: sync:: Arc ;
10
9
@@ -33,49 +32,77 @@ use super::NpmResolutionPackage;
33
32
use super :: NpmVersionMatcher ;
34
33
35
34
#[ derive( Default , Clone ) ]
36
- struct VisitedVersions ( HashSet < String > ) ;
35
+ struct VisitedVersionsPath {
36
+ previous_node : Option < Arc < VisitedVersionsPath > > ,
37
+ visited_version : ( String , NpmVersion ) ,
38
+ }
37
39
38
- impl VisitedVersions {
39
- pub fn add ( & mut self , id : & NpmPackageId ) -> bool {
40
- self . 0 . insert ( Self :: id_as_key ( id) )
40
+ impl VisitedVersionsPath {
41
+ pub fn new ( id : & NpmPackageId ) -> Arc < Self > {
42
+ Arc :: new ( Self {
43
+ previous_node : None ,
44
+ visited_version : Self :: id_as_name_and_version ( id) ,
45
+ } )
41
46
}
42
47
43
- pub fn has_visited ( & self , id : & NpmPackageId ) -> bool {
44
- self . 0 . contains ( & Self :: id_as_key ( id) )
48
+ pub fn with_id (
49
+ self : & Arc < VisitedVersionsPath > ,
50
+ id : & NpmPackageId ,
51
+ ) -> Option < Arc < Self > > {
52
+ if self . has_visited ( id) {
53
+ None
54
+ } else {
55
+ Some ( Arc :: new ( Self {
56
+ previous_node : Some ( self . clone ( ) ) ,
57
+ visited_version : Self :: id_as_name_and_version ( id) ,
58
+ } ) )
59
+ }
60
+ }
61
+
62
+ pub fn has_visited ( self : & Arc < Self > , id : & NpmPackageId ) -> bool {
63
+ let mut maybe_next_node = Some ( self ) ;
64
+ let name_and_version = Self :: id_as_name_and_version ( id) ;
65
+ while let Some ( next_node) = maybe_next_node {
66
+ if next_node. visited_version == name_and_version {
67
+ return true ;
68
+ }
69
+ maybe_next_node = next_node. previous_node . as_ref ( ) ;
70
+ }
71
+ false
45
72
}
46
73
47
- fn id_as_key ( id : & NpmPackageId ) -> String {
48
- // we only key on name and version in the id and not peer dependencies
49
- // because the peer dependencies could change above and below us,
50
- // but the names and versions won't
51
- format ! ( "{}@{}" , id. name, id. version)
74
+ fn id_as_name_and_version ( id : & NpmPackageId ) -> ( String , NpmVersion ) {
75
+ ( id. name . clone ( ) , id. version . clone ( ) )
52
76
}
53
77
}
54
78
55
79
#[ derive( Default , Clone ) ]
56
80
struct GraphPath {
57
- // todo(THIS PR): investigate if this should use a singly linked list too
58
- visited_versions : VisitedVersions ,
59
- // todo(THIS PR): switch to a singly linked list here
60
- specifiers : Vec < String > ,
81
+ previous_node : Option < Arc < GraphPath > > ,
82
+ specifier : String ,
61
83
}
62
84
63
85
impl GraphPath {
64
- pub fn with_step ( & self , specifier : & str , id : & NpmPackageId ) -> GraphPath {
65
- let mut copy = self . clone ( ) ;
66
- assert ! ( copy. visited_versions. add( id) ) ;
67
- copy. specifiers . push ( specifier. to_string ( ) ) ;
68
- copy
86
+ pub fn new ( specifier : String ) -> Arc < Self > {
87
+ Arc :: new ( Self {
88
+ previous_node : None ,
89
+ specifier,
90
+ } )
91
+ }
92
+
93
+ pub fn with_specifier ( self : & Arc < Self > , specifier : String ) -> Arc < Self > {
94
+ Arc :: new ( Self {
95
+ previous_node : Some ( self . clone ( ) ) ,
96
+ specifier,
97
+ } )
69
98
}
70
99
71
- pub fn with_specifier ( & self , specifier : String ) -> GraphPath {
72
- let mut copy = self . clone ( ) ;
73
- copy. specifiers . push ( specifier) ;
74
- copy
100
+ pub fn pop ( & self ) -> Option < & Arc < Self > > {
101
+ self . previous_node . as_ref ( )
75
102
}
76
103
77
- pub fn has_visited_version ( & self , id : & NpmPackageId ) -> bool {
78
- self . visited_versions . has_visited ( id )
104
+ pub fn is_last ( & self ) -> bool {
105
+ self . previous_node . is_none ( )
79
106
}
80
107
}
81
108
@@ -343,7 +370,8 @@ impl Graph {
343
370
pub struct GraphDependencyResolver < ' a , TNpmRegistryApi : NpmRegistryApi > {
344
371
graph : & ' a mut Graph ,
345
372
api : & ' a TNpmRegistryApi ,
346
- pending_unresolved_nodes : VecDeque < ( VisitedVersions , Arc < Mutex < Node > > ) > ,
373
+ pending_unresolved_nodes :
374
+ VecDeque < ( Arc < VisitedVersionsPath > , Arc < Mutex < Node > > ) > ,
347
375
}
348
376
349
377
impl < ' a , TNpmRegistryApi : NpmRegistryApi >
@@ -423,9 +451,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
423
451
& node,
424
452
& NodeParent :: Req ,
425
453
) ;
426
- self
427
- . pending_unresolved_nodes
428
- . push_back ( ( VisitedVersions :: default ( ) , node) ) ;
454
+ self . add_pending_unresolved_node ( None , & node) ;
429
455
Ok ( ( ) )
430
456
}
431
457
@@ -434,7 +460,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
434
460
entry : & NpmDependencyEntry ,
435
461
package_info : NpmPackageInfo ,
436
462
parent_id : & NpmPackageId ,
437
- visited_versions : & VisitedVersions ,
463
+ visited_versions : & Arc < VisitedVersionsPath > ,
438
464
) -> Result < ( ) , AnyError > {
439
465
let node = self . resolve_node_from_info (
440
466
& entry. name ,
@@ -456,10 +482,28 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
456
482
& node,
457
483
& NodeParent :: Node ( parent_id. clone ( ) ) ,
458
484
) ;
485
+ self . add_pending_unresolved_node ( Some ( visited_versions) , & node) ;
486
+ Ok ( ( ) )
487
+ }
488
+
489
+ fn add_pending_unresolved_node (
490
+ & mut self ,
491
+ maybe_previous_visited_versions : Option < & Arc < VisitedVersionsPath > > ,
492
+ node : & Arc < Mutex < Node > > ,
493
+ ) {
494
+ let node_id = node. lock ( ) . id . clone ( ) ;
495
+ let visited_versions = match maybe_previous_visited_versions {
496
+ Some ( previous_visited_versions) => {
497
+ match previous_visited_versions. with_id ( & node_id) {
498
+ Some ( visited_versions) => visited_versions,
499
+ None => return , // circular, don't visit this node
500
+ }
501
+ }
502
+ None => VisitedVersionsPath :: new ( & node_id) ,
503
+ } ;
459
504
self
460
505
. pending_unresolved_nodes
461
- . push_back ( ( visited_versions. clone ( ) , node) ) ;
462
- Ok ( ( ) )
506
+ . push_back ( ( visited_versions, node. clone ( ) ) ) ;
463
507
}
464
508
465
509
fn resolve_node_from_info (
@@ -503,18 +547,14 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
503
547
pub async fn resolve_pending ( & mut self ) -> Result < ( ) , AnyError > {
504
548
while !self . pending_unresolved_nodes . is_empty ( ) {
505
549
// now go down through the dependencies by tree depth
506
- while let Some ( ( mut visited_versions, parent_node) ) =
550
+ while let Some ( ( visited_versions, parent_node) ) =
507
551
self . pending_unresolved_nodes . pop_front ( )
508
552
{
509
553
let ( mut parent_id, deps) = {
510
554
let parent_node = parent_node. lock ( ) ;
511
555
( parent_node. id . clone ( ) , parent_node. deps . clone ( ) )
512
556
} ;
513
557
514
- if !visited_versions. add ( & parent_id) {
515
- continue ; // circular
516
- }
517
-
518
558
// cache all the dependencies' registry infos in parallel if should
519
559
if !should_sync_download ( ) {
520
560
let handles = deps
@@ -578,7 +618,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
578
618
parent_id : & NpmPackageId ,
579
619
peer_dep : & NpmDependencyEntry ,
580
620
peer_package_info : NpmPackageInfo ,
581
- visited_ancestor_versions : & VisitedVersions ,
621
+ visited_ancestor_versions : & Arc < VisitedVersionsPath > ,
582
622
) -> Result < Option < NpmPackageId > , AnyError > {
583
623
fn find_matching_child < ' a > (
584
624
peer_dep : & NpmDependencyEntry ,
@@ -597,36 +637,59 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
597
637
// Peer dependencies are resolved based on its ancestors' siblings.
598
638
// If not found, then it resolves based on the version requirement if non-optional.
599
639
let mut pending_ancestors = VecDeque :: new ( ) ; // go up the tree by depth
600
- let path = GraphPath :: default ( ) . with_step ( specifier, parent_id) ;
640
+ let path = GraphPath :: new ( specifier. to_string ( ) ) ;
641
+ let visited_versions = VisitedVersionsPath :: new ( parent_id) ;
601
642
602
643
// skip over the current node
603
644
for ( specifier, grand_parents) in
604
645
self . graph . borrow_node ( parent_id) . parents . clone ( )
605
646
{
606
647
let path = path. with_specifier ( specifier) ;
607
648
for grand_parent in grand_parents {
608
- pending_ancestors. push_back ( ( grand_parent, path. clone ( ) ) ) ;
649
+ let visited_versions = match & grand_parent {
650
+ NodeParent :: Node ( id) => visited_versions. with_id ( id) ,
651
+ NodeParent :: Req => Some ( visited_versions. clone ( ) ) ,
652
+ } ;
653
+ if let Some ( visited_versions) = visited_versions {
654
+ pending_ancestors. push_back ( (
655
+ grand_parent,
656
+ path. clone ( ) ,
657
+ visited_versions,
658
+ ) ) ;
659
+ }
609
660
}
610
661
}
611
662
612
- while let Some ( ( ancestor, path) ) = pending_ancestors. pop_front ( ) {
663
+ while let Some ( ( ancestor, path, visited_versions) ) =
664
+ pending_ancestors. pop_front ( )
665
+ {
613
666
match & ancestor {
614
667
NodeParent :: Node ( ancestor_node_id) => {
615
- // we've gone in a full circle, so don't keep looking
616
- if path. has_visited_version ( ancestor_node_id) {
617
- continue ;
618
- }
619
-
620
668
let maybe_peer_dep_id = if ancestor_node_id. name == peer_dep. name
621
669
&& peer_dep. version_req . satisfies ( & ancestor_node_id. version )
622
670
{
671
+ // found it
623
672
Some ( ancestor_node_id. clone ( ) )
624
673
} else {
674
+ // continue searching up
625
675
let ancestor = self . graph . borrow_node ( ancestor_node_id) ;
626
676
for ( specifier, parents) in & ancestor. parents {
627
- let new_path = path. with_step ( specifier, ancestor_node_id) ;
628
677
for parent in parents {
629
- pending_ancestors. push_back ( ( parent. clone ( ) , new_path. clone ( ) ) ) ;
678
+ let path = path. with_specifier ( specifier. to_string ( ) ) ;
679
+ let visited_versions = match parent {
680
+ NodeParent :: Node ( id) => visited_versions. with_id ( id) ,
681
+ NodeParent :: Req => Some ( visited_versions. clone ( ) ) ,
682
+ } ;
683
+ if let Some ( visited_versions) = visited_versions {
684
+ pending_ancestors. push_back ( (
685
+ parent. clone ( ) ,
686
+ path,
687
+ visited_versions,
688
+ ) ) ;
689
+ } else {
690
+ // we've gone in a full circle, so don't keep looking
691
+ continue ;
692
+ }
630
693
}
631
694
}
632
695
find_matching_child ( peer_dep, ancestor. children . values ( ) )
@@ -638,7 +701,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
638
701
parents,
639
702
ancestor_node_id,
640
703
& peer_dep_id,
641
- path. specifiers ,
704
+ & path,
642
705
visited_ancestor_versions,
643
706
) ) ) ;
644
707
}
@@ -648,8 +711,8 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
648
711
if let Some ( child_id) =
649
712
find_matching_child ( peer_dep, self . graph . package_reqs . values ( ) )
650
713
{
651
- let mut path = path. specifiers ;
652
- let specifier = path. pop ( ) . unwrap ( ) ; // go back down one level
714
+ let specifier = path. specifier . clone ( ) ;
715
+ let path = path. pop ( ) . unwrap ( ) ; // go back down one level from the package requirement
653
716
let old_id =
654
717
self . graph . package_reqs . get ( & specifier) . unwrap ( ) . clone ( ) ;
655
718
return Ok ( Some ( self . set_new_peer_dep (
@@ -684,8 +747,8 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
684
747
previous_parents : BTreeMap < String , BTreeSet < NodeParent > > ,
685
748
node_id : & NpmPackageId ,
686
749
peer_dep_id : & NpmPackageId ,
687
- mut path : Vec < String > ,
688
- visited_ancestor_versions : & VisitedVersions ,
750
+ path : & GraphPath ,
751
+ visited_ancestor_versions : & Arc < VisitedVersionsPath > ,
689
752
) -> NpmPackageId {
690
753
let mut peer_dep_id = Cow :: Borrowed ( peer_dep_id) ;
691
754
let old_id = node_id;
@@ -708,15 +771,12 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
708
771
}
709
772
710
773
// remove the previous parents from the old node
711
- let old_node_children = {
712
- for ( specifier, parents) in & previous_parents {
713
- for parent in parents {
714
- self . graph . remove_child_parent ( specifier, old_id, parent) ;
715
- }
774
+ for ( specifier, parents) in & previous_parents {
775
+ for parent in parents {
776
+ self . graph . remove_child_parent ( specifier, old_id, parent) ;
716
777
}
717
- let old_node = self . graph . borrow_node ( old_id) ;
718
- old_node. children . clone ( )
719
- } ;
778
+ }
779
+ let old_node_children = self . graph . borrow_node ( old_id) . children . clone ( ) ;
720
780
721
781
let ( _, new_node) = self . graph . get_or_create_for_id ( & new_id) ;
722
782
@@ -743,26 +803,25 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
743
803
let mut bottom_parent_id = new_id. clone ( ) ;
744
804
745
805
// continue going down the path
746
- if let Some ( next_specifier ) = path. pop ( ) {
747
- if path. is_empty ( ) {
806
+ if let Some ( path ) = path. pop ( ) {
807
+ if path. is_last ( ) {
748
808
// this means we're at the peer dependency now
749
809
debug ! (
750
810
"Resolved peer dependency for {} in {} to {}" ,
751
- & next_specifier , & new_id, & peer_dep_id,
811
+ & path . specifier , & new_id, & peer_dep_id,
752
812
) ;
753
- assert ! ( !old_node_children. contains_key( & next_specifier ) ) ;
813
+ assert ! ( !old_node_children. contains_key( & path . specifier ) ) ;
754
814
let node = self . graph . get_or_create_for_id ( & peer_dep_id) . 1 ;
755
815
self
756
- . pending_unresolved_nodes
757
- . push_back ( ( visited_ancestor_versions. clone ( ) , node. clone ( ) ) ) ;
816
+ . add_pending_unresolved_node ( Some ( visited_ancestor_versions) , & node) ;
758
817
self
759
818
. graph
760
- . set_child_parent_node ( & next_specifier , & node, & new_id) ;
819
+ . set_child_parent_node ( & path . specifier , & node, & new_id) ;
761
820
} else {
762
- let next_node_id = old_node_children. get ( & next_specifier ) . unwrap ( ) ;
821
+ let next_node_id = old_node_children. get ( & path . specifier ) . unwrap ( ) ;
763
822
bottom_parent_id = self . set_new_peer_dep (
764
823
BTreeMap :: from ( [ (
765
- next_specifier . to_string ( ) ,
824
+ path . specifier . to_string ( ) ,
766
825
BTreeSet :: from ( [ NodeParent :: Node ( new_id. clone ( ) ) ] ) ,
767
826
) ] ) ,
768
827
next_node_id,
0 commit comments