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