@@ -17,6 +17,7 @@ package parser
1717import (
1818 "bufio"
1919 "bytes"
20+ "container/list"
2021 "fmt"
2122 "github.com/cloudwego/abcoder/lang/log"
2223 "go/ast"
@@ -53,18 +54,61 @@ func (c cache) Visited(val interface{}) bool {
5354 return ok
5455}
5556
57+ type cacheEntry struct {
58+ key string
59+ value bool
60+ }
61+
5662// PackageCache 缓存 importPath 是否是 system package
5763type PackageCache struct {
58- lock sync.RWMutex
59- cache map [string ]bool
64+ lock sync.Mutex
65+ cache map [string ]* list.Element
66+ lru * list.List
67+ lruCapacity int
6068}
6169
62- func NewPackageCache () * PackageCache {
70+ func NewPackageCache (lruCapacity int ) * PackageCache {
6371 return & PackageCache {
64- cache : make (map [string ]bool ),
72+ cache : make (map [string ]* list.Element ),
73+ lru : list .New (),
74+ lruCapacity : lruCapacity ,
6575 }
6676}
6777
78+ // Get retrieves a value from the cache.
79+ func (pc * PackageCache ) Get (key string ) (bool , bool ) {
80+ pc .lock .Lock ()
81+ defer pc .lock .Unlock ()
82+ if elem , ok := pc .cache [key ]; ok {
83+ pc .lru .MoveToFront (elem )
84+ return elem .Value .(* cacheEntry ).value , true
85+ }
86+ return false , false
87+ }
88+
89+ // Set adds a value to the cache.
90+ func (pc * PackageCache ) Set (key string , value bool ) {
91+ pc .lock .Lock ()
92+ defer pc .lock .Unlock ()
93+
94+ if elem , ok := pc .cache [key ]; ok {
95+ pc .lru .MoveToFront (elem )
96+ elem .Value .(* cacheEntry ).value = value
97+ return
98+ }
99+
100+ if pc .lru .Len () >= pc .lruCapacity {
101+ oldest := pc .lru .Back ()
102+ if oldest != nil {
103+ pc .lru .Remove (oldest )
104+ delete (pc .cache , oldest .Value .(* cacheEntry ).key )
105+ }
106+ }
107+
108+ elem := pc .lru .PushFront (& cacheEntry {key : key , value : value })
109+ pc .cache [key ] = elem
110+ }
111+
68112var (
69113 gorootOnce sync.Once
70114 detectedGoRoot string
@@ -81,7 +125,7 @@ func getGoRoot() (string, error) {
81125 cmd .Stderr = & stderr
82126 err := cmd .Run ()
83127 if err != nil {
84- log .Info ("'go env GOROOT' failed: %w , stderr: %s; \n `isSysPkg` will downgrade." , err , stderr .String ())
128+ log .Info ("'go env GOROOT' failed: %v , stderr: %s; \n `isSysPkg` will downgrade." , err , stderr .String ())
85129 gorootErr = fmt .Errorf ("'go env GOROOT' failed: %w, stderr: %s" , err , stderr .String ())
86130 return
87131 }
@@ -99,48 +143,27 @@ func getGoRoot() (string, error) {
99143
100144// IsStandardPackage 检查一个包是否为标准库,并使用内部缓存。
101145func (pc * PackageCache ) IsStandardPackage (path string ) bool {
146+ if isStd , found := pc .Get (path ); found {
147+ return isStd
148+ }
102149
103150 goRoot , err := getGoRoot ()
104151 // 当前环境找不到 go root,退化到最简单判断
152+ var isStd bool
105153 if err != nil || goRoot == "" {
106- return ! strings .Contains (strings .Split (path , "/" )[0 ], "." )
107- }
108-
109- pc .lock .RLock ()
110- isStd , found := pc .cache [path ]
111- pc .lock .RUnlock ()
112-
113- if found {
114- return isStd
115- }
116-
117- pc .lock .Lock ()
118- defer pc .lock .Unlock ()
119-
120- isStd , found = pc .cache [path ]
121- if found {
122- return isStd
123- }
124-
125- pkgPath := filepath .Join (goRoot , "src" , path )
126- stat , err := os .Stat (pkgPath )
127- if err != nil {
128- if os .IsNotExist (err ) {
129- isStd = false
130- } else {
131- log .Info ("IsStandardPackage: failed to get file stat for %s: %v" , pkgPath , err )
132- return false
133- }
154+ isStd = ! strings .Contains (strings .Split (path , "/" )[0 ], "." )
134155 } else {
135- isStd = stat .IsDir ()
156+ pkgPath := filepath .Join (goRoot , "src" , path )
157+ _ , err = os .Stat (pkgPath )
158+ isStd = ! os .IsNotExist (err )
136159 }
137160
138- pc .cache [path ] = isStd
139-
161+ pc .Set (path , isStd )
140162 return isStd
141163}
142164
143- var stdlibCache = NewPackageCache ()
165+ // stdlibCache 缓存 importPath 是否是 system package, 10000 个缓存
166+ var stdlibCache = NewPackageCache (10000 )
144167
145168func isSysPkg (importPath string ) bool {
146169 return stdlibCache .IsStandardPackage (importPath )
0 commit comments