Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

106 lines
2.8 KiB
Go

package indexer
import (
"os"
"github.com/zzet/gortex/internal/contracts"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
)
// bodyFactsCache holds parsed body facts for the duration of one
// post-pass invocation. Keyed by the handler's FilePath; trees are
// parsed lazily on first lookup and released by Close. Per-handler
// BodyFacts are memoised by SymbolID inside cacheEntry so a router
// referencing many handlers in the same file only parses that file
// once.
type bodyFactsCache struct {
idx *Indexer
entries map[string]*bfCacheEntry // FilePath → entry
}
type bfCacheEntry struct {
tree *parser.ParseTree
src []byte
lang string
facts map[string]contracts.BodyFacts // SymbolID → facts
}
// newBodyFactsCache creates a new cache scoped to one resolver pass.
func newBodyFactsCache(idx *Indexer) *bodyFactsCache {
return &bodyFactsCache{idx: idx, entries: map[string]*bfCacheEntry{}}
}
// For returns BodyFacts for the handler graph node. Returns nopBodyFacts
// when the handler is unknown, the file can't be read, or the language
// has no registered factory.
func (c *bodyFactsCache) For(handler *graph.Node) contracts.BodyFacts {
if handler == nil {
return nopFacts
}
entry := c.entryFor(handler)
if entry == nil {
return nopFacts
}
if bf, ok := entry.facts[handler.ID]; ok {
return bf
}
bf := contracts.MakeBodyFacts(entry.tree, handler)
entry.facts[handler.ID] = bf
return bf
}
func (c *bodyFactsCache) entryFor(handler *graph.Node) *bfCacheEntry {
if entry, ok := c.entries[handler.FilePath]; ok {
return entry
}
src := c.readFile(handler)
if len(src) == 0 {
c.entries[handler.FilePath] = nil
return nil
}
lang := handler.Language
if lang == "" {
lang = detectLangFromPath(handler.FilePath)
}
tree := contracts.ParseTreeForLang(lang, src)
entry := &bfCacheEntry{
tree: tree,
src: src,
lang: lang,
facts: map[string]contracts.BodyFacts{},
}
c.entries[handler.FilePath] = entry
return entry
}
// readFile resolves the handler's graph path to disk and reads its
// bytes. Mirrors the path-resolution dance in resolveProviderHandlers
// (strip repo prefix, join with rootPath).
func (c *bodyFactsCache) readFile(handler *graph.Node) []byte {
disk := c.idx.ResolveFilePath(handler.FilePath)
if disk == "" {
return nil
}
data, err := os.ReadFile(disk)
if err != nil {
return nil
}
return data
}
// Close releases every cached parse tree. Safe to call once.
func (c *bodyFactsCache) Close() {
for _, e := range c.entries {
if e == nil {
continue
}
e.tree.Release()
}
c.entries = nil
}
// nopFacts is the singleton no-op returned when no facts are available.
// Cheaper than allocating a new contracts.nopBodyFacts each call.
var nopFacts = contracts.MakeBodyFacts(nil, nil)