Skip to content

Commit

Permalink
perf(linker): Fixes brute force chunk cycle detection (#3069)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruiconti authored Apr 22, 2023
1 parent dbefad5 commit 9092a1b
Showing 1 changed file with 28 additions and 15 deletions.
43 changes: 28 additions & 15 deletions internal/linker/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,28 +452,41 @@ func (c *linkerContext) mangleProps(mangleCache map[string]interface{}) {
// can cause initialization bugs. So let's forbid these cycles for now to guard
// against code splitting bugs that could cause us to generate buggy chunks.
func (c *linkerContext) enforceNoCyclicChunkImports() {
var validate func(int, []int)
validate = func(chunkIndex int, path []int) {
for _, otherChunkIndex := range path {
if chunkIndex == otherChunkIndex {
c.log.AddError(nil, logger.Range{}, "Internal error: generated chunks contain a circular import")
return
}
var validate func(int, map[int]int) bool

// DFS memoization with 3-colors, more space efficient
// 0: white (unvisited), 1: gray (visiting), 2: black (visited)
colors := make(map[int]int)
validate = func(chunkIndex int, colors map[int]int) bool {
if colors[chunkIndex] == 1 {
c.log.AddError(nil, logger.Range{}, "Internal error: generated chunks contain a circular import")
return true
}
path = append(path, chunkIndex)

if colors[chunkIndex] == 2 {
return false
}

colors[chunkIndex] = 1

for _, chunkImport := range c.chunks[chunkIndex].crossChunkImports {
// Ignore cycles caused by dynamic "import()" expressions. These are fine
// because they don't necessarily cause initialization order issues and
// they don't indicate a bug in our chunk generation algorithm. They arise
// normally in real code (e.g. two files that import each other).
if chunkImport.importKind != ast.ImportDynamic {
validate(int(chunkImport.chunkIndex), path)

// Recursively validate otherChunkIndex
if validate(int(chunkImport.chunkIndex), colors) {
return true
}
}
}

colors[chunkIndex] = 2
return false
}
path := make([]int, 0, len(c.chunks))

for i := range c.chunks {
validate(i, path)
if validate(i, colors) {
break
}
}
}

Expand Down

0 comments on commit 9092a1b

Please sign in to comment.