Skip to content

Commit 28e5b3d

Browse files
committed
Add override fix
1 parent 644ed42 commit 28e5b3d

File tree

1 file changed

+129
-83
lines changed

1 file changed

+129
-83
lines changed

src/shadow/arborist.ts

Lines changed: 129 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -478,92 +478,138 @@ function walk(
478478
function deleteEdgeIn(node: NodeClass, edge: SafeEdge) {
479479
node.edgesIn.delete(edge)
480480
if (edge.overrides) {
481-
// updateNodeOverrideSetDueToEdgeRemoval(node, edge.overrides)
481+
updateNodeOverrideSetDueToEdgeRemoval(node, edge.overrides)
482482
}
483483
}
484484

485-
// function recalculateOutEdgesOverrides (node: NodeClass) {
486-
// // For each edge out propogate the new overrides through.
487-
// for (const [, edge] of node.edgesOut) {
488-
// edge.reload(true)
489-
// if (edge.to) {
490-
// updateNodeOverrideSet(edge.to, edge.overrides)
491-
// }
492-
// }
493-
// }
494-
495-
// function findSpecificOverrideSet (first, second) {
496-
// for (let OverrideSet = second; OverrideSet; OverrideSet = OverrideSet.parent) {
497-
// if (OverrideSet.isEqual(first)) {
498-
// return second
499-
// }
500-
// }
501-
// for (let OverrideSet = first; OverrideSet; OverrideSet = OverrideSet.parent) {
502-
// if (OverrideSet.isEqual(second)) {
503-
// return first
504-
// }
505-
// }
506-
// console.log('Conflicting override sets')
507-
// }
508-
509-
// function updateNodeOverrideSetDueToEdgeRemoval (node: NodeClass, otherOverrideSet) {
510-
// // If this edge's overrides isn't equal to this node's overrides, then removing it won't change newOverrideSet later.
511-
// if (!node.overrides || !node.overrides.isEqual(otherOverrideSet)) {
512-
// return false
513-
// }
514-
// let newOverrideSet
515-
// for (const edge of node.edgesIn) {
516-
// if (newOverrideSet) {
517-
// newOverrideSet = findSpecificOverrideSet(node, edge.overrides, newOverrideSet)
518-
// } else {
519-
// newOverrideSet = edge.overrides
520-
// }
521-
// }
522-
// if (overrides.isEqual(newOverrideSet)) {
523-
// return false
524-
// }
525-
// this.overrides = newOverrideSet
526-
// if (this.overrides) {
527-
// // Optimization: if there's any override set at all, then no non-extraneous node has an empty override set. So if we temporarily have no
528-
// // override set (for example, we removed all the edges in), there's no use updating all the edges out right now. Let's just wait until
529-
// // we have an actual override set later.
530-
// recalculateOutEdgesOverrides(node)
531-
// }
532-
// return true
533-
// }
534-
535-
// // This logic isn't perfect either. When we have two edges in that have different override sets, then we have to decide which set is correct.
536-
// // This function assumes the more specific override set is applicable, so if we have dependencies A->B->C and A->C
537-
// // and an override set that specifies what happens for C under A->B, this will work even if the new A->C edge comes along and tries to change
538-
// // the override set.
539-
// // The strictly correct logic is not to allow two edges with different overrides to point to the same node, because even if this node can satisfy
540-
// // both, one of its dependencies might need to be different depending on the edge leading to it.
541-
// // However, this might cause a lot of duplication, because the conflict in the dependencies might never actually happen.
542-
// function updateNodeOverrideSet (otherOverrideSet) {
543-
// if (!this.overrides) {
544-
// // Assuming there are any overrides at all, the overrides field is never undefined for any node at the end state of the tree.
545-
// // So if the new edge's overrides is undefined it will be updated later. So we can wait with updating the node's overrides field.
546-
// if (!otherOverrideSet) {
547-
// return false
548-
// }
549-
// this.overrides = otherOverrideSet
550-
// this.recalculateOutEdgesOverrides()
551-
// return true
552-
// }
553-
// if (this.overrides.isEqual(otherOverrideSet)) {
554-
// return false
555-
// }
556-
// const newOverrideSet = this.findSpecificOverrideSet(this.overrides, otherOverrideSet)
557-
// if (newOverrideSet) {
558-
// if (!this.overrides.isEqual(newOverrideSet)) {
559-
// this.overrides = newOverrideSet
560-
// this.recalculateOutEdgesOverrides()
561-
// return true
562-
// }
563-
// return false
564-
// }
565-
// // This is an error condition. We can only get here if the new override set is in conflict with the existing.
566-
// }
485+
function findSpecificOverrideSet (first: OverrideSet | undefined, second: OverrideSet | undefined) {
486+
let overrideSet = second
487+
while (overrideSet) {
488+
if (overrideSetsEqual(overrideSet, first)) {
489+
return second
490+
}
491+
overrideSet = overrideSet.parent
492+
}
493+
overrideSet = first
494+
while (overrideSet) {
495+
if (overrideSetsEqual(overrideSet, second)) {
496+
return first
497+
}
498+
overrideSet = overrideSet.parent
499+
}
500+
console.error('Conflicting override sets')
501+
}
502+
503+
function recalculateOutEdgesOverrides (node: NodeClass) {
504+
// For each edge out propagate the new overrides through.
505+
for (const [, edge] of node.edgesOut) {
506+
edge.reload(true)
507+
if (edge.to) {
508+
updateNodeOverrideSet(edge.to, edge.overrides)
509+
}
510+
}
511+
}
512+
513+
function updateNodeOverrideSetDueToEdgeRemoval (node: NodeClass, other: OverrideSet) {
514+
// If this edge's overrides isn't equal to this node's overrides, then removing it won't change newOverrideSet later.
515+
if (!node.overrides || !overrideSetsEqual(node.overrides, other)) {
516+
return false
517+
}
518+
let newOverrideSet
519+
for (const edge of node.edgesIn) {
520+
if (newOverrideSet) {
521+
newOverrideSet = findSpecificOverrideSet(edge.overrides, newOverrideSet)
522+
} else {
523+
newOverrideSet = edge.overrides
524+
}
525+
}
526+
if (overrideSetsEqual(node.overrides, newOverrideSet)) {
527+
return false
528+
}
529+
node.overrides = newOverrideSet
530+
if (node.overrides) {
531+
// Optimization: if there's any override set at all, then no non-extraneous node has an empty override set. So if we temporarily have no
532+
// override set (for example, we removed all the edges in), there's no use updating all the edges out right now. Let's just wait until
533+
// we have an actual override set later.
534+
recalculateOutEdgesOverrides(node)
535+
}
536+
return true
537+
}
538+
539+
// This logic isn't perfect either. When we have two edges in that have different override sets, then we have to decide which set is correct.
540+
// This function assumes the more specific override set is applicable, so if we have dependencies A->B->C and A->C
541+
// and an override set that specifies what happens for C under A->B, this will work even if the new A->C edge comes along and tries to change
542+
// the override set.
543+
// The strictly correct logic is not to allow two edges with different overrides to point to the same node, because even if this node can satisfy
544+
// both, one of its dependencies might need to be different depending on the edge leading to it.
545+
// However, this might cause a lot of duplication, because the conflict in the dependencies might never actually happen.
546+
function updateNodeOverrideSet (node: NodeClass, otherOverrideSet: OverrideSet | undefined) {
547+
if (!node.overrides) {
548+
// Assuming there are any overrides at all, the overrides field is never undefined for any node at the end state of the tree.
549+
// So if the new edge's overrides is undefined it will be updated later. So we can wait with updating the node's overrides field.
550+
if (!otherOverrideSet) {
551+
return false
552+
}
553+
node.overrides = otherOverrideSet
554+
recalculateOutEdgesOverrides(node)
555+
return true
556+
}
557+
const { overrides } = node
558+
if (overrideSetsEqual(overrides, otherOverrideSet)) {
559+
return false
560+
}
561+
const newOverrideSet = findSpecificOverrideSet(overrides, otherOverrideSet)
562+
if (newOverrideSet) {
563+
if (!overrideSetsEqual(overrides, newOverrideSet)) {
564+
node.overrides = newOverrideSet
565+
recalculateOutEdgesOverrides(node)
566+
return true
567+
}
568+
return false
569+
}
570+
// This is an error condition. We can only get here if the new override set is in conflict with the existing.
571+
}
572+
573+
function overrideSetsChildrenAreEqual(overrideSet: OverrideSet, other: OverrideSet) {
574+
const { children } = overrideSet
575+
const { children: otherChildren } = other
576+
if (children.size !== otherChildren.size) {
577+
return false
578+
}
579+
for (const key of children.keys()) {
580+
if (!otherChildren.has(key)) {
581+
return false
582+
}
583+
const child = <OverrideSet>children.get(key)
584+
const otherChild = <OverrideSet>otherChildren.get(key)
585+
if (child!.value !== otherChild!.value) {
586+
return false
587+
}
588+
if (!overrideSetsChildrenAreEqual(child, otherChild)) {
589+
return false
590+
}
591+
}
592+
return true
593+
}
594+
595+
function overrideSetsEqual (overrideSet: OverrideSet, other: OverrideSet | undefined) {
596+
if (overrideSet === other) {
597+
return true
598+
}
599+
if (!other) {
600+
return false
601+
}
602+
if (overrideSet.key !== other.key || overrideSet.value !== other.value) {
603+
return false
604+
}
605+
if (!overrideSetsChildrenAreEqual(overrideSet, other)) {
606+
return false
607+
}
608+
if (!overrideSet.parent) {
609+
return !other.parent
610+
}
611+
return overrideSetsEqual(overrideSet.parent, other.parent)
612+
}
567613

568614
// An edge in the dependency graph
569615
// Represents a dependency relationship of some kind

0 commit comments

Comments
 (0)