Home | History | Annotate | Download | only in types
      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 
      5 package types
      6 
      7 import (
      8 	"cmd/internal/obj"
      9 	"cmd/internal/objabi"
     10 	"fmt"
     11 	"sort"
     12 	"sync"
     13 )
     14 
     15 // pkgMap maps a package path to a package.
     16 var pkgMap = make(map[string]*Pkg)
     17 
     18 type 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 	Pathsym  *obj.LSym
     22 	Prefix   string // escaped path for use in symbol table
     23 	Imported bool   // export data of this package was parsed
     24 	Direct   bool   // imported directly
     25 	Syms     map[string]*Sym
     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.
     31 func 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 	p.Prefix = objabi.PathToPrefix(path)
     43 	p.Syms = make(map[string]*Sym)
     44 	pkgMap[path] = p
     45 
     46 	return p
     47 }
     48 
     49 // ImportedPkgList returns the list of directly imported packages.
     50 // The list is sorted by package path.
     51 func ImportedPkgList() []*Pkg {
     52 	var list []*Pkg
     53 	for _, p := range pkgMap {
     54 		if p.Direct {
     55 			list = append(list, p)
     56 		}
     57 	}
     58 	sort.Sort(byPath(list))
     59 	return list
     60 }
     61 
     62 type byPath []*Pkg
     63 
     64 func (a byPath) Len() int           { return len(a) }
     65 func (a byPath) Less(i, j int) bool { return a[i].Path < a[j].Path }
     66 func (a byPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
     67 
     68 var nopkg = &Pkg{
     69 	Syms: make(map[string]*Sym),
     70 }
     71 
     72 func (pkg *Pkg) Lookup(name string) *Sym {
     73 	s, _ := pkg.LookupOK(name)
     74 	return s
     75 }
     76 
     77 var InitSyms []*Sym
     78 
     79 // LookupOK looks up name in pkg and reports whether it previously existed.
     80 func (pkg *Pkg) LookupOK(name string) (s *Sym, existed bool) {
     81 	// TODO(gri) remove this check in favor of specialized lookup
     82 	if pkg == nil {
     83 		pkg = nopkg
     84 	}
     85 	if s := pkg.Syms[name]; s != nil {
     86 		return s, true
     87 	}
     88 
     89 	s = &Sym{
     90 		Name: name,
     91 		Pkg:  pkg,
     92 	}
     93 	if name == "init" {
     94 		InitSyms = append(InitSyms, s)
     95 	}
     96 	pkg.Syms[name] = s
     97 	return s, false
     98 }
     99 
    100 func (pkg *Pkg) LookupBytes(name []byte) *Sym {
    101 	// TODO(gri) remove this check in favor of specialized lookup
    102 	if pkg == nil {
    103 		pkg = nopkg
    104 	}
    105 	if s := pkg.Syms[string(name)]; s != nil {
    106 		return s
    107 	}
    108 	str := InternString(name)
    109 	return pkg.Lookup(str)
    110 }
    111 
    112 var (
    113 	internedStringsmu sync.Mutex // protects internedStrings
    114 	internedStrings   = map[string]string{}
    115 )
    116 
    117 func InternString(b []byte) string {
    118 	internedStringsmu.Lock()
    119 	s, ok := internedStrings[string(b)] // string(b) here doesn't allocate
    120 	if !ok {
    121 		s = string(b)
    122 		internedStrings[s] = s
    123 	}
    124 	internedStringsmu.Unlock()
    125 	return s
    126 }
    127 
    128 // CleanroomDo invokes f in an environment with with no preexisting packages.
    129 // For testing of import/export only.
    130 func CleanroomDo(f func()) {
    131 	saved := pkgMap
    132 	pkgMap = make(map[string]*Pkg)
    133 	f()
    134 	pkgMap = saved
    135 }
    136