Skip to content

Commit 9092a1b

Browse files
authored
perf(linker): Fixes brute force chunk cycle detection (#3069)
1 parent dbefad5 commit 9092a1b

File tree

1 file changed

+28
-15
lines changed

1 file changed

+28
-15
lines changed

internal/linker/linker.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -452,28 +452,41 @@ func (c *linkerContext) mangleProps(mangleCache map[string]interface{}) {
452452
// can cause initialization bugs. So let's forbid these cycles for now to guard
453453
// against code splitting bugs that could cause us to generate buggy chunks.
454454
func (c *linkerContext) enforceNoCyclicChunkImports() {
455-
var validate func(int, []int)
456-
validate = func(chunkIndex int, path []int) {
457-
for _, otherChunkIndex := range path {
458-
if chunkIndex == otherChunkIndex {
459-
c.log.AddError(nil, logger.Range{}, "Internal error: generated chunks contain a circular import")
460-
return
461-
}
455+
var validate func(int, map[int]int) bool
456+
457+
// DFS memoization with 3-colors, more space efficient
458+
// 0: white (unvisited), 1: gray (visiting), 2: black (visited)
459+
colors := make(map[int]int)
460+
validate = func(chunkIndex int, colors map[int]int) bool {
461+
if colors[chunkIndex] == 1 {
462+
c.log.AddError(nil, logger.Range{}, "Internal error: generated chunks contain a circular import")
463+
return true
462464
}
463-
path = append(path, chunkIndex)
465+
466+
if colors[chunkIndex] == 2 {
467+
return false
468+
}
469+
470+
colors[chunkIndex] = 1
471+
464472
for _, chunkImport := range c.chunks[chunkIndex].crossChunkImports {
465-
// Ignore cycles caused by dynamic "import()" expressions. These are fine
466-
// because they don't necessarily cause initialization order issues and
467-
// they don't indicate a bug in our chunk generation algorithm. They arise
468-
// normally in real code (e.g. two files that import each other).
469473
if chunkImport.importKind != ast.ImportDynamic {
470-
validate(int(chunkImport.chunkIndex), path)
474+
475+
// Recursively validate otherChunkIndex
476+
if validate(int(chunkImport.chunkIndex), colors) {
477+
return true
478+
}
471479
}
472480
}
481+
482+
colors[chunkIndex] = 2
483+
return false
473484
}
474-
path := make([]int, 0, len(c.chunks))
485+
475486
for i := range c.chunks {
476-
validate(i, path)
487+
if validate(i, colors) {
488+
break
489+
}
477490
}
478491
}
479492

0 commit comments

Comments
 (0)