1package main 2 3import ( 4 "bytes" 5 "fmt" 6 "go/ast" 7 "go/token" 8 "go/types" 9 "log" 10 "os" 11 "strings" 12 13 "github.com/visualfc/gocode/internal/gcexportdata" 14) 15 16type package_parser interface { 17 parse_export(callback func(pkg string, decl ast.Decl)) 18} 19 20//------------------------------------------------------------------------- 21// package_file_cache 22// 23// Structure that represents a cache for an imported pacakge. In other words 24// these are the contents of an archive (*.a) file. 25//------------------------------------------------------------------------- 26 27type package_file_cache struct { 28 name string // file name 29 import_name string 30 vendor_name string 31 mtime int64 32 defalias string 33 34 scope *scope 35 main *decl // package declaration 36 others map[string]*decl 37} 38 39func new_package_file_cache(absname, name string, vname string) *package_file_cache { 40 m := new(package_file_cache) 41 m.name = absname 42 m.import_name = name 43 m.vendor_name = vname 44 m.mtime = 0 45 m.defalias = "" 46 return m 47} 48 49// Creates a cache that stays in cache forever. Useful for built-in packages. 50func new_package_file_cache_forever(name, defalias string) *package_file_cache { 51 m := new(package_file_cache) 52 m.name = name 53 m.mtime = -1 54 m.defalias = defalias 55 return m 56} 57 58func (m *package_file_cache) find_file() string { 59 if file_exists(m.name) { 60 return m.name 61 } 62 63 n := len(m.name) 64 filename := m.name[:n-1] + "6" 65 if file_exists(filename) { 66 return filename 67 } 68 69 filename = m.name[:n-1] + "8" 70 if file_exists(filename) { 71 return filename 72 } 73 74 filename = m.name[:n-1] + "5" 75 if file_exists(filename) { 76 return filename 77 } 78 return m.name 79} 80 81func (m *package_file_cache) update_cache(c *auto_complete_context) { 82 if m.mtime == -1 { 83 return 84 } 85 defer func() { 86 if err := recover(); err != nil { 87 log.Println("update_cache recover error:", err) 88 } 89 }() 90 91 import_path := m.import_name 92 if m.vendor_name != "" { 93 import_path = m.vendor_name 94 } 95 if pkg := c.walker.Imported[import_path]; pkg != nil { 96 if pkg.Name() == "" { 97 log.Println("error parser", import_path) 98 return 99 } 100 if chk, ok := c.walker.ImportedFilesCheck[import_path]; ok { 101 if m.mtime == chk.ModTime { 102 return 103 } 104 m.mtime = chk.ModTime 105 } 106 m.process_package_types(c, pkg) 107 return 108 } 109 110 fname := m.find_file() 111 stat, err := os.Stat(fname) 112 113 if err != nil { 114 m.process_package_data(c, nil, true) 115 return 116 } 117 statmtime := stat.ModTime().UnixNano() 118 if m.mtime != statmtime { 119 m.mtime = statmtime 120 data, err := file_reader.read_file(fname) 121 if err != nil { 122 return 123 } 124 m.process_package_data(c, data, false) 125 } 126} 127 128func (m *package_file_cache) process_package_types(c *auto_complete_context, pkg *types.Package) { 129 m.scope = new_named_scope(g_universe_scope, m.name) 130 131 // main package 132 m.main = new_decl(m.name, decl_package, nil) 133 // create map for other packages 134 m.others = make(map[string]*decl) 135 136 var pp package_parser 137 fset := token.NewFileSet() 138 var buf bytes.Buffer 139 gcexportdata.Write(&buf, fset, pkg) 140 var p gc_bin_parser 141 p.init(buf.Bytes(), m) 142 pp = &p 143 144 prefix := "!" + m.name + "!" 145 pp.parse_export(func(pkg string, decl ast.Decl) { 146 anonymify_ast(decl, decl_foreign, m.scope) 147 if pkg == "" || strings.HasPrefix(pkg, prefix) { 148 // main package 149 add_ast_decl_to_package(m.main, decl, m.scope) 150 } else { 151 // others 152 if _, ok := m.others[pkg]; !ok { 153 m.others[pkg] = new_decl(pkg, decl_package, nil) 154 } 155 add_ast_decl_to_package(m.others[pkg], decl, m.scope) 156 } 157 }) 158 159 // hack, add ourselves to the package scope 160 mainName := "!" + m.name + "!" + m.defalias 161 m.add_package_to_scope(mainName, m.name) 162 163 // replace dummy package decls in package scope to actual packages 164 for key := range m.scope.entities { 165 if !strings.HasPrefix(key, "!") { 166 continue 167 } 168 pkg, ok := m.others[key] 169 if !ok && key == mainName { 170 pkg = m.main 171 } 172 m.scope.replace_decl(key, pkg) 173 } 174} 175 176func (m *package_file_cache) process_package_data(c *auto_complete_context, data []byte, source bool) { 177 m.scope = new_named_scope(g_universe_scope, m.name) 178 179 // main package 180 m.main = new_decl(m.name, decl_package, nil) 181 // create map for other packages 182 m.others = make(map[string]*decl) 183 184 var pp package_parser 185 if source { 186 var tp types_parser 187 var srcDir string 188 importPath := m.import_name 189 if m.vendor_name != "" { 190 importPath = m.vendor_name 191 } 192 tp.initSource(m.import_name, importPath, srcDir, m, c) 193 data = tp.exportData() 194 if *g_debug { 195 log.Printf("parser source %q %q\n", importPath, srcDir) 196 } 197 if data == nil { 198 log.Println("error parser data source", importPath) 199 return 200 } 201 var p gc_bin_parser 202 p.init(data, m) 203 pp = &p 204 } else { 205 i := bytes.Index(data, []byte{'\n', '$', '$'}) 206 if i == -1 { 207 panic(fmt.Sprintf("Can't find the import section in the package file %s", m.name)) 208 } 209 offset := i + len("\n$$") 210 if data[offset] == 'B' { 211 // binary format, skip 'B\n' 212 //data = data[2:] 213 if data[offset+2] == 'i' { 214 var tp types_parser 215 tp.initData(m.import_name, data, m, c) 216 data = tp.exportData() 217 if data == nil { 218 log.Println("error parser data binary", m.import_name) 219 return 220 } 221 } else { 222 data = data[offset+2:] 223 } 224 var p gc_bin_parser 225 p.init(data, m) 226 pp = &p 227 } else { 228 data = data[offset:] 229 // textual format, find the beginning of the package clause 230 i := bytes.Index(data, []byte{'p', 'a', 'c', 'k', 'a', 'g', 'e'}) 231 if i == -1 { 232 panic("Can't find the package clause") 233 } 234 data = data[i:] 235 236 var p gc_parser 237 p.init(data, m) 238 pp = &p 239 } 240 } 241 242 prefix := "!" + m.name + "!" 243 pp.parse_export(func(pkg string, decl ast.Decl) { 244 anonymify_ast(decl, decl_foreign, m.scope) 245 if pkg == "" || strings.HasPrefix(pkg, prefix) { 246 // main package 247 add_ast_decl_to_package(m.main, decl, m.scope) 248 } else { 249 // others 250 if _, ok := m.others[pkg]; !ok { 251 m.others[pkg] = new_decl(pkg, decl_package, nil) 252 } 253 add_ast_decl_to_package(m.others[pkg], decl, m.scope) 254 } 255 }) 256 257 // hack, add ourselves to the package scope 258 mainName := "!" + m.name + "!" + m.defalias 259 m.add_package_to_scope(mainName, m.name) 260 261 // replace dummy package decls in package scope to actual packages 262 for key := range m.scope.entities { 263 if !strings.HasPrefix(key, "!") { 264 continue 265 } 266 pkg, ok := m.others[key] 267 if !ok && key == mainName { 268 pkg = m.main 269 } 270 m.scope.replace_decl(key, pkg) 271 } 272} 273 274func (m *package_file_cache) add_package_to_scope(alias, realname string) { 275 d := new_decl(realname, decl_package, nil) 276 m.scope.add_decl(alias, d) 277} 278 279func add_ast_decl_to_package(pkg *decl, decl ast.Decl, scope *scope) { 280 foreach_decl(decl, func(data *foreach_decl_struct) { 281 class := ast_decl_class(data.decl) 282 for i, name := range data.names { 283 typ, v, vi := data.type_value_index(i) 284 285 d := new_decl_full(name.Name, class, decl_foreign|ast_decl_flags(data.decl), typ, v, vi, scope) 286 if d == nil { 287 continue 288 } 289 290 if !name.IsExported() && d.class != decl_type { 291 return 292 } 293 294 methodof := method_of(data.decl) 295 if methodof != "" { 296 decl := pkg.find_child(methodof) 297 if decl != nil { 298 decl.add_child(d) 299 } else { 300 decl = new_decl(methodof, decl_methods_stub, scope) 301 decl.add_child(d) 302 pkg.add_child(decl) 303 } 304 } else { 305 decl := pkg.find_child(d.name) 306 if decl != nil { 307 decl.expand_or_replace(d) 308 } else { 309 pkg.add_child(d) 310 } 311 } 312 } 313 }) 314} 315 316//------------------------------------------------------------------------- 317// package_cache 318//------------------------------------------------------------------------- 319 320type package_cache map[string]*package_file_cache 321 322func new_package_cache() package_cache { 323 m := make(package_cache) 324 325 // add built-in "unsafe" package 326 m.add_builtin_unsafe_package() 327 328 return m 329} 330 331// Function fills 'ps' set with packages from 'packages' import information. 332// In case if package is not in the cache, it creates one and adds one to the cache. 333func (c package_cache) append_packages(ps map[string]*package_file_cache, pkgs []package_import) { 334 for _, m := range pkgs { 335 if _, ok := ps[m.abspath]; ok { 336 continue 337 } 338 339 if mod, ok := c[m.abspath]; ok { 340 ps[m.abspath] = mod 341 } else { 342 mod = new_package_file_cache(m.abspath, m.path, m.vpath) 343 ps[m.abspath] = mod 344 c[m.abspath] = mod 345 } 346 } 347} 348 349var g_builtin_unsafe_package = []byte(` 350import 351$$ 352package unsafe 353 type @"".Pointer uintptr 354 func @"".Offsetof (? any) uintptr 355 func @"".Sizeof (? any) uintptr 356 func @"".Alignof (? any) uintptr 357 358$$ 359`) 360 361func (c package_cache) add_builtin_unsafe_package() { 362 pkg := new_package_file_cache_forever("unsafe", "unsafe") 363 pkg.process_package_data(nil, g_builtin_unsafe_package, false) 364 c["unsafe"] = pkg 365} 366