1
0
mirror of https://github.com/golang/go synced 2024-10-01 16:08:33 -06:00

cmd/internal/ld: edit into more idiomatic Go code

Instead of reimplementing chained hash tables, just use maps.

Use bool instead of uint8 for variables only set to 0 or 1.

Fix parsing of `import foo "foo" // indirect` lines.  Previously, this
was treated as an import of package path `"foo" // indirect`, which
could result in the cycle-detection code failing to detect a cycle
because it would be treated as a separate package from `"foo"`.

Also, since there are theoretically multiple quoted forms for a
package path, use strconv.Unquote to normalize them.  Side benefit:
Unquote will complain if any trailing comments sneak back in.

Aside: For most Go archives, Go package data is only present in the
__.PKGDEF member, but unless -u is used, ldpkg is only called on the
_go_.6 member.  Consequently, importcycles is a no-op when -u isn't
used as it has no package data to inspect.

Change-Id: I7076cf91a66726a8d9c5676adfea13c5532001fa
Reviewed-on: https://go-review.googlesource.com/7002
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
Matthew Dempsky 2015-03-05 17:45:11 -08:00 committed by Rob Pike
parent b2f29511dd
commit 717cb74907

View File

@ -9,6 +9,7 @@ import (
"cmd/internal/obj" "cmd/internal/obj"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
) )
@ -37,42 +38,22 @@ func expandpkg(t0 string, pkg string) string {
* package import data * package import data
*/ */
type Import struct { type Import struct {
hash *Import // next in hash table prefix string // "type", "var", "func", "const"
prefix string // "type", "var", "func", "const"
name string name string
def string def string
file string file string
} }
const ( // importmap records type information about imported symbols to detect inconsistencies.
NIHASH = 1024 // Entries are keyed by qualified symbol name (e.g., "runtime.Callers" or "net/url.Error").
) var importmap = map[string]*Import{}
var ihash [NIHASH]*Import func lookupImport(name string) *Import {
if x, ok := importmap[name]; ok {
var nimport int return x
func hashstr(name string) int {
h := uint32(0)
for cp := name; cp != ""; cp = cp[1:] {
h = h*1119 + uint32(cp[0])
} }
h &= 0xffffff x := &Import{name: name}
return int(h) importmap[name] = x
}
func ilookup(name string) *Import {
h := hashstr(name) % NIHASH
for x := ihash[h]; x != nil; x = x.hash {
if x.name[0] == name[0] && x.name == name {
return x
}
}
x := new(Import)
x.name = name
x.hash = ihash[h]
ihash[h] = x
nimport++
return x return x
} }
@ -210,12 +191,10 @@ func loadpkgdata(file string, pkg string, data string) {
var prefix string var prefix string
var name string var name string
var def string var def string
var x *Import
file = file
p := data p := data
for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 { for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 {
x = ilookup(name) x := lookupImport(name)
if x.prefix == "" { if x.prefix == "" {
x.prefix = prefix x.prefix = prefix
x.def = def x.def = def
@ -235,8 +214,6 @@ func loadpkgdata(file string, pkg string, data string) {
} }
func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int { func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int {
var prefix string
// skip white space // skip white space
p := *pp p := *pp
@ -249,7 +226,7 @@ loop:
} }
// prefix: (var|type|func|const) // prefix: (var|type|func|const)
prefix = p prefix := p
if len(p) < 7 { if len(p) < 7 {
return -1 return -1
@ -268,7 +245,7 @@ loop:
p = p[1:] p = p[1:]
} }
p = p[1:] p = p[1:]
name := p line := p
for len(p) > 0 && p[0] != '\n' { for len(p) > 0 && p[0] != '\n' {
p = p[1:] p = p[1:]
} }
@ -277,9 +254,16 @@ loop:
nerrors++ nerrors++
return -1 return -1
} }
name = name[:len(name)-len(p)] line = line[:len(line)-len(p)]
line = strings.TrimSuffix(line, " // indirect")
path, err := strconv.Unquote(line)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %s: confused in import path: %q\n", os.Args[0], file, line)
nerrors++
return -1
}
p = p[1:] p = p[1:]
imported(pkg, name) imported(pkg, path)
goto loop goto loop
} else { } else {
fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix) fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix)
@ -753,66 +737,60 @@ func addexport() {
} }
type Pkg struct { type Pkg struct {
mark uint8 mark bool
checked uint8 checked bool
next *Pkg path string
path_ string
impby []*Pkg impby []*Pkg
all *Pkg
} }
var phash [1024]*Pkg var (
// pkgmap records the imported-by relationship between packages.
// Entries are keyed by package path (e.g., "runtime" or "net/url").
pkgmap = map[string]*Pkg{}
var pkgall *Pkg pkgall []*Pkg
)
func getpkg(path_ string) *Pkg { func lookupPkg(path string) *Pkg {
h := hashstr(path_) % len(phash) if p, ok := pkgmap[path]; ok {
for p := phash[h]; p != nil; p = p.next { return p
if p.path_ == path_ {
return p
}
} }
p := new(Pkg) p := &Pkg{path: path}
p.path_ = path_ pkgmap[path] = p
p.next = phash[h] pkgall = append(pkgall, p)
phash[h] = p
p.all = pkgall
pkgall = p
return p return p
} }
func imported(pkg string, import_ string) { // imported records that package pkg imports package imp.
func imported(pkg, imp string) {
// everyone imports runtime, even runtime. // everyone imports runtime, even runtime.
if import_ == "\"runtime\"" { if imp == "runtime" {
return return
} }
pkg = fmt.Sprintf("%q", pkg) // turn pkg path into quoted form, freed below p := lookupPkg(pkg)
p := getpkg(pkg) i := lookupPkg(imp)
i := getpkg(import_)
i.impby = append(i.impby, p) i.impby = append(i.impby, p)
} }
func cycle(p *Pkg) *Pkg { func (p *Pkg) cycle() *Pkg {
if p.checked != 0 { if p.checked {
return nil return nil
} }
if p.mark != 0 { if p.mark {
nerrors++ nerrors++
fmt.Printf("import cycle:\n") fmt.Printf("import cycle:\n")
fmt.Printf("\t%s\n", p.path_) fmt.Printf("\t%s\n", p.path)
return p return p
} }
p.mark = 1 p.mark = true
var bad *Pkg for _, q := range p.impby {
for i := 0; i < len(p.impby); i++ { if bad := q.cycle(); bad != nil {
bad = cycle(p.impby[i]) p.mark = false
if bad != nil { p.checked = true
p.mark = 0 fmt.Printf("\timports %s\n", p.path)
p.checked = 1
fmt.Printf("\timports %s\n", p.path_)
if bad == p { if bad == p {
return nil return nil
} }
@ -820,14 +798,14 @@ func cycle(p *Pkg) *Pkg {
} }
} }
p.checked = 1 p.checked = true
p.mark = 0 p.mark = false
return nil return nil
} }
func importcycles() { func importcycles() {
for p := pkgall; p != nil; p = p.all { for _, p := range pkgall {
cycle(p) p.cycle()
} }
} }