1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package types
6
7import (
8	"cmd/internal/obj"
9	"cmd/internal/objabi"
10	"fmt"
11	"strconv"
12	"sync"
13)
14
15// pkgMap maps a package path to a package.
16var pkgMap = make(map[string]*Pkg)
17
18type Pkg struct {
19	Path    string // string literal used in import statement, e.g. "runtime/internal/sys"
20	Name    string // package name, e.g. "sys"
21	Prefix  string // escaped path for use in symbol table
22	Syms    map[string]*Sym
23	Pathsym *obj.LSym
24
25	Direct bool // imported directly
26}
27
28// NewPkg returns a new Pkg for the given package path and name.
29// Unless name is the empty string, if the package exists already,
30// the existing package name and the provided name must match.
31func NewPkg(path, name string) *Pkg {
32	if p := pkgMap[path]; p != nil {
33		if name != "" && p.Name != name {
34			panic(fmt.Sprintf("conflicting package names %s and %s for path %q", p.Name, name, path))
35		}
36		return p
37	}
38
39	p := new(Pkg)
40	p.Path = path
41	p.Name = name
42	if path == "go.shape" {
43		// Don't escape "go.shape", since it's not needed (it's a builtin
44		// package), and we don't want escape codes showing up in shape type
45		// names, which also appear in names of function/method
46		// instantiations.
47		p.Prefix = path
48	} else {
49		p.Prefix = objabi.PathToPrefix(path)
50	}
51	p.Syms = make(map[string]*Sym)
52	pkgMap[path] = p
53
54	return p
55}
56
57func PkgMap() map[string]*Pkg {
58	return pkgMap
59}
60
61var nopkg = &Pkg{
62	Syms: make(map[string]*Sym),
63}
64
65func (pkg *Pkg) Lookup(name string) *Sym {
66	s, _ := pkg.LookupOK(name)
67	return s
68}
69
70// LookupOK looks up name in pkg and reports whether it previously existed.
71func (pkg *Pkg) LookupOK(name string) (s *Sym, existed bool) {
72	// TODO(gri) remove this check in favor of specialized lookup
73	if pkg == nil {
74		pkg = nopkg
75	}
76	if s := pkg.Syms[name]; s != nil {
77		return s, true
78	}
79
80	s = &Sym{
81		Name: name,
82		Pkg:  pkg,
83	}
84	pkg.Syms[name] = s
85	return s, false
86}
87
88func (pkg *Pkg) LookupBytes(name []byte) *Sym {
89	// TODO(gri) remove this check in favor of specialized lookup
90	if pkg == nil {
91		pkg = nopkg
92	}
93	if s := pkg.Syms[string(name)]; s != nil {
94		return s
95	}
96	str := InternString(name)
97	return pkg.Lookup(str)
98}
99
100// LookupNum looks up the symbol starting with prefix and ending with
101// the decimal n. If prefix is too long, LookupNum panics.
102func (pkg *Pkg) LookupNum(prefix string, n int) *Sym {
103	var buf [20]byte // plenty long enough for all current users
104	copy(buf[:], prefix)
105	b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10)
106	return pkg.LookupBytes(b)
107}
108
109// Selector looks up a selector identifier.
110func (pkg *Pkg) Selector(name string) *Sym {
111	if IsExported(name) {
112		pkg = LocalPkg
113	}
114	return pkg.Lookup(name)
115}
116
117var (
118	internedStringsmu sync.Mutex // protects internedStrings
119	internedStrings   = map[string]string{}
120)
121
122func InternString(b []byte) string {
123	internedStringsmu.Lock()
124	s, ok := internedStrings[string(b)] // string(b) here doesn't allocate
125	if !ok {
126		s = string(b)
127		internedStrings[s] = s
128	}
129	internedStringsmu.Unlock()
130	return s
131}
132