Merge "golang.org/x/tools: Update to release branch of Go 1.6"
diff --git a/go/src/golang.org/x/tools/README.google b/go/src/golang.org/x/tools/README.google
index 11e6893..daf3b49 100644
--- a/go/src/golang.org/x/tools/README.google
+++ b/go/src/golang.org/x/tools/README.google
@@ -1,5 +1,5 @@
-URL: https://go.googlesource.com/tools/+archive/b60a44a905945100f46b6fc9184df866dbccf955.tar.gz
-Version: b60a44a905945100f46b6fc9184df866dbccf955
+URL: https://go.googlesource.com/tools/+archive/c887be1b2ebd11663d4bf2fbca508c449172339e.tar.gz
+Version: c887be1b2ebd11663d4bf2fbca508c449172339e
License: New BSD
License File: LICENSE
@@ -7,6 +7,4 @@
Packages and tools supporting the Go programming language.
Local Modifications:
-Whitelisting Vanadium named slice types for go vet checks.
-Removed two test cases from go/vcs/vcs_test.go that fail now that
-code.google.com is in archive mode.
+- Applied https://go-review.googlesource.com/#/c/19189/
diff --git a/go/src/golang.org/x/tools/cmd/bundle/main.go b/go/src/golang.org/x/tools/cmd/bundle/main.go
new file mode 100644
index 0000000..f0051fe
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/bundle/main.go
@@ -0,0 +1,222 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
+// The bundle command concatenates the source files of a package,
+// renaming package-level names by adding a prefix and renaming
+// identifiers as needed to preserve referential integrity.
+//
+// Example:
+// $ bundle golang.org/x/net/http2 net/http http2
+//
+// The command above prints a single file containing the code of
+// golang.org/x/net/http2, suitable for inclusion in package net/http,
+// in which toplevel names have been prefixed with "http2".
+//
+// Assumptions:
+// - no file in the package imports "C", that is, uses cgo.
+// - no file in the package has GOOS or GOARCH build tags or file names.
+// - comments associated with the package or import declarations,
+// may be discarded, as may comments associated with no top-level
+// declaration at all.
+// - neither the original package nor the destination package contains
+// any identifiers starting with the designated prefix.
+// (This allows us to avoid various conflict checks.)
+// - there are no renaming imports.
+// - test files are ignored.
+// - none of the renamed identifiers is significant
+// to reflection-based logic.
+//
+// Only package-level var, func, const, and type objects are renamed,
+// and embedded fields of renamed types. No methods are renamed, so we
+// needn't worry about preserving interface satisfaction.
+//
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/tools/go/loader"
+)
+
+func main() {
+ log.SetPrefix("bundle: ")
+ log.SetFlags(0)
+
+ flag.Parse()
+ args := flag.Args()
+ if len(args) != 3 {
+ log.Fatal(`Usage: bundle package dest prefix
+
+Arguments:
+ package is the import path of the package to concatenate.
+ dest is the import path of the package in which the resulting file will reside.
+ prefix is the string to attach to all renamed identifiers.
+`)
+ }
+ initialPkg, dest, prefix := args[0], args[1], args[2]
+
+ if err := bundle(os.Stdout, initialPkg, dest, prefix); err != nil {
+ log.Fatal(err)
+ }
+}
+
+var ctxt = &build.Default
+
+func bundle(w io.Writer, initialPkg, dest, prefix string) error {
+ // Load the initial package.
+ conf := loader.Config{ParserMode: parser.ParseComments, Build: ctxt}
+ conf.TypeCheckFuncBodies = func(p string) bool { return p == initialPkg }
+ conf.Import(initialPkg)
+
+ lprog, err := conf.Load()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ info := lprog.Package(initialPkg)
+
+ objsToUpdate := make(map[types.Object]bool)
+ var rename func(from types.Object)
+ rename = func(from types.Object) {
+ if !objsToUpdate[from] {
+ objsToUpdate[from] = true
+
+ // Renaming a type that is used as an embedded field
+ // requires renaming the field too. e.g.
+ // type T int // if we rename this to U..
+ // var s struct {T}
+ // print(s.T) // ...this must change too
+ if _, ok := from.(*types.TypeName); ok {
+ for id, obj := range info.Uses {
+ if obj == from {
+ if field := info.Defs[id]; field != nil {
+ rename(field)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Rename each package-level object.
+ scope := info.Pkg.Scope()
+ for _, name := range scope.Names() {
+ rename(scope.Lookup(name))
+ }
+
+ var out bytes.Buffer
+
+ fmt.Fprintf(&out, "// Code generated by golang.org/x/tools/cmd/bundle command:\n")
+ fmt.Fprintf(&out, "// $ bundle %s %s %s\n\n", initialPkg, dest, prefix)
+
+ // Concatenate package comments from all files...
+ for _, f := range info.Files {
+ if doc := f.Doc.Text(); strings.TrimSpace(doc) != "" {
+ for _, line := range strings.Split(doc, "\n") {
+ fmt.Fprintf(&out, "// %s\n", line)
+ }
+ }
+ }
+ // ...but don't let them become the actual package comment.
+ fmt.Fprintln(&out)
+
+ // TODO(adonovan): don't assume pkg.name == basename(pkg.path).
+ fmt.Fprintf(&out, "package %s\n\n", filepath.Base(dest))
+
+ // Print a single declaration that imports all necessary packages.
+ // TODO(adonovan):
+ // - support renaming imports.
+ // - preserve comments from the original import declarations.
+ for _, f := range info.Files {
+ for _, imp := range f.Imports {
+ if imp.Name != nil {
+ log.Fatalf("%s: renaming imports not supported",
+ lprog.Fset.Position(imp.Pos()))
+ }
+ }
+ }
+ fmt.Fprintln(&out, "import (")
+ for _, p := range info.Pkg.Imports() {
+ if p.Path() == dest {
+ continue
+ }
+ fmt.Fprintf(&out, "\t%q\n", p.Path())
+ }
+ fmt.Fprintln(&out, ")\n")
+
+ // Modify and print each file.
+ for _, f := range info.Files {
+ // Update renamed identifiers.
+ for id, obj := range info.Defs {
+ if objsToUpdate[obj] {
+ id.Name = prefix + obj.Name()
+ }
+ }
+ for id, obj := range info.Uses {
+ if objsToUpdate[obj] {
+ id.Name = prefix + obj.Name()
+ }
+ }
+
+ // For each qualified identifier that refers to the
+ // destination package, remove the qualifier.
+ // The "@@@." strings are removed in postprocessing.
+ ast.Inspect(f, func(n ast.Node) bool {
+ if sel, ok := n.(*ast.SelectorExpr); ok {
+ if id, ok := sel.X.(*ast.Ident); ok {
+ if obj, ok := info.Uses[id].(*types.PkgName); ok {
+ if obj.Imported().Path() == dest {
+ id.Name = "@@@"
+ }
+ }
+ }
+ }
+ return true
+ })
+
+ // Pretty-print package-level declarations.
+ // but no package or import declarations.
+ //
+ // TODO(adonovan): this may cause loss of comments
+ // preceding or associated with the package or import
+ // declarations or not associated with any declaration.
+ // Check.
+ var buf bytes.Buffer
+ for _, decl := range f.Decls {
+ if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
+ continue
+ }
+ buf.Reset()
+ format.Node(&buf, lprog.Fset, decl)
+ // Remove each "@@@." in the output.
+ // TODO(adonovan): not hygienic.
+ out.Write(bytes.Replace(buf.Bytes(), []byte("@@@."), nil, -1))
+ out.WriteString("\n\n")
+ }
+ }
+
+ // Now format the entire thing.
+ result, err := format.Source(out.Bytes())
+ if err != nil {
+ log.Fatalf("formatting failed: %v", err)
+ }
+
+ _, err = w.Write(result)
+ return err
+}
diff --git a/go/src/golang.org/x/tools/cmd/bundle/main_test.go b/go/src/golang.org/x/tools/cmd/bundle/main_test.go
new file mode 100644
index 0000000..b4d0810
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/bundle/main_test.go
@@ -0,0 +1,66 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os/exec"
+ "runtime"
+ "testing"
+
+ "golang.org/x/tools/go/buildutil"
+)
+
+func TestBundle(t *testing.T) {
+ load := func(name string) string {
+ data, err := ioutil.ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return string(data)
+ }
+
+ ctxt = buildutil.FakeContext(map[string]map[string]string{
+ "initial": {
+ "a.go": load("testdata/src/initial/a.go"),
+ "b.go": load("testdata/src/initial/b.go"),
+ },
+ "fmt": {
+ "print.go": `package fmt; func Println(...interface{})`,
+ },
+ })
+
+ var out bytes.Buffer
+ if err := bundle(&out, "initial", "dest", "prefix"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := out.String(), load("testdata/out.golden"); got != want {
+ t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want)
+
+ if err := ioutil.WriteFile("testdata/out.got", out.Bytes(), 0644); err != nil {
+ t.Fatal(err)
+ }
+ t.Log(diff("testdata/out.got", "testdata/out.golden"))
+ }
+}
+
+func diff(a, b string) string {
+ var cmd *exec.Cmd
+ switch runtime.GOOS {
+ case "plan9":
+ cmd = exec.Command("/bin/diff", "-c", a, b)
+ default:
+ cmd = exec.Command("/usr/bin/diff", "-u", a, b)
+ }
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Stderr = &out
+ cmd.Run() // nonzero exit is expected
+ if out.Len() == 0 {
+ return "(failed to compute diff)"
+ }
+ return out.String()
+}
diff --git a/go/src/golang.org/x/tools/cmd/bundle/testdata/out.golden b/go/src/golang.org/x/tools/cmd/bundle/testdata/out.golden
new file mode 100644
index 0000000..5153d3f
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/bundle/testdata/out.golden
@@ -0,0 +1,31 @@
+// Code generated by golang.org/x/tools/cmd/bundle command:
+// $ bundle initial dest prefix
+
+// The package doc comment
+//
+
+package dest
+
+import (
+ "fmt"
+)
+
+// init functions are not renamed
+func init() { prefixfoo() }
+
+// Type S.
+type prefixS struct {
+ prefixt
+ u int
+}
+
+// Function bar.
+func prefixbar(s *prefixS) { fmt.Println(s.prefixt, s.u) }
+
+type prefixt int
+
+const prefixc = 1
+
+func prefixfoo() {
+ fmt.Println()
+}
diff --git a/go/src/golang.org/x/tools/cmd/bundle/testdata/src/initial/a.go b/go/src/golang.org/x/tools/cmd/bundle/testdata/src/initial/a.go
new file mode 100644
index 0000000..99cd145
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/bundle/testdata/src/initial/a.go
@@ -0,0 +1,15 @@
+package initial
+
+import "fmt"
+
+// init functions are not renamed
+func init() { foo() }
+
+// Type S.
+type S struct {
+ t
+ u int
+}
+
+// Function bar.
+func bar(s *S) { fmt.Println(s.t, s.u) }
diff --git a/go/src/golang.org/x/tools/cmd/bundle/testdata/src/initial/b.go b/go/src/golang.org/x/tools/cmd/bundle/testdata/src/initial/b.go
new file mode 100644
index 0000000..399b6ed
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/bundle/testdata/src/initial/b.go
@@ -0,0 +1,12 @@
+// The package doc comment
+package initial
+
+import "fmt"
+
+type t int
+
+const c = 1
+
+func foo() {
+ fmt.Println()
+}
diff --git a/go/src/golang.org/x/tools/cmd/callgraph/main.go b/go/src/golang.org/x/tools/cmd/callgraph/main.go
index 29179dd..eed92aa 100644
--- a/go/src/golang.org/x/tools/cmd/callgraph/main.go
+++ b/go/src/golang.org/x/tools/cmd/callgraph/main.go
@@ -20,12 +20,14 @@
// callee file/line/col
import (
+ "bufio"
"bytes"
"flag"
"fmt"
"go/build"
"go/token"
"io"
+ "log"
"os"
"runtime"
"text/template"
@@ -41,15 +43,21 @@
"golang.org/x/tools/go/ssa/ssautil"
)
-var algoFlag = flag.String("algo", "rta",
- `Call graph construction algorithm (static, cha, rta, pta)`)
+// flags
+var (
+ algoFlag = flag.String("algo", "rta",
+ `Call graph construction algorithm (static, cha, rta, pta)`)
-var testFlag = flag.Bool("test", false,
- "Loads test code (*_test.go) for imported packages")
+ testFlag = flag.Bool("test", false,
+ "Loads test code (*_test.go) for imported packages")
-var formatFlag = flag.String("format",
- "{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}",
- "A template expression specifying how to format an edge")
+ formatFlag = flag.String("format",
+ "{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}",
+ "A template expression specifying how to format an edge")
+
+ ptalogFlag = flag.String("ptalog", "",
+ "Location of the points-to analysis log file, or empty to disable logging.")
+)
func init() {
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
@@ -180,7 +188,7 @@
// Create and build SSA-form program representation.
prog := ssautil.CreateProgram(iprog, 0)
- prog.BuildAll()
+ prog.Build()
// -- call graph construction ------------------------------------------
@@ -194,6 +202,25 @@
cg = cha.CallGraph(prog)
case "pta":
+ // Set up points-to analysis log file.
+ var ptalog io.Writer
+ if *ptalogFlag != "" {
+ if f, err := os.Create(*ptalogFlag); err != nil {
+ log.Fatalf("Failed to create PTA log file: %s", err)
+ } else {
+ buf := bufio.NewWriter(f)
+ ptalog = buf
+ defer func() {
+ if err := buf.Flush(); err != nil {
+ log.Printf("flush: %s", err)
+ }
+ if err := f.Close(); err != nil {
+ log.Printf("close: %s", err)
+ }
+ }()
+ }
+ }
+
main, err := mainPackage(prog, tests)
if err != nil {
return err
@@ -201,6 +228,7 @@
config := &pointer.Config{
Mains: []*ssa.Package{main},
BuildCallGraph: true,
+ Log: ptalog,
}
ptares, err := pointer.Analyze(config)
if err != nil {
@@ -295,7 +323,7 @@
// Otherwise, use the first package named main.
for _, pkg := range pkgs {
- if pkg.Object.Name() == "main" {
+ if pkg.Pkg.Name() == "main" {
if pkg.Func("main") == nil {
return nil, fmt.Errorf("no func main() in main package")
}
diff --git a/go/src/golang.org/x/tools/cmd/eg/eg.go b/go/src/golang.org/x/tools/cmd/eg/eg.go
index 5970c1a..c3cee7d 100644
--- a/go/src/golang.org/x/tools/cmd/eg/eg.go
+++ b/go/src/golang.org/x/tools/cmd/eg/eg.go
@@ -90,7 +90,7 @@
// Analyze the template.
template := iprog.Created[0]
- xform, err := eg.NewTransformer(iprog.Fset, template, *verboseFlag)
+ xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
if err != nil {
return err
}
diff --git a/go/src/golang.org/x/tools/cmd/fiximports/main.go b/go/src/golang.org/x/tools/cmd/fiximports/main.go
index 86ae777..365a261 100644
--- a/go/src/golang.org/x/tools/cmd/fiximports/main.go
+++ b/go/src/golang.org/x/tools/cmd/fiximports/main.go
@@ -96,6 +96,8 @@
dryrun = flag.Bool("n", false, "dry run: show changes, but don't apply them")
badDomains = flag.String("baddomains", "code.google.com",
"a comma-separated list of domains from which packages should not be imported")
+ replaceFlag = flag.String("replace", "",
+ "a comma-separated list of noncanonical=canonical pairs of package paths. If both items in a pair end with '...', they are treated as path prefixes.")
)
// seams for testing
@@ -132,6 +134,8 @@
}
}
+type canonicalName struct{ path, name string }
+
// fiximports fixes imports in the specified packages.
// Invariant: a false result implies an error was already printed.
func fiximports(packages ...string) bool {
@@ -158,13 +162,41 @@
return false
}
- // noncanonical maps each non-canonical package path to
- // its canonical name.
+ // packageName maps each package's path to its name.
+ packageName := make(map[string]string)
+ for _, p := range pkgs {
+ packageName[p.ImportPath] = p.Package.Name
+ }
+
+ // canonical maps each non-canonical package path to
+ // its canonical path and name.
// A present nil value indicates that the canonical package
// is unknown: hosted on a bad domain with no redirect.
- noncanonical := make(map[string]*build.Package)
+ canonical := make(map[string]canonicalName)
domains := strings.Split(*badDomains, ",")
+ type replaceItem struct {
+ old, new string
+ matchPrefix bool
+ }
+ var replace []replaceItem
+ for _, pair := range strings.Split(*replaceFlag, ",") {
+ if pair == "" {
+ continue
+ }
+ words := strings.Split(pair, "=")
+ if len(words) != 2 {
+ fmt.Fprintf(stderr, "importfix: -replace: %q is not of the form \"canonical=noncanonical\".\n", pair)
+ return false
+ }
+ replace = append(replace, replaceItem{
+ old: strings.TrimSuffix(words[0], "..."),
+ new: strings.TrimSuffix(words[1], "..."),
+ matchPrefix: strings.HasSuffix(words[0], "...") &&
+ strings.HasSuffix(words[1], "..."),
+ })
+ }
+
// Find non-canonical packages and populate importedBy graph.
for _, p := range pkgs {
if p.Error != nil {
@@ -187,11 +219,41 @@
addEdge(&p.Package, imp)
}
+ // Does package have an explicit import comment?
if p.ImportComment != "" {
if p.ImportComment != p.ImportPath {
- noncanonical[p.ImportPath] = &p.Package
+ canonical[p.ImportPath] = canonicalName{
+ path: p.Package.ImportComment,
+ name: p.Package.Name,
+ }
}
} else {
+ // Is package matched by a -replace item?
+ var newPath string
+ for _, item := range replace {
+ if item.matchPrefix {
+ if strings.HasPrefix(p.ImportPath, item.old) {
+ newPath = item.new + p.ImportPath[len(item.old):]
+ break
+ }
+ } else if p.ImportPath == item.old {
+ newPath = item.new
+ break
+ }
+ }
+ if newPath != "" {
+ newName := packageName[newPath]
+ if newName == "" {
+ newName = filepath.Base(newPath) // a guess
+ }
+ canonical[p.ImportPath] = canonicalName{
+ path: newPath,
+ name: newName,
+ }
+ continue
+ }
+
+ // Is package matched by a -baddomains item?
for _, domain := range domains {
slash := strings.Index(p.ImportPath, "/")
if slash < 0 {
@@ -200,7 +262,7 @@
if p.ImportPath[:slash] == domain {
// Package comes from bad domain and has no import comment.
// Report an error each time this package is imported.
- noncanonical[p.ImportPath] = nil
+ canonical[p.ImportPath] = canonicalName{}
// TODO(adonovan): should we make an HTTP request to
// see if there's an HTTP redirect, a "go-import" meta tag,
@@ -212,10 +274,10 @@
}
}
- // Find all clients (direct importers) of noncanonical packages.
+ // Find all clients (direct importers) of canonical packages.
// These are the packages that need fixing up.
clients := make(map[*build.Package]bool)
- for path := range noncanonical {
+ for path := range canonical {
for client := range importedBy[path] {
clients[client] = true
}
@@ -244,7 +306,7 @@
// Rewrite selected client packages.
ok := true
for client := range clients {
- if !rewritePackage(client, noncanonical) {
+ if !rewritePackage(client, canonical) {
ok = false
// There were errors.
@@ -291,7 +353,7 @@
}
// Invariant: false result => error already printed.
-func rewritePackage(client *build.Package, noncanonical map[string]*build.Package) bool {
+func rewritePackage(client *build.Package, canonical map[string]canonicalName) bool {
ok := true
used := make(map[string]bool)
@@ -305,7 +367,7 @@
first = true
fmt.Fprintf(stderr, "%s\n", client.ImportPath)
}
- err := rewriteFile(filepath.Join(client.Dir, filename), noncanonical, used)
+ err := rewriteFile(filepath.Join(client.Dir, filename), canonical, used)
if err != nil {
fmt.Fprintf(stderr, "\tERROR: %v\n", err)
ok = false
@@ -319,8 +381,8 @@
}
sort.Strings(keys)
for _, key := range keys {
- if p := noncanonical[key]; p != nil {
- fmt.Fprintf(stderr, "\tfixed: %s -> %s\n", key, p.ImportComment)
+ if p := canonical[key]; p.path != "" {
+ fmt.Fprintf(stderr, "\tfixed: %s -> %s\n", key, p.path)
} else {
fmt.Fprintf(stderr, "\tERROR: %s has no import comment\n", key)
ok = false
@@ -331,10 +393,10 @@
}
// rewrite reads, modifies, and writes filename, replacing all imports
-// of packages P in noncanonical by noncanonical[P].
-// It records in used which noncanonical packages were imported.
+// of packages P in canonical by canonical[P].
+// It records in used which canonical packages were imported.
// used[P]=="" indicates that P was imported but its canonical path is unknown.
-func rewriteFile(filename string, noncanonical map[string]*build.Package, used map[string]bool) error {
+func rewriteFile(filename string, canonical map[string]canonicalName, used map[string]bool) error {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
@@ -348,15 +410,15 @@
fset.Position(imp.Pos()), imp.Path.Value, err)
continue
}
- p, ok := noncanonical[impPath]
+ canon, ok := canonical[impPath]
if !ok {
continue // import path is canonical
}
used[impPath] = true
- if p == nil {
- // The canonical path is unknown.
+ if canon.path == "" {
+ // The canonical path is unknown (a -baddomain).
// Show the offending import.
// TODO(adonovan): should we show the actual source text?
fmt.Fprintf(stderr, "\t%s:%d: import %q\n",
@@ -367,18 +429,16 @@
changed = true
- imp.Path.Value = strconv.Quote(p.ImportComment)
+ imp.Path.Value = strconv.Quote(canon.path)
// Add a renaming import if necessary.
//
// This is a guess at best. We can't see whether a 'go
// get' of the canonical import path would have the same
// name or not. Assume it's the last segment.
- //
- // TODO(adonovan): should we make an HTTP request?
- newBase := path.Base(p.ImportComment)
- if imp.Name == nil && newBase != p.Name {
- imp.Name = &ast.Ident{Name: p.Name}
+ newBase := path.Base(canon.path)
+ if imp.Name == nil && newBase != canon.name {
+ imp.Name = &ast.Ident{Name: canon.name}
}
}
diff --git a/go/src/golang.org/x/tools/cmd/fiximports/main_test.go b/go/src/golang.org/x/tools/cmd/fiximports/main_test.go
index c8f7bc3..e6be110 100644
--- a/go/src/golang.org/x/tools/cmd/fiximports/main_test.go
+++ b/go/src/golang.org/x/tools/cmd/fiximports/main_test.go
@@ -40,11 +40,13 @@
defer func() {
stderr = os.Stderr
*badDomains = "code.google.com"
+ *replaceFlag = ""
}()
for i, test := range []struct {
packages []string // packages to rewrite, "go list" syntax
badDomains string // -baddomains flag
+ replaceFlag string // -replace flag
wantOK bool
wantStderr string
wantRewrite map[string]string
@@ -106,8 +108,77 @@
)`,
},
},
+ // #3. The -replace flag lets user supply missing import comments.
+ {
+ packages: []string{"all"},
+ replaceFlag: "titanic.biz/foo=new.com/foo",
+ wantOK: true,
+ wantStderr: `
+testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
+fruit.io/banana
+ fixed: old.com/one -> new.com/one
+ fixed: titanic.biz/bar -> new.com/bar
+ fixed: titanic.biz/foo -> new.com/foo
+`,
+ wantRewrite: map[string]string{
+ "$GOPATH/src/fruit.io/banana/banana.go": `package banana
+
+import (
+ _ "new.com/bar"
+ _ "new.com/foo"
+ _ "new.com/one"
+)`,
+ },
+ },
+ // #4. The -replace flag supports wildcards.
+ // An explicit import comment takes precedence.
+ {
+ packages: []string{"all"},
+ replaceFlag: "titanic.biz/...=new.com/...",
+ wantOK: true,
+ wantStderr: `
+testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
+fruit.io/banana
+ fixed: old.com/one -> new.com/one
+ fixed: titanic.biz/bar -> new.com/bar
+ fixed: titanic.biz/foo -> new.com/foo
+`,
+ wantRewrite: map[string]string{
+ "$GOPATH/src/fruit.io/banana/banana.go": `package banana
+
+import (
+ _ "new.com/bar"
+ _ "new.com/foo"
+ _ "new.com/one"
+)`,
+ },
+ },
+ // #5. The -replace flag trumps -baddomains.
+ {
+ packages: []string{"all"},
+ badDomains: "titanic.biz",
+ replaceFlag: "titanic.biz/foo=new.com/foo",
+ wantOK: true,
+ wantStderr: `
+testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
+fruit.io/banana
+ fixed: old.com/one -> new.com/one
+ fixed: titanic.biz/bar -> new.com/bar
+ fixed: titanic.biz/foo -> new.com/foo
+`,
+ wantRewrite: map[string]string{
+ "$GOPATH/src/fruit.io/banana/banana.go": `package banana
+
+import (
+ _ "new.com/bar"
+ _ "new.com/foo"
+ _ "new.com/one"
+)`,
+ },
+ },
} {
*badDomains = test.badDomains
+ *replaceFlag = test.replaceFlag
stderr = new(bytes.Buffer)
gotRewrite := make(map[string]string)
diff --git a/go/src/golang.org/x/tools/cmd/godex/gc.go b/go/src/golang.org/x/tools/cmd/godex/gc.go
index 85335b9..e252900 100644
--- a/go/src/golang.org/x/tools/cmd/godex/gc.go
+++ b/go/src/golang.org/x/tools/cmd/godex/gc.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This file implements access to gc-generated export data.
package main
diff --git a/go/src/golang.org/x/tools/cmd/godex/gc14.go b/go/src/golang.org/x/tools/cmd/godex/gc14.go
new file mode 100644
index 0000000..40c433d
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/godex/gc14.go
@@ -0,0 +1,17 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This file implements access to gc-generated export data.
+
+package main
+
+import (
+ "golang.org/x/tools/go/gcimporter"
+)
+
+func init() {
+ register("gc", gcimporter.Import)
+}
diff --git a/go/src/golang.org/x/tools/cmd/godex/gccgo.go b/go/src/golang.org/x/tools/cmd/godex/gccgo.go
index aee2d8e..2f2168d 100644
--- a/go/src/golang.org/x/tools/cmd/godex/gccgo.go
+++ b/go/src/golang.org/x/tools/cmd/godex/gccgo.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This file implements access to gccgo-generated export data.
package main
diff --git a/go/src/golang.org/x/tools/cmd/godex/gccgo14.go b/go/src/golang.org/x/tools/cmd/godex/gccgo14.go
new file mode 100644
index 0000000..1317527
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/godex/gccgo14.go
@@ -0,0 +1,42 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This file implements access to gccgo-generated export data.
+
+package main
+
+import (
+ "golang.org/x/tools/go/gccgoimporter"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ initmap = make(map[*types.Package]gccgoimporter.InitData)
+)
+
+func init() {
+ incpaths := []string{"/"}
+
+ // importer for default gccgo
+ var inst gccgoimporter.GccgoInstallation
+ inst.InitFromDriver("gccgo")
+ register("gccgo", inst.GetImporter(incpaths, initmap))
+}
+
+// Print the extra gccgo compiler data for this package, if it exists.
+func (p *printer) printGccgoExtra(pkg *types.Package) {
+ if initdata, ok := initmap[pkg]; ok {
+ p.printf("/*\npriority %d\n", initdata.Priority)
+
+ p.printDecl("init", len(initdata.Inits), func() {
+ for _, init := range initdata.Inits {
+ p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority)
+ }
+ })
+
+ p.print("*/\n")
+ }
+}
diff --git a/go/src/golang.org/x/tools/cmd/godex/godex.go b/go/src/golang.org/x/tools/cmd/godex/godex.go
index dee0990..3cdca2e 100644
--- a/go/src/golang.org/x/tools/cmd/godex/godex.go
+++ b/go/src/golang.org/x/tools/cmd/godex/godex.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package main
import (
diff --git a/go/src/golang.org/x/tools/cmd/godex/godex14.go b/go/src/golang.org/x/tools/cmd/godex/godex14.go
new file mode 100644
index 0000000..c193bad
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/godex/godex14.go
@@ -0,0 +1,209 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "go/build"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
+ verbose = flag.Bool("v", false, "verbose mode")
+)
+
+// lists of registered sources and corresponding importers
+var (
+ sources []string
+ importers []types.Importer
+ importFailed = errors.New("import failed")
+)
+
+// map of imported packages
+var packages = make(map[string]*types.Package)
+
+func usage() {
+ fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func report(msg string) {
+ fmt.Fprintln(os.Stderr, "error: "+msg)
+ os.Exit(2)
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() == 0 {
+ report("no package name, path, or file provided")
+ }
+
+ imp := tryImports
+ if *source != "" {
+ imp = lookup(*source)
+ if imp == nil {
+ report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
+ }
+ }
+
+ for _, arg := range flag.Args() {
+ path, name := splitPathIdent(arg)
+ logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
+
+ // generate possible package path prefixes
+ // (at the moment we do this for each argument - should probably cache the generated prefixes)
+ prefixes := make(chan string)
+ go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))
+
+ // import package
+ pkg, err := tryPrefixes(packages, prefixes, path, imp)
+ if err != nil {
+ logf("\t=> ignoring %q: %s\n", path, err)
+ continue
+ }
+
+ // filter objects if needed
+ var filter func(types.Object) bool
+ if name != "" {
+ filter = func(obj types.Object) bool {
+ // TODO(gri) perhaps use regular expression matching here?
+ return obj.Name() == name
+ }
+ }
+
+ // print contents
+ print(os.Stdout, pkg, filter)
+ }
+}
+
+func logf(format string, args ...interface{}) {
+ if *verbose {
+ fmt.Fprintf(os.Stderr, format, args...)
+ }
+}
+
+// splitPathIdent splits a path.name argument into its components.
+// All but the last path element may contain dots.
+func splitPathIdent(arg string) (path, name string) {
+ if i := strings.LastIndex(arg, "."); i >= 0 {
+ if j := strings.LastIndex(arg, "/"); j < i {
+ // '.' is not part of path
+ path = arg[:i]
+ name = arg[i+1:]
+ return
+ }
+ }
+ path = arg
+ return
+}
+
+// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
+// by prepending all possible prefixes to path. It returns with the first package that it could import, or
+// with an error.
+func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
+ for prefix := range prefixes {
+ actual := path
+ if prefix == "" {
+ // don't use filepath.Join as it will sanitize the path and remove
+ // a leading dot and then the path is not recognized as a relative
+ // package path by the importers anymore
+ logf("\ttrying no prefix\n")
+ } else {
+ actual = filepath.Join(prefix, path)
+ logf("\ttrying prefix %q\n", prefix)
+ }
+ pkg, err = imp(packages, actual)
+ if err == nil {
+ break
+ }
+ logf("\t=> importing %q failed: %s\n", actual, err)
+ }
+ return
+}
+
+// tryImports is an importer that tries all registered importers
+// successively until one of them succeeds or all of them failed.
+func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ for i, imp := range importers {
+ logf("\t\ttrying %s import\n", sources[i])
+ pkg, err = imp(packages, path)
+ if err == nil {
+ break
+ }
+ logf("\t\t=> %s import failed: %s\n", sources[i], err)
+ }
+ return
+}
+
+// protect protects an importer imp from panics and returns the protected importer.
+func protect(imp types.Importer) types.Importer {
+ return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ defer func() {
+ if recover() != nil {
+ pkg = nil
+ err = importFailed
+ }
+ }()
+ return imp(packages, path)
+ }
+}
+
+// register registers an importer imp for a given source src.
+func register(src string, imp types.Importer) {
+ if lookup(src) != nil {
+ panic(src + " importer already registered")
+ }
+ sources = append(sources, src)
+ importers = append(importers, protect(imp))
+}
+
+// lookup returns the importer imp for a given source src.
+func lookup(src string) types.Importer {
+ for i, s := range sources {
+ if s == src {
+ return importers[i]
+ }
+ }
+ return nil
+}
+
+func genPrefixes(out chan string, all bool) {
+ out <- ""
+ if all {
+ platform := build.Default.GOOS + "_" + build.Default.GOARCH
+ dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
+ for _, dirname := range dirnames {
+ walkDir(filepath.Join(dirname, "pkg", platform), "", out)
+ }
+ }
+ close(out)
+}
+
+func walkDir(dirname, prefix string, out chan string) {
+ fiList, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ return
+ }
+ for _, fi := range fiList {
+ if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
+ prefix := filepath.Join(prefix, fi.Name())
+ out <- prefix
+ walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/cmd/godex/print.go b/go/src/golang.org/x/tools/cmd/godex/print.go
index e519f41..d411afd 100644
--- a/go/src/golang.org/x/tools/cmd/godex/print.go
+++ b/go/src/golang.org/x/tools/cmd/godex/print.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package main
import (
diff --git a/go/src/golang.org/x/tools/cmd/godex/print14.go b/go/src/golang.org/x/tools/cmd/godex/print14.go
new file mode 100644
index 0000000..55d7c5b
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/godex/print14.go
@@ -0,0 +1,370 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "io"
+ "math/big"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+)
+
+// TODO(gri) use tabwriter for alignment?
+
+func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
+ var p printer
+ p.pkg = pkg
+ p.printPackage(pkg, filter)
+ p.printGccgoExtra(pkg)
+ io.Copy(w, &p.buf)
+}
+
+type printer struct {
+ pkg *types.Package
+ buf bytes.Buffer
+ indent int // current indentation level
+ last byte // last byte written
+}
+
+func (p *printer) print(s string) {
+ // Write the string one byte at a time. We care about the presence of
+ // newlines for indentation which we will see even in the presence of
+ // (non-corrupted) Unicode; no need to read one rune at a time.
+ for i := 0; i < len(s); i++ {
+ ch := s[i]
+ if ch != '\n' && p.last == '\n' {
+ // Note: This could lead to a range overflow for very large
+ // indentations, but it's extremely unlikely to happen for
+ // non-pathological code.
+ p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent])
+ }
+ p.buf.WriteByte(ch)
+ p.last = ch
+ }
+}
+
+func (p *printer) printf(format string, args ...interface{}) {
+ p.print(fmt.Sprintf(format, args...))
+}
+
+// methodsFor returns the named type and corresponding methods if the type
+// denoted by obj is not an interface and has methods. Otherwise it returns
+// the zero value.
+func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
+ named, _ := obj.Type().(*types.Named)
+ if named == nil {
+ // A type name's type can also be the
+ // exported basic type unsafe.Pointer.
+ return nil, nil
+ }
+ if _, ok := named.Underlying().(*types.Interface); ok {
+ // ignore interfaces
+ return nil, nil
+ }
+ methods := combinedMethodSet(named)
+ if len(methods) == 0 {
+ return nil, nil
+ }
+ return named, methods
+}
+
+func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
+ // collect objects by kind
+ var (
+ consts []*types.Const
+ typem []*types.Named // non-interface types with methods
+ typez []*types.TypeName // interfaces or types without methods
+ vars []*types.Var
+ funcs []*types.Func
+ builtins []*types.Builtin
+ methods = make(map[*types.Named][]*types.Selection) // method sets for named types
+ )
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Exported() {
+ // collect top-level exported and possibly filtered objects
+ if filter == nil || filter(obj) {
+ switch obj := obj.(type) {
+ case *types.Const:
+ consts = append(consts, obj)
+ case *types.TypeName:
+ // group into types with methods and types without
+ if named, m := methodsFor(obj); named != nil {
+ typem = append(typem, named)
+ methods[named] = m
+ } else {
+ typez = append(typez, obj)
+ }
+ case *types.Var:
+ vars = append(vars, obj)
+ case *types.Func:
+ funcs = append(funcs, obj)
+ case *types.Builtin:
+ // for unsafe.Sizeof, etc.
+ builtins = append(builtins, obj)
+ }
+ }
+ } else if filter == nil {
+ // no filtering: collect top-level unexported types with methods
+ if obj, _ := obj.(*types.TypeName); obj != nil {
+ // see case *types.TypeName above
+ if named, m := methodsFor(obj); named != nil {
+ typem = append(typem, named)
+ methods[named] = m
+ }
+ }
+ }
+ }
+
+ p.printf("package %s // %q\n", pkg.Name(), pkg.Path())
+
+ p.printDecl("const", len(consts), func() {
+ for _, obj := range consts {
+ p.printObj(obj)
+ p.print("\n")
+ }
+ })
+
+ p.printDecl("var", len(vars), func() {
+ for _, obj := range vars {
+ p.printObj(obj)
+ p.print("\n")
+ }
+ })
+
+ p.printDecl("type", len(typez), func() {
+ for _, obj := range typez {
+ p.printf("%s ", obj.Name())
+ p.writeType(p.pkg, obj.Type().Underlying())
+ p.print("\n")
+ }
+ })
+
+ // non-interface types with methods
+ for _, named := range typem {
+ first := true
+ if obj := named.Obj(); obj.Exported() {
+ if first {
+ p.print("\n")
+ first = false
+ }
+ p.printf("type %s ", obj.Name())
+ p.writeType(p.pkg, named.Underlying())
+ p.print("\n")
+ }
+ for _, m := range methods[named] {
+ if obj := m.Obj(); obj.Exported() {
+ if first {
+ p.print("\n")
+ first = false
+ }
+ p.printFunc(m.Recv(), obj.(*types.Func))
+ p.print("\n")
+ }
+ }
+ }
+
+ if len(funcs) > 0 {
+ p.print("\n")
+ for _, obj := range funcs {
+ p.printFunc(nil, obj)
+ p.print("\n")
+ }
+ }
+
+ // TODO(gri) better handling of builtins (package unsafe only)
+ if len(builtins) > 0 {
+ p.print("\n")
+ for _, obj := range builtins {
+ p.printf("func %s() // builtin\n", obj.Name())
+ }
+ }
+
+ p.print("\n")
+}
+
+func (p *printer) printDecl(keyword string, n int, printGroup func()) {
+ switch n {
+ case 0:
+ // nothing to do
+ case 1:
+ p.printf("\n%s ", keyword)
+ printGroup()
+ default:
+ p.printf("\n%s (\n", keyword)
+ p.indent++
+ printGroup()
+ p.indent--
+ p.print(")\n")
+ }
+}
+
+// absInt returns the absolute value of v as a *big.Int.
+// v must be a numeric value.
+func absInt(v exact.Value) *big.Int {
+ // compute big-endian representation of v
+ b := exact.Bytes(v) // little-endian
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+ return new(big.Int).SetBytes(b)
+}
+
+var (
+ one = big.NewRat(1, 1)
+ ten = big.NewRat(10, 1)
+)
+
+// floatString returns the string representation for a
+// numeric value v in normalized floating-point format.
+func floatString(v exact.Value) string {
+ if exact.Sign(v) == 0 {
+ return "0.0"
+ }
+ // x != 0
+
+ // convert |v| into a big.Rat x
+ x := new(big.Rat).SetFrac(absInt(exact.Num(v)), absInt(exact.Denom(v)))
+
+ // normalize x and determine exponent e
+ // (This is not very efficient, but also not speed-critical.)
+ var e int
+ for x.Cmp(ten) >= 0 {
+ x.Quo(x, ten)
+ e++
+ }
+ for x.Cmp(one) < 0 {
+ x.Mul(x, ten)
+ e--
+ }
+
+ // TODO(gri) Values such as 1/2 are easier to read in form 0.5
+ // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as
+ // 10.0. Fine-tune best exponent range for readability.
+
+ s := x.FloatString(100) // good-enough precision
+
+ // trim trailing 0's
+ i := len(s)
+ for i > 0 && s[i-1] == '0' {
+ i--
+ }
+ s = s[:i]
+
+ // add a 0 if the number ends in decimal point
+ if len(s) > 0 && s[len(s)-1] == '.' {
+ s += "0"
+ }
+
+ // add exponent and sign
+ if e != 0 {
+ s += fmt.Sprintf("e%+d", e)
+ }
+ if exact.Sign(v) < 0 {
+ s = "-" + s
+ }
+
+ // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator
+ // are just a small number of decimal digits), add the exact fraction as
+ // a comment. For instance: 3.3333...e-1 /* = 1/3 */
+
+ return s
+}
+
+// valString returns the string representation for the value v.
+// Setting floatFmt forces an integer value to be formatted in
+// normalized floating-point format.
+// TODO(gri) Move this code into package exact.
+func valString(v exact.Value, floatFmt bool) string {
+ switch v.Kind() {
+ case exact.Int:
+ if floatFmt {
+ return floatString(v)
+ }
+ case exact.Float:
+ return floatString(v)
+ case exact.Complex:
+ re := exact.Real(v)
+ im := exact.Imag(v)
+ var s string
+ if exact.Sign(re) != 0 {
+ s = floatString(re)
+ if exact.Sign(im) >= 0 {
+ s += " + "
+ } else {
+ s += " - "
+ im = exact.UnaryOp(token.SUB, im, 0) // negate im
+ }
+ }
+ // im != 0, otherwise v would be exact.Int or exact.Float
+ return s + floatString(im) + "i"
+ }
+ return v.String()
+}
+
+func (p *printer) printObj(obj types.Object) {
+ p.print(obj.Name())
+
+ typ, basic := obj.Type().Underlying().(*types.Basic)
+ if basic && typ.Info()&types.IsUntyped != 0 {
+ // don't write untyped types
+ } else {
+ p.print(" ")
+ p.writeType(p.pkg, obj.Type())
+ }
+
+ if obj, ok := obj.(*types.Const); ok {
+ floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0
+ p.print(" = ")
+ p.print(valString(obj.Val(), floatFmt))
+ }
+}
+
+func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
+ p.print("func ")
+ sig := obj.Type().(*types.Signature)
+ if recvType != nil {
+ p.print("(")
+ p.writeType(p.pkg, recvType)
+ p.print(") ")
+ }
+ p.print(obj.Name())
+ p.writeSignature(p.pkg, sig)
+}
+
+// combinedMethodSet returns the method set for a named type T
+// merged with all the methods of *T that have different names than
+// the methods of T.
+//
+// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
+// but doesn't require a MethodSetCache.
+// TODO(gri) If this functionality doesn't change over time, consider
+// just calling IntuitiveMethodSet eventually.
+func combinedMethodSet(T *types.Named) []*types.Selection {
+ // method set for T
+ mset := types.NewMethodSet(T)
+ var res []*types.Selection
+ for i, n := 0, mset.Len(); i < n; i++ {
+ res = append(res, mset.At(i))
+ }
+
+ // add all *T methods with names different from T methods
+ pmset := types.NewMethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ pm := pmset.At(i)
+ if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
+ res = append(res, pm)
+ }
+ }
+
+ return res
+}
diff --git a/go/src/golang.org/x/tools/cmd/godex/source.go b/go/src/golang.org/x/tools/cmd/godex/source.go
index 22d7813..a86031a 100644
--- a/go/src/golang.org/x/tools/cmd/godex/source.go
+++ b/go/src/golang.org/x/tools/cmd/godex/source.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This file implements access to export data from source.
package main
diff --git a/go/src/golang.org/x/tools/cmd/godex/source14.go b/go/src/golang.org/x/tools/cmd/godex/source14.go
new file mode 100644
index 0000000..620a9b3
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/godex/source14.go
@@ -0,0 +1,21 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This file implements access to export data from source.
+
+package main
+
+import (
+ "golang.org/x/tools/go/types"
+)
+
+func init() {
+ register("source", sourceImporter)
+}
+
+func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) {
+ panic("unimplemented")
+}
diff --git a/go/src/golang.org/x/tools/cmd/godex/writetype.go b/go/src/golang.org/x/tools/cmd/godex/writetype.go
index 10c8e65..c079855 100644
--- a/go/src/golang.org/x/tools/cmd/godex/writetype.go
+++ b/go/src/golang.org/x/tools/cmd/godex/writetype.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This file implements writing of types. The functionality is lifted
// directly from go/types, but now contains various modifications for
// nicer output.
diff --git a/go/src/golang.org/x/tools/cmd/godex/writetype14.go b/go/src/golang.org/x/tools/cmd/godex/writetype14.go
new file mode 100644
index 0000000..ea009b4
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/godex/writetype14.go
@@ -0,0 +1,244 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This file implements writing of types. The functionality is lifted
+// directly from go/types, but now contains various modifications for
+// nicer output.
+//
+// TODO(gri) back-port once we have a fixed interface and once the
+// go/types API is not frozen anymore for the 1.3 release; and remove
+// this implementation if possible.
+
+package main
+
+import "golang.org/x/tools/go/types"
+
+func (p *printer) writeType(this *types.Package, typ types.Type) {
+ p.writeTypeInternal(this, typ, make([]types.Type, 8))
+}
+
+// From go/types - leave for now to ease back-porting this code.
+const GcCompatibilityMode = false
+
+func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) {
+ // Theoretically, this is a quadratic lookup algorithm, but in
+ // practice deeply nested composite types with unnamed component
+ // types are uncommon. This code is likely more efficient than
+ // using a map.
+ for _, t := range visited {
+ if t == typ {
+ p.printf("â—‹%T", typ) // cycle to typ
+ return
+ }
+ }
+ visited = append(visited, typ)
+
+ switch t := typ.(type) {
+ case nil:
+ p.print("<nil>")
+
+ case *types.Basic:
+ if t.Kind() == types.UnsafePointer {
+ p.print("unsafe.")
+ }
+ if GcCompatibilityMode {
+ // forget the alias names
+ switch t.Kind() {
+ case types.Byte:
+ t = types.Typ[types.Uint8]
+ case types.Rune:
+ t = types.Typ[types.Int32]
+ }
+ }
+ p.print(t.Name())
+
+ case *types.Array:
+ p.printf("[%d]", t.Len())
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Slice:
+ p.print("[]")
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Struct:
+ n := t.NumFields()
+ if n == 0 {
+ p.print("struct{}")
+ return
+ }
+
+ p.print("struct {\n")
+ p.indent++
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+ if !f.Anonymous() {
+ p.printf("%s ", f.Name())
+ }
+ p.writeTypeInternal(this, f.Type(), visited)
+ if tag := t.Tag(i); tag != "" {
+ p.printf(" %q", tag)
+ }
+ p.print("\n")
+ }
+ p.indent--
+ p.print("}")
+
+ case *types.Pointer:
+ p.print("*")
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Tuple:
+ p.writeTuple(this, t, false, visited)
+
+ case *types.Signature:
+ p.print("func")
+ p.writeSignatureInternal(this, t, visited)
+
+ case *types.Interface:
+ // We write the source-level methods and embedded types rather
+ // than the actual method set since resolved method signatures
+ // may have non-printable cycles if parameters have anonymous
+ // interface types that (directly or indirectly) embed the
+ // current interface. For instance, consider the result type
+ // of m:
+ //
+ // type T interface{
+ // m() interface{ T }
+ // }
+ //
+ n := t.NumMethods()
+ if n == 0 {
+ p.print("interface{}")
+ return
+ }
+
+ p.print("interface {\n")
+ p.indent++
+ if GcCompatibilityMode {
+ // print flattened interface
+ // (useful to compare against gc-generated interfaces)
+ for i := 0; i < n; i++ {
+ m := t.Method(i)
+ p.print(m.Name())
+ p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
+ p.print("\n")
+ }
+ } else {
+ // print explicit interface methods and embedded types
+ for i, n := 0, t.NumExplicitMethods(); i < n; i++ {
+ m := t.ExplicitMethod(i)
+ p.print(m.Name())
+ p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
+ p.print("\n")
+ }
+ for i, n := 0, t.NumEmbeddeds(); i < n; i++ {
+ typ := t.Embedded(i)
+ p.writeTypeInternal(this, typ, visited)
+ p.print("\n")
+ }
+ }
+ p.indent--
+ p.print("}")
+
+ case *types.Map:
+ p.print("map[")
+ p.writeTypeInternal(this, t.Key(), visited)
+ p.print("]")
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Chan:
+ var s string
+ var parens bool
+ switch t.Dir() {
+ case types.SendRecv:
+ s = "chan "
+ // chan (<-chan T) requires parentheses
+ if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly {
+ parens = true
+ }
+ case types.SendOnly:
+ s = "chan<- "
+ case types.RecvOnly:
+ s = "<-chan "
+ default:
+ panic("unreachable")
+ }
+ p.print(s)
+ if parens {
+ p.print("(")
+ }
+ p.writeTypeInternal(this, t.Elem(), visited)
+ if parens {
+ p.print(")")
+ }
+
+ case *types.Named:
+ s := "<Named w/o object>"
+ if obj := t.Obj(); obj != nil {
+ if pkg := obj.Pkg(); pkg != nil {
+ if pkg != this {
+ p.print(pkg.Path())
+ p.print(".")
+ }
+ // TODO(gri): function-local named types should be displayed
+ // differently from named types at package level to avoid
+ // ambiguity.
+ }
+ s = obj.Name()
+ }
+ p.print(s)
+
+ default:
+ // For externally defined implementations of Type.
+ p.print(t.String())
+ }
+}
+
+func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) {
+ p.print("(")
+ for i, n := 0, tup.Len(); i < n; i++ {
+ if i > 0 {
+ p.print(", ")
+ }
+ v := tup.At(i)
+ if name := v.Name(); name != "" {
+ p.print(name)
+ p.print(" ")
+ }
+ typ := v.Type()
+ if variadic && i == n-1 {
+ p.print("...")
+ typ = typ.(*types.Slice).Elem()
+ }
+ p.writeTypeInternal(this, typ, visited)
+ }
+ p.print(")")
+}
+
+func (p *printer) writeSignature(this *types.Package, sig *types.Signature) {
+ p.writeSignatureInternal(this, sig, make([]types.Type, 8))
+}
+
+func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) {
+ p.writeTuple(this, sig.Params(), sig.Variadic(), visited)
+
+ res := sig.Results()
+ n := res.Len()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ p.print(" ")
+ if n == 1 && res.At(0).Name() == "" {
+ // single unnamed result
+ p.writeTypeInternal(this, res.At(0).Type(), visited)
+ return
+ }
+
+ // multiple or named result(s)
+ p.writeTuple(this, res, false, visited)
+}
diff --git a/go/src/golang.org/x/tools/cmd/godoc/appinit.go b/go/src/golang.org/x/tools/cmd/godoc/appinit.go
index 3d6a921..0c83df9 100644
--- a/go/src/golang.org/x/tools/cmd/godoc/appinit.go
+++ b/go/src/golang.org/x/tools/cmd/godoc/appinit.go
@@ -12,17 +12,24 @@
import (
"archive/zip"
"log"
+ "net/http"
"path"
"regexp"
"golang.org/x/tools/godoc"
+ "golang.org/x/tools/godoc/dl"
+ "golang.org/x/tools/godoc/proxy"
+ "golang.org/x/tools/godoc/short"
"golang.org/x/tools/godoc/static"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/mapfs"
"golang.org/x/tools/godoc/vfs/zipfs"
+
+ "google.golang.org/appengine"
)
func init() {
+ enforceHosts = !appengine.IsDevAppServer()
playEnabled = true
log.Println("initializing godoc ...")
@@ -61,7 +68,15 @@
pres.NotesRx = regexp.MustCompile("BUG")
readTemplates(pres, true)
- registerHandlers(pres)
+
+ mux := registerHandlers(pres)
+ dl.RegisterHandlers(mux)
+ short.RegisterHandlers(mux)
+
+ // Register /compile and /share handlers against the default serve mux
+ // so that other app modules can make plain HTTP requests to those
+ // hosts. (For reasons, HTTPS communication between modules is broken.)
+ proxy.RegisterHandlers(http.DefaultServeMux)
log.Println("godoc initialization complete")
}
diff --git a/go/src/golang.org/x/tools/cmd/godoc/dl.go b/go/src/golang.org/x/tools/cmd/godoc/dl.go
index bd73831..40e6658 100644
--- a/go/src/golang.org/x/tools/cmd/godoc/dl.go
+++ b/go/src/golang.org/x/tools/cmd/godoc/dl.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !appengine
+
package main
import "net/http"
@@ -10,5 +12,5 @@
// This file will not be included when deploying godoc to golang.org.
func init() {
- http.Handle("/dl/", http.RedirectHandler("http://golang.org/dl/", http.StatusFound))
+ http.Handle("/dl/", http.RedirectHandler("https://golang.org/dl/", http.StatusFound))
}
diff --git a/go/src/golang.org/x/tools/cmd/godoc/godoc_test.go b/go/src/golang.org/x/tools/cmd/godoc/godoc_test.go
index a228fae..e4ab12f 100644
--- a/go/src/golang.org/x/tools/cmd/godoc/godoc_test.go
+++ b/go/src/golang.org/x/tools/cmd/godoc/godoc_test.go
@@ -44,7 +44,9 @@
{
args: []string{"nonexistingpkg"},
matches: []string{
- `no such file or directory|does not exist|cannot find the file`,
+ // The last pattern (does not e) is for plan9:
+ // http://build.golang.org/log/2d8e5e14ed365bfa434b37ec0338cd9e6f8dd9bf
+ `no such file or directory|does not exist|cannot find the file|(?:' does not e)`,
},
},
{
@@ -266,6 +268,18 @@
},
needIndex: true,
},
+ {
+ path: "/pkg/strings/",
+ match: []string{
+ `href="/src/strings/strings.go"`,
+ },
+ },
+ {
+ path: "/cmd/compile/internal/amd64/",
+ match: []string{
+ `href="/src/cmd/compile/internal/amd64/reg.go"`,
+ },
+ },
}
for _, test := range tests {
if test.needIndex && !withIndex {
diff --git a/go/src/golang.org/x/tools/cmd/godoc/handlers.go b/go/src/golang.org/x/tools/cmd/godoc/handlers.go
index 1f79d80..dda1bb8 100644
--- a/go/src/golang.org/x/tools/cmd/godoc/handlers.go
+++ b/go/src/golang.org/x/tools/cmd/godoc/handlers.go
@@ -13,8 +13,11 @@
package main
import (
+ "encoding/json"
+ "go/format"
"log"
"net/http"
+ "strings"
"text/template"
"golang.org/x/tools/godoc"
@@ -27,16 +30,57 @@
fs = vfs.NameSpace{}
)
-func registerHandlers(pres *godoc.Presentation) {
+var enforceHosts = false // set true in production on app engine
+
+// hostEnforcerHandler redirects requests to "http://foo.golang.org/bar"
+// to "https://golang.org/bar".
+// It permits requests to the host "godoc-test.golang.org" for testing.
+type hostEnforcerHandler struct {
+ h http.Handler
+}
+
+func (h hostEnforcerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if !enforceHosts {
+ h.h.ServeHTTP(w, r)
+ return
+ }
+ if r.TLS == nil || !h.validHost(r.Host) {
+ r.URL.Scheme = "https"
+ if h.validHost(r.Host) {
+ r.URL.Host = r.Host
+ } else {
+ r.URL.Host = "golang.org"
+ }
+ http.Redirect(w, r, r.URL.String(), http.StatusFound)
+ return
+ }
+ h.h.ServeHTTP(w, r)
+}
+
+func (h hostEnforcerHandler) validHost(host string) bool {
+ switch strings.ToLower(host) {
+ case "golang.org", "godoc-test.golang.org":
+ return true
+ }
+ return false
+}
+
+func registerHandlers(pres *godoc.Presentation) *http.ServeMux {
if pres == nil {
panic("nil Presentation")
}
- http.HandleFunc("/doc/codewalk/", codewalk)
- http.Handle("/doc/play/", pres.FileServer())
- http.Handle("/robots.txt", pres.FileServer())
- http.Handle("/", pres)
- http.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
- redirect.Register(nil)
+ mux := http.NewServeMux()
+ mux.HandleFunc("/doc/codewalk/", codewalk)
+ mux.Handle("/doc/play/", pres.FileServer())
+ mux.Handle("/robots.txt", pres.FileServer())
+ mux.Handle("/", pres)
+ mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
+ mux.HandleFunc("/fmt", fmtHandler)
+ redirect.Register(mux)
+
+ http.Handle("/", hostEnforcerHandler{mux})
+
+ return mux
}
func readTemplate(name string) *template.Template {
@@ -81,3 +125,22 @@
p.SearchDescXML = readTemplate("opensearch.xml")
}
}
+
+type fmtResponse struct {
+ Body string
+ Error string
+}
+
+// fmtHandler takes a Go program in its "body" form value, formats it with
+// standard gofmt formatting, and writes a fmtResponse as a JSON object.
+func fmtHandler(w http.ResponseWriter, r *http.Request) {
+ resp := new(fmtResponse)
+ body, err := format.Source([]byte(r.FormValue("body")))
+ if err != nil {
+ resp.Error = err.Error()
+ } else {
+ resp.Body = string(body)
+ }
+ w.Header().Set("Content-type", "application/json; charset=utf-8")
+ json.NewEncoder(w).Encode(resp)
+}
diff --git a/go/src/golang.org/x/tools/cmd/godoc/play.go b/go/src/golang.org/x/tools/cmd/godoc/play.go
index a56ffe2..02f477d 100644
--- a/go/src/golang.org/x/tools/cmd/godoc/play.go
+++ b/go/src/golang.org/x/tools/cmd/godoc/play.go
@@ -2,43 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !appengine
+
package main
-import (
- "encoding/json"
- "fmt"
- "go/format"
- "net/http"
-
- // This package registers "/compile" and "/share" handlers
- // that redirect to the golang.org playground.
- _ "golang.org/x/tools/playground"
-)
-
-func init() {
- http.HandleFunc("/fmt", fmtHandler)
-}
-
-type fmtResponse struct {
- Body string
- Error string
-}
-
-// fmtHandler takes a Go program in its "body" form value, formats it with
-// standard gofmt formatting, and writes a fmtResponse as a JSON object.
-func fmtHandler(w http.ResponseWriter, r *http.Request) {
- resp := new(fmtResponse)
- body, err := format.Source([]byte(r.FormValue("body")))
- if err != nil {
- resp.Error = err.Error()
- } else {
- resp.Body = string(body)
- }
- json.NewEncoder(w).Encode(resp)
-}
-
-// disabledHandler serves a 501 "Not Implemented" response.
-func disabledHandler(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusNotImplemented)
- fmt.Fprint(w, "This functionality is not available via local godoc.")
-}
+// This package registers "/compile" and "/share" handlers
+// that redirect to the golang.org playground.
+import _ "golang.org/x/tools/playground"
diff --git a/go/src/golang.org/x/tools/cmd/godoc/setup-godoc-app.bash b/go/src/golang.org/x/tools/cmd/godoc/setup-godoc-app.bash
index 9d82cd7..a462ebe 100755
--- a/go/src/golang.org/x/tools/cmd/godoc/setup-godoc-app.bash
+++ b/go/src/golang.org/x/tools/cmd/godoc/setup-godoc-app.bash
@@ -67,7 +67,7 @@
go=$APPENGINE_SDK/goapp
$go get -d -tags appengine $GODOC
mkdir -p $APPDIR/$GODOC
- cp $(find $($go list -f '{{.Dir}}' $GODOC) -type f -depth 1) $APPDIR/$GODOC/
+ cp $(find $($go list -f '{{.Dir}}' $GODOC) -mindepth 1 -maxdepth 1 -type f) $APPDIR/$GODOC/
}
makeAppYaml() {
diff --git a/go/src/golang.org/x/tools/cmd/godoc/x.go b/go/src/golang.org/x/tools/cmd/godoc/x.go
index b4af41a..d6ee745 100644
--- a/go/src/golang.org/x/tools/cmd/godoc/x.go
+++ b/go/src/golang.org/x/tools/cmd/godoc/x.go
@@ -24,6 +24,7 @@
var xMap = map[string]xRepo{
"codereview": {"https://code.google.com/p/go.codereview", "hg"},
+ "arch": {"https://go.googlesource.com/arch", "git"},
"benchmarks": {"https://go.googlesource.com/benchmarks", "git"},
"blog": {"https://go.googlesource.com/blog", "git"},
"build": {"https://go.googlesource.com/build", "git"},
@@ -36,9 +37,12 @@
"oauth2": {"https://go.googlesource.com/oauth2", "git"},
"playground": {"https://go.googlesource.com/playground", "git"},
"review": {"https://go.googlesource.com/review", "git"},
+ "sync": {"https://go.googlesource.com/sync", "git"},
"sys": {"https://go.googlesource.com/sys", "git"},
"talks": {"https://go.googlesource.com/talks", "git"},
+ "term": {"https://go.googlesource.com/term", "git"},
"text": {"https://go.googlesource.com/text", "git"},
+ "time": {"https://go.googlesource.com/time", "git"},
"tools": {"https://go.googlesource.com/tools", "git"},
"tour": {"https://go.googlesource.com/tour", "git"},
}
diff --git a/go/src/golang.org/x/tools/cmd/gorename/main.go b/go/src/golang.org/x/tools/cmd/gorename/main.go
index 922dc0c..b1b895c 100644
--- a/go/src/golang.org/x/tools/cmd/gorename/main.go
+++ b/go/src/golang.org/x/tools/cmd/gorename/main.go
@@ -11,8 +11,8 @@
"flag"
"fmt"
"go/build"
+ "log"
"os"
- "runtime"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/refactor/rename"
@@ -28,25 +28,17 @@
func init() {
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
flag.BoolVar(&rename.Force, "force", false, "proceed, even if conflicts were reported")
- flag.BoolVar(&rename.DryRun, "dryrun", false, "show the change, but do not apply it")
flag.BoolVar(&rename.Verbose, "v", false, "print verbose information")
-
- // If $GOMAXPROCS isn't set, use the full capacity of the machine.
- // For small machines, use at least 4 threads.
- if os.Getenv("GOMAXPROCS") == "" {
- n := runtime.NumCPU()
- if n < 4 {
- n = 4
- }
- runtime.GOMAXPROCS(n)
- }
+ flag.BoolVar(&rename.Diff, "d", false, "display diffs instead of rewriting files")
+ flag.StringVar(&rename.DiffCmd, "diffcmd", "diff", "diff command invoked when using -d")
}
func main() {
+ log.SetPrefix("gorename: ")
+ log.SetFlags(0)
flag.Parse()
if len(flag.Args()) > 0 {
- fmt.Fprintln(os.Stderr, "gorename: surplus arguments.")
- os.Exit(1)
+ log.Fatal("surplus arguments")
}
if *helpFlag || (*offsetFlag == "" && *fromFlag == "" && *toFlag == "") {
@@ -56,7 +48,7 @@
if err := rename.Main(&build.Default, *offsetFlag, *fromFlag, *toFlag); err != nil {
if err != rename.ConflictError {
- fmt.Fprintf(os.Stderr, "gorename: %s\n", err)
+ log.Fatal(err)
}
os.Exit(1)
}
diff --git a/go/src/golang.org/x/tools/cmd/gotype/gotype.go b/go/src/golang.org/x/tools/cmd/gotype/gotype.go
index 4a5c7de..bd3721f 100644
--- a/go/src/golang.org/x/tools/cmd/gotype/gotype.go
+++ b/go/src/golang.org/x/tools/cmd/gotype/gotype.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package main
import (
@@ -9,18 +11,15 @@
"fmt"
"go/ast"
"go/build"
+ "go/importer"
"go/parser"
"go/scanner"
"go/token"
+ "go/types"
"io/ioutil"
"os"
"path/filepath"
- "runtime"
"time"
-
- "golang.org/x/tools/go/gccgoimporter"
- _ "golang.org/x/tools/go/gcimporter"
- "golang.org/x/tools/go/types"
)
var (
@@ -186,6 +185,10 @@
}
func checkPkgFiles(files []*ast.File) {
+ compiler := "gc"
+ if *gccgo {
+ compiler = "gccgo"
+ }
type bailout struct{}
conf := types.Config{
FakeImportC: true,
@@ -195,12 +198,8 @@
}
report(err)
},
- Sizes: sizes,
- }
- if *gccgo {
- var inst gccgoimporter.GccgoInstallation
- inst.InitFromDriver("gccgo")
- conf.Import = inst.GetImporter(nil, nil)
+ Importer: importer.For(compiler, nil),
+ Sizes: sizes,
}
defer func() {
@@ -233,8 +232,6 @@
}
func main() {
- runtime.GOMAXPROCS(runtime.NumCPU()) // remove this once runtime is smarter
-
flag.Usage = usage
flag.Parse()
if *printAST || *printTrace {
diff --git a/go/src/golang.org/x/tools/cmd/gotype/gotype14.go b/go/src/golang.org/x/tools/cmd/gotype/gotype14.go
new file mode 100644
index 0000000..92d089e
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/gotype/gotype14.go
@@ -0,0 +1,268 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This is a 1:1 copy of gotype.go but for the changes required to build
+// against Go1.4 and before.
+// TODO(gri) Decide long-term fate of gotype (issue #12303).
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "time"
+
+ "golang.org/x/tools/go/gccgoimporter"
+ _ "golang.org/x/tools/go/gcimporter"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ // main operation modes
+ allFiles = flag.Bool("a", false, "use all (incl. _test.go) files when processing a directory")
+ allErrors = flag.Bool("e", false, "report all errors (not just the first 10)")
+ verbose = flag.Bool("v", false, "verbose mode")
+ gccgo = flag.Bool("gccgo", false, "use gccgoimporter instead of gcimporter")
+
+ // debugging support
+ sequential = flag.Bool("seq", false, "parse sequentially, rather than in parallel")
+ printAST = flag.Bool("ast", false, "print AST (forces -seq)")
+ printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
+ parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
+)
+
+var (
+ fset = token.NewFileSet()
+ errorCount = 0
+ parserMode parser.Mode
+ sizes types.Sizes
+)
+
+func initParserMode() {
+ if *allErrors {
+ parserMode |= parser.AllErrors
+ }
+ if *printTrace {
+ parserMode |= parser.Trace
+ }
+ if *parseComments && (*printAST || *printTrace) {
+ parserMode |= parser.ParseComments
+ }
+}
+
+func initSizes() {
+ wordSize := 8
+ maxAlign := 8
+ switch build.Default.GOARCH {
+ case "386", "arm":
+ wordSize = 4
+ maxAlign = 4
+ // add more cases as needed
+ }
+ sizes = &types.StdSizes{WordSize: int64(wordSize), MaxAlign: int64(maxAlign)}
+}
+
+func usage() {
+ fmt.Fprintln(os.Stderr, "usage: gotype [flags] [path ...]")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func report(err error) {
+ scanner.PrintError(os.Stderr, err)
+ if list, ok := err.(scanner.ErrorList); ok {
+ errorCount += len(list)
+ return
+ }
+ errorCount++
+}
+
+// parse may be called concurrently
+func parse(filename string, src interface{}) (*ast.File, error) {
+ if *verbose {
+ fmt.Println(filename)
+ }
+ file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
+ if *printAST {
+ ast.Print(fset, file)
+ }
+ return file, err
+}
+
+func parseStdin() (*ast.File, error) {
+ src, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ return nil, err
+ }
+ return parse("<standard input>", src)
+}
+
+func parseFiles(filenames []string) ([]*ast.File, error) {
+ files := make([]*ast.File, len(filenames))
+
+ if *sequential {
+ for i, filename := range filenames {
+ var err error
+ files[i], err = parse(filename, nil)
+ if err != nil {
+ return nil, err // leave unfinished goroutines hanging
+ }
+ }
+ } else {
+ type parseResult struct {
+ file *ast.File
+ err error
+ }
+
+ out := make(chan parseResult)
+ for _, filename := range filenames {
+ go func(filename string) {
+ file, err := parse(filename, nil)
+ out <- parseResult{file, err}
+ }(filename)
+ }
+
+ for i := range filenames {
+ res := <-out
+ if res.err != nil {
+ return nil, res.err // leave unfinished goroutines hanging
+ }
+ files[i] = res.file
+ }
+ }
+
+ return files, nil
+}
+
+func parseDir(dirname string) ([]*ast.File, error) {
+ ctxt := build.Default
+ pkginfo, err := ctxt.ImportDir(dirname, 0)
+ if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
+ return nil, err
+ }
+ filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
+ if *allFiles {
+ filenames = append(filenames, pkginfo.TestGoFiles...)
+ }
+
+ // complete file names
+ for i, filename := range filenames {
+ filenames[i] = filepath.Join(dirname, filename)
+ }
+
+ return parseFiles(filenames)
+}
+
+func getPkgFiles(args []string) ([]*ast.File, error) {
+ if len(args) == 0 {
+ // stdin
+ file, err := parseStdin()
+ if err != nil {
+ return nil, err
+ }
+ return []*ast.File{file}, nil
+ }
+
+ if len(args) == 1 {
+ // possibly a directory
+ path := args[0]
+ info, err := os.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+ if info.IsDir() {
+ return parseDir(path)
+ }
+ }
+
+ // list of files
+ return parseFiles(args)
+}
+
+func checkPkgFiles(files []*ast.File) {
+ type bailout struct{}
+ conf := types.Config{
+ FakeImportC: true,
+ Error: func(err error) {
+ if !*allErrors && errorCount >= 10 {
+ panic(bailout{})
+ }
+ report(err)
+ },
+ Sizes: sizes,
+ }
+ if *gccgo {
+ var inst gccgoimporter.GccgoInstallation
+ inst.InitFromDriver("gccgo")
+ conf.Import = inst.GetImporter(nil, nil)
+ }
+
+ defer func() {
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ default:
+ // re-panic
+ panic(p)
+ }
+ }()
+
+ const path = "pkg" // any non-empty string will do for now
+ conf.Check(path, fset, files, nil)
+}
+
+func printStats(d time.Duration) {
+ fileCount := 0
+ lineCount := 0
+ fset.Iterate(func(f *token.File) bool {
+ fileCount++
+ lineCount += f.LineCount()
+ return true
+ })
+
+ fmt.Printf(
+ "%s (%d files, %d lines, %d lines/s)\n",
+ d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
+ )
+}
+
+func main() {
+ runtime.GOMAXPROCS(runtime.NumCPU()) // not needed for go1.5
+
+ flag.Usage = usage
+ flag.Parse()
+ if *printAST || *printTrace {
+ *sequential = true
+ }
+ initParserMode()
+ initSizes()
+
+ start := time.Now()
+
+ files, err := getPkgFiles(flag.Args())
+ if err != nil {
+ report(err)
+ os.Exit(2)
+ }
+
+ checkPkgFiles(files)
+ if errorCount > 0 {
+ os.Exit(2)
+ }
+
+ if *verbose {
+ printStats(time.Since(start))
+ }
+}
diff --git a/go/src/golang.org/x/tools/cmd/oracle/main.go b/go/src/golang.org/x/tools/cmd/oracle/main.go
index a7a1661..7b23b15 100644
--- a/go/src/golang.org/x/tools/cmd/oracle/main.go
+++ b/go/src/golang.org/x/tools/cmd/oracle/main.go
@@ -78,7 +78,7 @@
golang.org/x/tools/cmd/oracle
Print the callgraph of the trivial web-server in JSON format:
-% oracle -format=json $GOROOT/src/net/http/triv.go callgraph
+% oracle -format=json callstack $GOROOT/src/net/http/triv.go
` + loader.FromArgsUsage
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
diff --git a/go/src/golang.org/x/tools/cmd/present/appengine.go b/go/src/golang.org/x/tools/cmd/present/appengine.go
index 7df536c..1e39ce1 100644
--- a/go/src/golang.org/x/tools/cmd/present/appengine.go
+++ b/go/src/golang.org/x/tools/cmd/present/appengine.go
@@ -10,21 +10,13 @@
"mime"
"golang.org/x/tools/present"
-
- _ "golang.org/x/tools/playground"
)
-var basePath = "./present/"
-
func init() {
- initTemplates(basePath)
- playScript(basePath, "HTTPTransport")
+ initTemplates("./present/")
present.PlayEnabled = true
+ initPlayground("./present/", nil)
// App Engine has no /etc/mime.types
mime.AddExtensionType(".svg", "image/svg+xml")
}
-
-func playable(c present.Code) bool {
- return present.PlayEnabled && c.Play && c.Ext == ".go"
-}
diff --git a/go/src/golang.org/x/tools/cmd/present/doc.go b/go/src/golang.org/x/tools/cmd/present/doc.go
index fafcefe..7c32696 100644
--- a/go/src/golang.org/x/tools/cmd/present/doc.go
+++ b/go/src/golang.org/x/tools/cmd/present/doc.go
@@ -7,8 +7,6 @@
presents slide and article files from the current directory.
It may be run as a stand-alone command or an App Engine app.
-Instructions for deployment to App Engine are in the README of the
-golang.org/x/tools repository.
Usage of present:
-base="": base path for slide template and static resources
@@ -20,6 +18,33 @@
The setup of the Go version of NaCl is documented at:
https://golang.org/wiki/NativeClient
+To use with App Engine, copy the tools/cmd/present directory to the root of
+your application and create an app.yaml file similar to this:
+
+ application: [application]
+ version: [version]
+ runtime: go
+ api_version: go1
+
+ handlers:
+ - url: /favicon.ico
+ static_files: present/static/favicon.ico
+ upload: present/static/favicon.ico
+ - url: /static
+ static_dir: present/static
+ application_readable: true
+ - url: /.*
+ script: _go_app
+
+ # nobuild_files is a regexp that identifies which files to not build. It
+ # is useful for embedding static assets like code snippets and preventing
+ # them from producing build errors for your project.
+ nobuild_files: [path regexp for talk materials]
+
+Present then can be tested in a local App Engine environment with
+
+ goapp serve
+
Input files are named foo.extension, where "extension" defines the format of
the generated output. The supported formats are:
.slide // HTML5 slide presentation
diff --git a/go/src/golang.org/x/tools/cmd/present/local.go b/go/src/golang.org/x/tools/cmd/present/local.go
index d91dcc2..d968553 100644
--- a/go/src/golang.org/x/tools/cmd/present/local.go
+++ b/go/src/golang.org/x/tools/cmd/present/local.go
@@ -15,35 +15,34 @@
"net/http"
"net/url"
"os"
- "runtime"
"strings"
- "golang.org/x/tools/playground/socket"
"golang.org/x/tools/present"
)
const basePkg = "golang.org/x/tools/cmd/present"
-var basePath string
+var (
+ httpAddr = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
+ originHost = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
+ basePath = flag.String("base", "", "base path for slide template and static resources")
+ nativeClient = flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")
+)
func main() {
- httpAddr := flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
- originHost := flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
- flag.StringVar(&basePath, "base", "", "base path for slide template and static resources")
flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
- nativeClient := flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")
flag.Parse()
- if basePath == "" {
+ if *basePath == "" {
p, err := build.Default.Import(basePkg, "", build.FindOnly)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v\n", err)
fmt.Fprintf(os.Stderr, basePathMessage, basePkg)
os.Exit(1)
}
- basePath = p.Dir
+ *basePath = p.Dir
}
- err := initTemplates(basePath)
+ err := initTemplates(*basePath)
if err != nil {
log.Fatalf("Failed to parse templates: %v", err)
}
@@ -58,6 +57,7 @@
if err != nil {
log.Fatal(err)
}
+
origin := &url.URL{Scheme: "http"}
if *originHost != "" {
origin.Host = net.JoinHostPort(*originHost, port)
@@ -76,20 +76,8 @@
}
}
- if present.PlayEnabled {
- if *nativeClient {
- socket.RunScripts = false
- socket.Environ = func() []string {
- if runtime.GOARCH == "amd64" {
- return environ("GOOS=nacl", "GOARCH=amd64p32")
- }
- return environ("GOOS=nacl")
- }
- }
- playScript(basePath, "SocketTransport")
- http.Handle("/socket", socket.NewHandler(origin))
- }
- http.Handle("/static/", http.FileServer(http.Dir(basePath)))
+ initPlayground(*basePath, origin)
+ http.Handle("/static/", http.FileServer(http.Dir(*basePath)))
if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&
present.PlayEnabled && !*nativeClient {
@@ -100,10 +88,6 @@
log.Fatal(http.Serve(ln, nil))
}
-func playable(c present.Code) bool {
- return present.PlayEnabled && c.Play
-}
-
func environ(vars ...string) []string {
env := os.Environ()
for _, r := range vars {
diff --git a/go/src/golang.org/x/tools/cmd/present/play_http.go b/go/src/golang.org/x/tools/cmd/present/play_http.go
new file mode 100644
index 0000000..02e7314
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/present/play_http.go
@@ -0,0 +1,23 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine appenginevm
+
+package main
+
+import (
+ "net/url"
+
+ "golang.org/x/tools/present"
+
+ _ "golang.org/x/tools/playground"
+)
+
+func initPlayground(basepath string, origin *url.URL) {
+ playScript(basepath, "HTTPTransport")
+}
+
+func playable(c present.Code) bool {
+ return present.PlayEnabled && c.Play && c.Ext == ".go"
+}
diff --git a/go/src/golang.org/x/tools/cmd/present/play_socket.go b/go/src/golang.org/x/tools/cmd/present/play_socket.go
new file mode 100644
index 0000000..df63eda
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/present/play_socket.go
@@ -0,0 +1,36 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine,!appenginevm
+
+package main
+
+import (
+ "net/http"
+ "net/url"
+ "runtime"
+
+ "golang.org/x/tools/playground/socket"
+ "golang.org/x/tools/present"
+)
+
+func initPlayground(basepath string, origin *url.URL) {
+ if present.PlayEnabled {
+ if *nativeClient {
+ socket.RunScripts = false
+ socket.Environ = func() []string {
+ if runtime.GOARCH == "amd64" {
+ return environ("GOOS=nacl", "GOARCH=amd64p32")
+ }
+ return environ("GOOS=nacl")
+ }
+ }
+ playScript(basepath, "SocketTransport")
+ http.Handle("/socket", socket.NewHandler(origin))
+ }
+}
+
+func playable(c present.Code) bool {
+ return present.PlayEnabled && c.Play
+}
diff --git a/go/src/golang.org/x/tools/cmd/present/static/styles.css b/go/src/golang.org/x/tools/cmd/present/static/styles.css
index 5cb2953..688127f 100644
--- a/go/src/golang.org/x/tools/cmd/present/static/styles.css
+++ b/go/src/golang.org/x/tools/cmd/present/static/styles.css
@@ -379,11 +379,26 @@
color: black;
}
-article > .image {
+article > .image,
+article > .video {
text-align: center;
margin-top: 40px;
}
+article > .background {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: -1;
+}
+
+article > .background > img {
+ max-height: 100%;
+ max-width: 100%;
+}
+
table {
width: 100%;
border-collapse: collapse;
diff --git a/go/src/golang.org/x/tools/cmd/present/templates/action.tmpl b/go/src/golang.org/x/tools/cmd/present/templates/action.tmpl
index 2893058..1eae3b7 100644
--- a/go/src/golang.org/x/tools/cmd/present/templates/action.tmpl
+++ b/go/src/golang.org/x/tools/cmd/present/templates/action.tmpl
@@ -37,6 +37,20 @@
</div>
{{end}}
+{{define "video"}}
+<div class="video">
+ <video {{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}} controls>
+ <source src="{{.URL}}" type="{{.SourceType}}">
+ </video>
+</div>
+{{end}}
+
+{{define "background"}}
+<div class="background">
+ <img src="{{.URL}}">
+</div>
+{{end}}
+
{{define "iframe"}}
<iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe>
{{end}}
@@ -45,4 +59,4 @@
{{define "html"}}{{.HTML}}{{end}}
-{{define "caption"}}<figcaption>{{style .Text}}</figcaption>{{end}}
\ No newline at end of file
+{{define "caption"}}<figcaption>{{style .Text}}</figcaption>{{end}}
diff --git a/go/src/golang.org/x/tools/cmd/present/templates/article.tmpl b/go/src/golang.org/x/tools/cmd/present/templates/article.tmpl
index 40d1c93..411f00b 100644
--- a/go/src/golang.org/x/tools/cmd/present/templates/article.tmpl
+++ b/go/src/golang.org/x/tools/cmd/present/templates/article.tmpl
@@ -7,6 +7,23 @@
<title>{{.Title}}</title>
<link type="text/css" rel="stylesheet" href="/static/article.css">
<meta charset='utf-8'>
+ <script>
+ // Initialize Google Analytics tracking code on production site only.
+ if (window["location"] && window["location"]["hostname"] == "talks.golang.org") {
+ var _gaq = _gaq || [];
+ _gaq.push(["_setAccount", "UA-11222381-6"]);
+ _gaq.push(["b._setAccount", "UA-49880327-6"]);
+ window.trackPageview = function() {
+ _gaq.push(["_trackPageview", location.pathname+location.hash]);
+ _gaq.push(["b._trackPageview", location.pathname+location.hash]);
+ };
+ window.trackPageview();
+ window.trackEvent = function(category, action, opt_label, opt_value, opt_noninteraction) {
+ _gaq.push(["_trackEvent", category, action, opt_label, opt_value, opt_noninteraction]);
+ _gaq.push(["b._trackEvent", category, action, opt_label, opt_value, opt_noninteraction]);
+ };
+ }
+ </script>
</head>
<body>
@@ -39,7 +56,21 @@
{{end}}
</div>
</div>
+
+ {{if .PlayEnabled}}
<script src='/play.js'></script>
+ {{end}}
+
+ <script>
+ (function() {
+ // Load Google Analytics tracking code on production site only.
+ if (window["location"] && window["location"]["hostname"] == "talks.golang.org") {
+ var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
+ ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
+ var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
+ }
+ })();
+ </script>
</body>
</html>
{{end}}
diff --git a/go/src/golang.org/x/tools/cmd/present/templates/dir.tmpl b/go/src/golang.org/x/tools/cmd/present/templates/dir.tmpl
index 15cf97f..1732623 100644
--- a/go/src/golang.org/x/tools/cmd/present/templates/dir.tmpl
+++ b/go/src/golang.org/x/tools/cmd/present/templates/dir.tmpl
@@ -1,10 +1,27 @@
<!DOCTYPE html>
<html>
<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Talks - The Go Programming Language</title>
<link type="text/css" rel="stylesheet" href="/static/dir.css">
<script src="/static/dir.js"></script>
+ <script>
+ // Initialize Google Analytics tracking code on production site only.
+ if (window["location"] && window["location"]["hostname"] == "talks.golang.org") {
+ var _gaq = _gaq || [];
+ _gaq.push(["_setAccount", "UA-11222381-6"]);
+ _gaq.push(["b._setAccount", "UA-49880327-6"]);
+ window.trackPageview = function() {
+ _gaq.push(["_trackPageview", location.pathname+location.hash]);
+ _gaq.push(["b._trackPageview", location.pathname+location.hash]);
+ };
+ window.trackPageview();
+ window.trackEvent = function(category, action, opt_label, opt_value, opt_noninteraction) {
+ _gaq.push(["_trackEvent", category, action, opt_label, opt_value, opt_noninteraction]);
+ _gaq.push(["b._trackEvent", category, action, opt_label, opt_value, opt_noninteraction]);
+ };
+ }
+ </script>
</head>
<body>
@@ -77,5 +94,15 @@
<a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
</div>
+<script>
+ (function() {
+ // Load Google Analytics tracking code on production site only.
+ if (window["location"] && window["location"]["hostname"] == "talks.golang.org") {
+ var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
+ ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
+ var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
+ }
+ })();
+</script>
</body>
</html>
diff --git a/go/src/golang.org/x/tools/cmd/present/templates/slides.tmpl b/go/src/golang.org/x/tools/cmd/present/templates/slides.tmpl
index 11070d2..2b4ec98 100644
--- a/go/src/golang.org/x/tools/cmd/present/templates/slides.tmpl
+++ b/go/src/golang.org/x/tools/cmd/present/templates/slides.tmpl
@@ -7,6 +7,23 @@
<title>{{.Title}}</title>
<meta charset='utf-8'>
<script src='/static/slides.js'></script>
+ <script>
+ // Initialize Google Analytics tracking code on production site only.
+ if (window["location"] && window["location"]["hostname"] == "talks.golang.org") {
+ var _gaq = _gaq || [];
+ _gaq.push(["_setAccount", "UA-11222381-6"]);
+ _gaq.push(["b._setAccount", "UA-49880327-6"]);
+ window.trackPageview = function() {
+ _gaq.push(["_trackPageview", location.pathname+location.hash]);
+ _gaq.push(["b._trackPageview", location.pathname+location.hash]);
+ };
+ window.trackPageview();
+ window.trackEvent = function(category, action, opt_label, opt_value, opt_noninteraction) {
+ _gaq.push(["_trackEvent", category, action, opt_label, opt_value, opt_noninteraction]);
+ _gaq.push(["b._trackEvent", category, action, opt_label, opt_value, opt_noninteraction]);
+ };
+ }
+ </script>
</head>
<body style='display: none'>
@@ -54,10 +71,21 @@
(Press 'H' or navigate to hide this message.)
</div>
+ {{if .PlayEnabled}}
+ <script src='/play.js'></script>
+ {{end}}
+
+ <script>
+ (function() {
+ // Load Google Analytics tracking code on production site only.
+ if (window["location"] && window["location"]["hostname"] == "talks.golang.org") {
+ var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
+ ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
+ var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
+ }
+ })();
+ </script>
</body>
- {{if .PlayEnabled}}
- <script src='/play.js'></script>
- {{end}}
</html>
{{end}}
diff --git a/go/src/golang.org/x/tools/cmd/ssadump/main.go b/go/src/golang.org/x/tools/cmd/ssadump/main.go
index 75f1601..bdc4d0a 100644
--- a/go/src/golang.org/x/tools/cmd/ssadump/main.go
+++ b/go/src/golang.org/x/tools/cmd/ssadump/main.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
package main // import "golang.org/x/tools/cmd/ssadump"
@@ -9,6 +11,7 @@
"flag"
"fmt"
"go/build"
+ "go/types"
"os"
"runtime"
"runtime/pprof"
@@ -18,7 +21,6 @@
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
var (
@@ -147,7 +149,7 @@
// Run the interpreter.
if *runFlag {
- prog.BuildAll()
+ prog.Build()
var main *ssa.Package
pkgs := prog.AllPackages()
@@ -162,7 +164,7 @@
} else {
// Otherwise, run main.main.
for _, pkg := range pkgs {
- if pkg.Object.Name() == "main" {
+ if pkg.Pkg.Name() == "main" {
main = pkg
if main.Func("main") == nil {
return fmt.Errorf("no func main() in main package")
@@ -180,7 +182,7 @@
build.Default.GOARCH, runtime.GOARCH)
}
- interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args)
+ interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args)
}
return nil
}
diff --git a/go/src/golang.org/x/tools/cmd/ssadump/main14.go b/go/src/golang.org/x/tools/cmd/ssadump/main14.go
new file mode 100644
index 0000000..201ea81
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/ssadump/main14.go
@@ -0,0 +1,188 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
+package main // import "golang.org/x/tools/cmd/ssadump"
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "os"
+ "runtime"
+ "runtime/pprof"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/interp"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0)
+
+ testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")
+
+ runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.")
+
+ interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
+The value is a sequence of zero or more more of these letters:
+R disable [R]ecover() from panic; show interpreter crash instead.
+T [T]race execution of the program. Best for single-threaded programs!
+`)
+)
+
+const usage = `SSA builder and interpreter.
+Usage: ssadump [<flag> ...] <args> ...
+Use -help flag to display options.
+
+Examples:
+% ssadump -build=F hello.go # dump SSA form of a single package
+% ssadump -run -interp=T hello.go # interpret a program, with tracing
+% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely
+` + loader.FromArgsUsage +
+ `
+When -run is specified, ssadump will run the program.
+The entry point depends on the -test flag:
+if clear, it runs the first package named main.
+if set, it runs the tests of each package.
+`
+
+var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+
+ // If $GOMAXPROCS isn't set, use the full capacity of the machine.
+ // For small machines, use at least 4 threads.
+ if os.Getenv("GOMAXPROCS") == "" {
+ n := runtime.NumCPU()
+ if n < 4 {
+ n = 4
+ }
+ runtime.GOMAXPROCS(n)
+ }
+}
+
+func main() {
+ if err := doMain(); err != nil {
+ fmt.Fprintf(os.Stderr, "ssadump: %s\n", err)
+ os.Exit(1)
+ }
+}
+
+func doMain() error {
+ flag.Parse()
+ args := flag.Args()
+
+ conf := loader.Config{Build: &build.Default}
+
+ // Choose types.Sizes from conf.Build.
+ var wordSize int64 = 8
+ switch conf.Build.GOARCH {
+ case "386", "arm":
+ wordSize = 4
+ }
+ conf.TypeChecker.Sizes = &types.StdSizes{
+ MaxAlign: 8,
+ WordSize: wordSize,
+ }
+
+ var interpMode interp.Mode
+ for _, c := range *interpFlag {
+ switch c {
+ case 'T':
+ interpMode |= interp.EnableTracing
+ case 'R':
+ interpMode |= interp.DisableRecover
+ default:
+ return fmt.Errorf("unknown -interp option: '%c'", c)
+ }
+ }
+
+ if len(args) == 0 {
+ fmt.Fprint(os.Stderr, usage)
+ os.Exit(1)
+ }
+
+ // Profiling support.
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ // Use the initial packages from the command line.
+ args, err := conf.FromArgs(args, *testFlag)
+ if err != nil {
+ return err
+ }
+
+ // The interpreter needs the runtime package.
+ if *runFlag {
+ conf.Import("runtime")
+ }
+
+ // Load, parse and type-check the whole program.
+ iprog, err := conf.Load()
+ if err != nil {
+ return err
+ }
+
+ // Create and build SSA-form program representation.
+ prog := ssautil.CreateProgram(iprog, *modeFlag)
+
+ // Build and display only the initial packages
+ // (and synthetic wrappers), unless -run is specified.
+ for _, info := range iprog.InitialPackages() {
+ prog.Package(info.Pkg).Build()
+ }
+
+ // Run the interpreter.
+ if *runFlag {
+ prog.Build()
+
+ var main *ssa.Package
+ pkgs := prog.AllPackages()
+ if *testFlag {
+ // If -test, run all packages' tests.
+ if len(pkgs) > 0 {
+ main = prog.CreateTestMainPackage(pkgs...)
+ }
+ if main == nil {
+ return fmt.Errorf("no tests")
+ }
+ } else {
+ // Otherwise, run main.main.
+ for _, pkg := range pkgs {
+ if pkg.Pkg.Name() == "main" {
+ main = pkg
+ if main.Func("main") == nil {
+ return fmt.Errorf("no func main() in main package")
+ }
+ break
+ }
+ }
+ if main == nil {
+ return fmt.Errorf("no main package")
+ }
+ }
+
+ if runtime.GOARCH != build.Default.GOARCH {
+ return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)",
+ build.Default.GOARCH, runtime.GOARCH)
+ }
+
+ interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args)
+ }
+ return nil
+}
diff --git a/go/src/golang.org/x/tools/cmd/stress/stress.go b/go/src/golang.org/x/tools/cmd/stress/stress.go
index e0ff6b4..470d261 100644
--- a/go/src/golang.org/x/tools/cmd/stress/stress.go
+++ b/go/src/golang.org/x/tools/cmd/stress/stress.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TODO: syscall.SIGABRT is not defined for Plan 9 (issue #11975)
-
// +build !plan9
// The stress utility is intended for catching of episodic failures.
diff --git a/go/src/golang.org/x/tools/cmd/stringer/stringer.go b/go/src/golang.org/x/tools/cmd/stringer/stringer.go
index be87dec..992125a 100644
--- a/go/src/golang.org/x/tools/cmd/stringer/stringer.go
+++ b/go/src/golang.org/x/tools/cmd/stringer/stringer.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
// interface. Given the name of a (signed or unsigned) integer type T that has constants
// defined, stringer will create a new self-contained Go source file implementing
@@ -64,20 +66,18 @@
"fmt"
"go/ast"
"go/build"
+ exact "go/constant"
"go/format"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strings"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
-
- _ "golang.org/x/tools/go/gcimporter"
)
var (
@@ -128,7 +128,7 @@
}
// Print the header and package clause.
- g.Printf("// generated by stringer %s; DO NOT EDIT\n", strings.Join(os.Args[1:], " "))
+ g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT\n", strings.Join(os.Args[1:], " "))
g.Printf("\n")
g.Printf("package %s", g.pkg.name)
g.Printf("\n")
@@ -260,7 +260,7 @@
// check type-checks the package. The package must be OK to proceed.
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
pkg.defs = make(map[*ast.Ident]types.Object)
- config := types.Config{FakeImportC: true}
+ config := types.Config{Importer: importer.Default(), FakeImportC: true}
info := &types.Info{
Defs: pkg.defs,
}
diff --git a/go/src/golang.org/x/tools/cmd/stringer/stringer14.go b/go/src/golang.org/x/tools/cmd/stringer/stringer14.go
new file mode 100644
index 0000000..e0962e3
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/stringer/stringer14.go
@@ -0,0 +1,640 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
+// interface. Given the name of a (signed or unsigned) integer type T that has constants
+// defined, stringer will create a new self-contained Go source file implementing
+// func (t T) String() string
+// The file is created in the same package and directory as the package that defines T.
+// It has helpful defaults designed for use with go generate.
+//
+// Stringer works best with constants that are consecutive values such as created using iota,
+// but creates good code regardless. In the future it might also provide custom support for
+// constant sets that are bit patterns.
+//
+// For example, given this snippet,
+//
+// package painkiller
+//
+// type Pill int
+//
+// const (
+// Placebo Pill = iota
+// Aspirin
+// Ibuprofen
+// Paracetamol
+// Acetaminophen = Paracetamol
+// )
+//
+// running this command
+//
+// stringer -type=Pill
+//
+// in the same directory will create the file pill_string.go, in package painkiller,
+// containing a definition of
+//
+// func (Pill) String() string
+//
+// That method will translate the value of a Pill constant to the string representation
+// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
+// print the string "Aspirin".
+//
+// Typically this process would be run using go generate, like this:
+//
+// //go:generate stringer -type=Pill
+//
+// If multiple constants have the same value, the lexically first matching name will
+// be used (in the example, Acetaminophen will print as "Paracetamol").
+//
+// With no arguments, it processes the package in the current directory.
+// Otherwise, the arguments must name a single directory holding a Go package
+// or a set of Go source files that represent a single Go package.
+//
+// The -type flag accepts a comma-separated list of types so a single run can
+// generate methods for multiple types. The default output file is t_string.go,
+// where t is the lower-cased name of the first type listed. It can be overridden
+// with the -output flag.
+//
+package main // import "golang.org/x/tools/cmd/stringer"
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+
+ _ "golang.org/x/tools/go/gcimporter"
+)
+
+var (
+ typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
+ output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
+)
+
+// Usage is a replacement usage function for the flags package.
+func Usage() {
+ fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
+ fmt.Fprintf(os.Stderr, "\tstringer [flags[ -type T files... # Must be a single package\n")
+ fmt.Fprintf(os.Stderr, "For more information, see:\n")
+ fmt.Fprintf(os.Stderr, "\thttp://godoc.org/golang.org/x/tools/cmd/stringer\n")
+ fmt.Fprintf(os.Stderr, "Flags:\n")
+ flag.PrintDefaults()
+}
+
+func main() {
+ log.SetFlags(0)
+ log.SetPrefix("stringer: ")
+ flag.Usage = Usage
+ flag.Parse()
+ if len(*typeNames) == 0 {
+ flag.Usage()
+ os.Exit(2)
+ }
+ types := strings.Split(*typeNames, ",")
+
+ // We accept either one directory or a list of files. Which do we have?
+ args := flag.Args()
+ if len(args) == 0 {
+ // Default: process whole package in current directory.
+ args = []string{"."}
+ }
+
+ // Parse the package once.
+ var (
+ dir string
+ g Generator
+ )
+ if len(args) == 1 && isDirectory(args[0]) {
+ dir = args[0]
+ g.parsePackageDir(args[0])
+ } else {
+ dir = filepath.Dir(args[0])
+ g.parsePackageFiles(args)
+ }
+
+ // Print the header and package clause.
+ g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT\n", strings.Join(os.Args[1:], " "))
+ g.Printf("\n")
+ g.Printf("package %s", g.pkg.name)
+ g.Printf("\n")
+ g.Printf("import \"fmt\"\n") // Used by all methods.
+
+ // Run generate for each type.
+ for _, typeName := range types {
+ g.generate(typeName)
+ }
+
+ // Format the output.
+ src := g.format()
+
+ // Write to file.
+ outputName := *output
+ if outputName == "" {
+ baseName := fmt.Sprintf("%s_string.go", types[0])
+ outputName = filepath.Join(dir, strings.ToLower(baseName))
+ }
+ err := ioutil.WriteFile(outputName, src, 0644)
+ if err != nil {
+ log.Fatalf("writing output: %s", err)
+ }
+}
+
+// isDirectory reports whether the named file is a directory.
+func isDirectory(name string) bool {
+ info, err := os.Stat(name)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return info.IsDir()
+}
+
+// Generator holds the state of the analysis. Primarily used to buffer
+// the output for format.Source.
+type Generator struct {
+ buf bytes.Buffer // Accumulated output.
+ pkg *Package // Package we are scanning.
+}
+
+func (g *Generator) Printf(format string, args ...interface{}) {
+ fmt.Fprintf(&g.buf, format, args...)
+}
+
+// File holds a single parsed file and associated data.
+type File struct {
+ pkg *Package // Package to which this file belongs.
+ file *ast.File // Parsed AST.
+ // These fields are reset for each type being generated.
+ typeName string // Name of the constant type.
+ values []Value // Accumulator for constant values of that type.
+}
+
+type Package struct {
+ dir string
+ name string
+ defs map[*ast.Ident]types.Object
+ files []*File
+ typesPkg *types.Package
+}
+
+// parsePackageDir parses the package residing in the directory.
+func (g *Generator) parsePackageDir(directory string) {
+ pkg, err := build.Default.ImportDir(directory, 0)
+ if err != nil {
+ log.Fatalf("cannot process directory %s: %s", directory, err)
+ }
+ var names []string
+ names = append(names, pkg.GoFiles...)
+ names = append(names, pkg.CgoFiles...)
+ // TODO: Need to think about constants in test files. Maybe write type_string_test.go
+ // in a separate pass? For later.
+ // names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
+ names = append(names, pkg.SFiles...)
+ names = prefixDirectory(directory, names)
+ g.parsePackage(directory, names, nil)
+}
+
+// parsePackageFiles parses the package occupying the named files.
+func (g *Generator) parsePackageFiles(names []string) {
+ g.parsePackage(".", names, nil)
+}
+
+// prefixDirectory places the directory name on the beginning of each name in the list.
+func prefixDirectory(directory string, names []string) []string {
+ if directory == "." {
+ return names
+ }
+ ret := make([]string, len(names))
+ for i, name := range names {
+ ret[i] = filepath.Join(directory, name)
+ }
+ return ret
+}
+
+// parsePackage analyzes the single package constructed from the named files.
+// If text is non-nil, it is a string to be used instead of the content of the file,
+// to be used for testing. parsePackage exits if there is an error.
+func (g *Generator) parsePackage(directory string, names []string, text interface{}) {
+ var files []*File
+ var astFiles []*ast.File
+ g.pkg = new(Package)
+ fs := token.NewFileSet()
+ for _, name := range names {
+ if !strings.HasSuffix(name, ".go") {
+ continue
+ }
+ parsedFile, err := parser.ParseFile(fs, name, text, 0)
+ if err != nil {
+ log.Fatalf("parsing package: %s: %s", name, err)
+ }
+ astFiles = append(astFiles, parsedFile)
+ files = append(files, &File{
+ file: parsedFile,
+ pkg: g.pkg,
+ })
+ }
+ if len(astFiles) == 0 {
+ log.Fatalf("%s: no buildable Go files", directory)
+ }
+ g.pkg.name = astFiles[0].Name.Name
+ g.pkg.files = files
+ g.pkg.dir = directory
+ // Type check the package.
+ g.pkg.check(fs, astFiles)
+}
+
+// check type-checks the package. The package must be OK to proceed.
+func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
+ pkg.defs = make(map[*ast.Ident]types.Object)
+ config := types.Config{FakeImportC: true}
+ info := &types.Info{
+ Defs: pkg.defs,
+ }
+ typesPkg, err := config.Check(pkg.dir, fs, astFiles, info)
+ if err != nil {
+ log.Fatalf("checking package: %s", err)
+ }
+ pkg.typesPkg = typesPkg
+}
+
+// generate produces the String method for the named type.
+func (g *Generator) generate(typeName string) {
+ values := make([]Value, 0, 100)
+ for _, file := range g.pkg.files {
+ // Set the state for this run of the walker.
+ file.typeName = typeName
+ file.values = nil
+ if file.file != nil {
+ ast.Inspect(file.file, file.genDecl)
+ values = append(values, file.values...)
+ }
+ }
+
+ if len(values) == 0 {
+ log.Fatalf("no values defined for type %s", typeName)
+ }
+ runs := splitIntoRuns(values)
+ // The decision of which pattern to use depends on the number of
+ // runs in the numbers. If there's only one, it's easy. For more than
+ // one, there's a tradeoff between complexity and size of the data
+ // and code vs. the simplicity of a map. A map takes more space,
+ // but so does the code. The decision here (crossover at 10) is
+ // arbitrary, but considers that for large numbers of runs the cost
+ // of the linear scan in the switch might become important, and
+ // rather than use yet another algorithm such as binary search,
+ // we punt and use a map. In any case, the likelihood of a map
+ // being necessary for any realistic example other than bitmasks
+ // is very low. And bitmasks probably deserve their own analysis,
+ // to be done some other day.
+ switch {
+ case len(runs) == 1:
+ g.buildOneRun(runs, typeName)
+ case len(runs) <= 10:
+ g.buildMultipleRuns(runs, typeName)
+ default:
+ g.buildMap(runs, typeName)
+ }
+}
+
+// splitIntoRuns breaks the values into runs of contiguous sequences.
+// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
+// The input slice is known to be non-empty.
+func splitIntoRuns(values []Value) [][]Value {
+ // We use stable sort so the lexically first name is chosen for equal elements.
+ sort.Stable(byValue(values))
+ // Remove duplicates. Stable sort has put the one we want to print first,
+ // so use that one. The String method won't care about which named constant
+ // was the argument, so the first name for the given value is the only one to keep.
+ // We need to do this because identical values would cause the switch or map
+ // to fail to compile.
+ j := 1
+ for i := 1; i < len(values); i++ {
+ if values[i].value != values[i-1].value {
+ values[j] = values[i]
+ j++
+ }
+ }
+ values = values[:j]
+ runs := make([][]Value, 0, 10)
+ for len(values) > 0 {
+ // One contiguous sequence per outer loop.
+ i := 1
+ for i < len(values) && values[i].value == values[i-1].value+1 {
+ i++
+ }
+ runs = append(runs, values[:i])
+ values = values[i:]
+ }
+ return runs
+}
+
+// format returns the gofmt-ed contents of the Generator's buffer.
+func (g *Generator) format() []byte {
+ src, err := format.Source(g.buf.Bytes())
+ if err != nil {
+ // Should never happen, but can arise when developing this code.
+ // The user can compile the output to see the error.
+ log.Printf("warning: internal error: invalid Go generated: %s", err)
+ log.Printf("warning: compile the package to analyze the error")
+ return g.buf.Bytes()
+ }
+ return src
+}
+
+// Value represents a declared constant.
+type Value struct {
+ name string // The name of the constant.
+ // The value is stored as a bit pattern alone. The boolean tells us
+ // whether to interpret it as an int64 or a uint64; the only place
+ // this matters is when sorting.
+ // Much of the time the str field is all we need; it is printed
+ // by Value.String.
+ value uint64 // Will be converted to int64 when needed.
+ signed bool // Whether the constant is a signed type.
+ str string // The string representation given by the "go/exact" package.
+}
+
+func (v *Value) String() string {
+ return v.str
+}
+
+// byValue lets us sort the constants into increasing order.
+// We take care in the Less method to sort in signed or unsigned order,
+// as appropriate.
+type byValue []Value
+
+func (b byValue) Len() int { return len(b) }
+func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byValue) Less(i, j int) bool {
+ if b[i].signed {
+ return int64(b[i].value) < int64(b[j].value)
+ }
+ return b[i].value < b[j].value
+}
+
+// genDecl processes one declaration clause.
+func (f *File) genDecl(node ast.Node) bool {
+ decl, ok := node.(*ast.GenDecl)
+ if !ok || decl.Tok != token.CONST {
+ // We only care about const declarations.
+ return true
+ }
+ // The name of the type of the constants we are declaring.
+ // Can change if this is a multi-element declaration.
+ typ := ""
+ // Loop over the elements of the declaration. Each element is a ValueSpec:
+ // a list of names possibly followed by a type, possibly followed by values.
+ // If the type and value are both missing, we carry down the type (and value,
+ // but the "go/types" package takes care of that).
+ for _, spec := range decl.Specs {
+ vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
+ if vspec.Type == nil && len(vspec.Values) > 0 {
+ // "X = 1". With no type but a value, the constant is untyped.
+ // Skip this vspec and reset the remembered type.
+ typ = ""
+ continue
+ }
+ if vspec.Type != nil {
+ // "X T". We have a type. Remember it.
+ ident, ok := vspec.Type.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ typ = ident.Name
+ }
+ if typ != f.typeName {
+ // This is not the type we're looking for.
+ continue
+ }
+ // We now have a list of names (from one line of source code) all being
+ // declared with the desired type.
+ // Grab their names and actual values and store them in f.values.
+ for _, name := range vspec.Names {
+ if name.Name == "_" {
+ continue
+ }
+ // This dance lets the type checker find the values for us. It's a
+ // bit tricky: look up the object declared by the name, find its
+ // types.Const, and extract its value.
+ obj, ok := f.pkg.defs[name]
+ if !ok {
+ log.Fatalf("no value for constant %s", name)
+ }
+ info := obj.Type().Underlying().(*types.Basic).Info()
+ if info&types.IsInteger == 0 {
+ log.Fatalf("can't handle non-integer constant type %s", typ)
+ }
+ value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
+ if value.Kind() != exact.Int {
+ log.Fatalf("can't happen: constant is not an integer %s", name)
+ }
+ i64, isInt := exact.Int64Val(value)
+ u64, isUint := exact.Uint64Val(value)
+ if !isInt && !isUint {
+ log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
+ }
+ if !isInt {
+ u64 = uint64(i64)
+ }
+ v := Value{
+ name: name.Name,
+ value: u64,
+ signed: info&types.IsUnsigned == 0,
+ str: value.String(),
+ }
+ f.values = append(f.values, v)
+ }
+ }
+ return false
+}
+
+// Helpers
+
+// usize returns the number of bits of the smallest unsigned integer
+// type that will hold n. Used to create the smallest possible slice of
+// integers to use as indexes into the concatenated strings.
+func usize(n int) int {
+ switch {
+ case n < 1<<8:
+ return 8
+ case n < 1<<16:
+ return 16
+ default:
+ // 2^32 is enough constants for anyone.
+ return 32
+ }
+}
+
+// declareIndexAndNameVars declares the index slices and concatenated names
+// strings representing the runs of values.
+func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
+ var indexes, names []string
+ for i, run := range runs {
+ index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
+ indexes = append(indexes, index)
+ names = append(names, name)
+ }
+ g.Printf("const (\n")
+ for _, name := range names {
+ g.Printf("\t%s\n", name)
+ }
+ g.Printf(")\n\n")
+ g.Printf("var (")
+ for _, index := range indexes {
+ g.Printf("\t%s\n", index)
+ }
+ g.Printf(")\n\n")
+}
+
+// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
+func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
+ index, name := g.createIndexAndNameDecl(run, typeName, "")
+ g.Printf("const %s\n", name)
+ g.Printf("var %s\n", index)
+}
+
+// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
+func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
+ b := new(bytes.Buffer)
+ indexes := make([]int, len(run))
+ for i := range run {
+ b.WriteString(run[i].name)
+ indexes[i] = b.Len()
+ }
+ nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
+ nameLen := b.Len()
+ b.Reset()
+ fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
+ for i, v := range indexes {
+ if i > 0 {
+ fmt.Fprintf(b, ", ")
+ }
+ fmt.Fprintf(b, "%d", v)
+ }
+ fmt.Fprintf(b, "}")
+ return b.String(), nameConst
+}
+
+// declareNameVars declares the concatenated names string representing all the values in the runs.
+func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
+ g.Printf("const _%s_name%s = \"", typeName, suffix)
+ for _, run := range runs {
+ for i := range run {
+ g.Printf("%s", run[i].name)
+ }
+ }
+ g.Printf("\"\n")
+}
+
+// buildOneRun generates the variables and String method for a single run of contiguous values.
+func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
+ values := runs[0]
+ g.Printf("\n")
+ g.declareIndexAndNameVar(values, typeName)
+ // The generated code is simple enough to write as a Printf format.
+ lessThanZero := ""
+ if values[0].signed {
+ lessThanZero = "i < 0 || "
+ }
+ if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
+ g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
+ } else {
+ g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
+ }
+}
+
+// Arguments to format are:
+// [1]: type name
+// [2]: size of index element (8 for uint8 etc.)
+// [3]: less than zero check (for signed types)
+const stringOneRun = `func (i %[1]s) String() string {
+ if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
+ return fmt.Sprintf("%[1]s(%%d)", i)
+ }
+ return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
+}
+`
+
+// Arguments to format are:
+// [1]: type name
+// [2]: lowest defined value for type, as a string
+// [3]: size of index element (8 for uint8 etc.)
+// [4]: less than zero check (for signed types)
+/*
+ */
+const stringOneRunWithOffset = `func (i %[1]s) String() string {
+ i -= %[2]s
+ if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
+ return fmt.Sprintf("%[1]s(%%d)", i + %[2]s)
+ }
+ return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
+}
+`
+
+// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
+// For this pattern, a single Printf format won't do.
+func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
+ g.Printf("\n")
+ g.declareIndexAndNameVars(runs, typeName)
+ g.Printf("func (i %s) String() string {\n", typeName)
+ g.Printf("\tswitch {\n")
+ for i, values := range runs {
+ if len(values) == 1 {
+ g.Printf("\tcase i == %s:\n", &values[0])
+ g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
+ continue
+ }
+ g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
+ if values[0].value != 0 {
+ g.Printf("\t\ti -= %s\n", &values[0])
+ }
+ g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
+ typeName, i, typeName, i, typeName, i)
+ }
+ g.Printf("\tdefault:\n")
+ g.Printf("\t\treturn fmt.Sprintf(\"%s(%%d)\", i)\n", typeName)
+ g.Printf("\t}\n")
+ g.Printf("}\n")
+}
+
+// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
+// It's a rare situation but has simple code.
+func (g *Generator) buildMap(runs [][]Value, typeName string) {
+ g.Printf("\n")
+ g.declareNameVars(runs, typeName, "")
+ g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
+ n := 0
+ for _, values := range runs {
+ for _, value := range values {
+ g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
+ n += len(value.name)
+ }
+ }
+ g.Printf("}\n\n")
+ g.Printf(stringMap, typeName)
+}
+
+// Argument to format is the type name.
+const stringMap = `func (i %[1]s) String() string {
+ if str, ok := _%[1]s_map[i]; ok {
+ return str
+ }
+ return fmt.Sprintf("%[1]s(%%d)", i)
+}
+`
diff --git a/go/src/golang.org/x/tools/cmd/tipgodoc/Dockerfile b/go/src/golang.org/x/tools/cmd/tip/Dockerfile
similarity index 63%
rename from go/src/golang.org/x/tools/cmd/tipgodoc/Dockerfile
rename to go/src/golang.org/x/tools/cmd/tip/Dockerfile
index 760ca0b..8364cdb 100644
--- a/go/src/golang.org/x/tools/cmd/tipgodoc/Dockerfile
+++ b/go/src/golang.org/x/tools/cmd/tip/Dockerfile
@@ -1,13 +1,13 @@
-FROM golang:1.4.2
+FROM golang:1.5
RUN apt-get update && apt-get install --no-install-recommends -y -q build-essential git
# golang puts its go install here (weird but true)
-ENV GOROOT_BOOTSTRAP /usr/src/go
+ENV GOROOT_BOOTSTRAP /usr/local/go
# golang sets GOPATH=/go
-ADD . /go/src/tipgodoc
-RUN go install tipgodoc
-ENTRYPOINT ["/go/bin/tipgodoc"]
+ADD . /go/src/tip
+RUN go install tip
+ENTRYPOINT ["/go/bin/tip"]
# Kubernetes expects us to listen on port 8080
EXPOSE 8080
diff --git a/go/src/golang.org/x/tools/cmd/tip/README b/go/src/golang.org/x/tools/cmd/tip/README
new file mode 100644
index 0000000..5fe1cfe
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/tip/README
@@ -0,0 +1,14 @@
+1. Deploy the app.
+
+ To deploy tip.golang.org:
+ $ gcloud --project golang-org preview app deploy --no-promote godoc.yaml
+
+ To deploy talks.golang.org:
+ $ gcloud --project golang-org preview app deploy --no-promote talks.yaml
+
+2. Wait until the deployed version is serving requests.
+
+3. Go to the developer console and upgrade the default version.
+ https://console.developers.google.com/appengine/versions?project=golang-org&moduleId=tip
+
+4. Clean up any old versions (they continue to use at least one instance).
diff --git a/go/src/golang.org/x/tools/cmd/tip/godoc.go b/go/src/golang.org/x/tools/cmd/tip/godoc.go
new file mode 100644
index 0000000..ab3c3d2
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/tip/godoc.go
@@ -0,0 +1,72 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+type godocBuilder struct {
+}
+
+func (b godocBuilder) Signature(heads map[string]string) string {
+ return heads["go"] + "-" + heads["tools"]
+}
+
+func (b godocBuilder) Init(dir, hostport string, heads map[string]string) (*exec.Cmd, error) {
+ goDir := filepath.Join(dir, "go")
+ toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
+ if err := checkout(repoURL+"go", heads["go"], goDir); err != nil {
+ return nil, err
+ }
+ if err := checkout(repoURL+"tools", heads["tools"], toolsDir); err != nil {
+ return nil, err
+ }
+
+ make := exec.Command(filepath.Join(goDir, "src/make.bash"))
+ make.Dir = filepath.Join(goDir, "src")
+ if err := runErr(make); err != nil {
+ return nil, err
+ }
+ goBin := filepath.Join(goDir, "bin/go")
+ install := exec.Command(goBin, "install", "golang.org/x/tools/cmd/godoc")
+ install.Env = []string{
+ "GOROOT=" + goDir,
+ "GOPATH=" + filepath.Join(dir, "gopath"),
+ "GOROOT_BOOTSTRAP=" + os.Getenv("GOROOT_BOOTSTRAP"),
+ }
+ if err := runErr(install); err != nil {
+ return nil, err
+ }
+
+ godocBin := filepath.Join(goDir, "bin/godoc")
+ godoc := exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s")
+ godoc.Env = []string{"GOROOT=" + goDir}
+ // TODO(adg): log this somewhere useful
+ godoc.Stdout = os.Stdout
+ godoc.Stderr = os.Stderr
+ if err := godoc.Start(); err != nil {
+ return nil, err
+ }
+ return godoc, nil
+}
+
+var indexingMsg = []byte("Indexing in progress: result may be inaccurate")
+
+func (b godocBuilder) HealthCheck(hostport string) error {
+ body, err := getOK(fmt.Sprintf("http://%v/search?q=FALLTHROUGH", hostport))
+ if err != nil {
+ return err
+ }
+ if bytes.Contains(body, indexingMsg) {
+ return errors.New("still indexing")
+ }
+ return nil
+}
diff --git a/go/src/golang.org/x/tools/cmd/tip/godoc.yaml b/go/src/golang.org/x/tools/cmd/tip/godoc.yaml
new file mode 100644
index 0000000..5f6d9dc
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/tip/godoc.yaml
@@ -0,0 +1,18 @@
+module: tip
+runtime: custom
+vm: true
+
+automatic_scaling:
+ min_num_instances: 1
+ max_num_instances: 2
+
+env_variables:
+ TIP_BUILDER: 'godoc'
+
+health_check:
+ enable_health_check: True
+ check_interval_sec: 5
+ timeout_sec: 4
+ unhealthy_threshold: 2
+ healthy_threshold: 2
+ restart_threshold: 240
diff --git a/go/src/golang.org/x/tools/cmd/tip/talks.go b/go/src/golang.org/x/tools/cmd/tip/talks.go
new file mode 100644
index 0000000..15167e1
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/tip/talks.go
@@ -0,0 +1,73 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+)
+
+type talksBuilder struct {
+}
+
+func (b talksBuilder) Signature(heads map[string]string) string {
+ return heads["talks"]
+}
+
+const talksToolsRev = "ac6d9c1d842f9b6482f39f7a172e0251a0f7cbc0"
+
+func (b talksBuilder) Init(dir, hostport string, heads map[string]string) (*exec.Cmd, error) {
+ toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
+ if err := checkout(repoURL+"tools", talksToolsRev, toolsDir); err != nil {
+ return nil, err
+ }
+ talksDir := filepath.Join(dir, "gopath/src/golang.org/x/talks")
+ if err := checkout(repoURL+"talks", heads["talks"], talksDir); err != nil {
+ return nil, err
+ }
+
+ goDir := os.Getenv("GOROOT_BOOTSTRAP")
+ if goDir == "" {
+ goDir = runtime.GOROOT()
+ }
+ goBin := filepath.Join(goDir, "bin/go")
+ goPath := filepath.Join(dir, "gopath")
+ presentPath := "golang.org/x/tools/cmd/present"
+ install := exec.Command(goBin, "install", "-tags=appenginevm", presentPath)
+ install.Env = []string{"GOROOT=" + goDir, "GOPATH=" + goPath}
+ if err := runErr(install); err != nil {
+ return nil, err
+ }
+
+ talksBin := filepath.Join(goPath, "bin/present")
+ presentSrc := filepath.Join(goPath, "src", presentPath)
+ present := exec.Command(talksBin, "-http="+hostport, "-base="+presentSrc)
+ present.Dir = talksDir
+ // TODO(adg): log this somewhere useful
+ present.Stdout = os.Stdout
+ present.Stderr = os.Stderr
+ if err := present.Start(); err != nil {
+ return nil, err
+ }
+ return present, nil
+}
+
+var talksMsg = []byte("Talks - The Go Programming Language")
+
+func (b talksBuilder) HealthCheck(hostport string) error {
+ body, err := getOK(fmt.Sprintf("http://%v/", hostport))
+ if err != nil {
+ return err
+ }
+ if !bytes.Contains(body, talksMsg) {
+ return errors.New("couldn't match string")
+ }
+ return nil
+}
diff --git a/go/src/golang.org/x/tools/cmd/tip/talks.yaml b/go/src/golang.org/x/tools/cmd/tip/talks.yaml
new file mode 100644
index 0000000..edc90a5
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/tip/talks.yaml
@@ -0,0 +1,18 @@
+module: talks
+runtime: custom
+vm: true
+
+automatic_scaling:
+ min_num_instances: 1
+ max_num_instances: 5
+
+env_variables:
+ TIP_BUILDER: 'talks'
+
+health_check:
+ enable_health_check: True
+ check_interval_sec: 5
+ timeout_sec: 4
+ unhealthy_threshold: 2
+ healthy_threshold: 2
+ restart_threshold: 240
diff --git a/go/src/golang.org/x/tools/cmd/tipgodoc/tip.go b/go/src/golang.org/x/tools/cmd/tip/tip.go
similarity index 65%
rename from go/src/golang.org/x/tools/cmd/tipgodoc/tip.go
rename to go/src/golang.org/x/tools/cmd/tip/tip.go
index ab3b8e6..099b75e 100644
--- a/go/src/golang.org/x/tools/cmd/tipgodoc/tip.go
+++ b/go/src/golang.org/x/tools/cmd/tip/tip.go
@@ -8,8 +8,8 @@
import (
"bufio"
- "bytes"
"encoding/json"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -30,12 +30,24 @@
startTimeout = 5 * time.Minute
)
-var indexingMsg = []byte("Indexing in progress: result may be inaccurate")
-
func main() {
- p := new(Proxy)
+ const k = "TIP_BUILDER"
+ var b Builder
+ switch os.Getenv(k) {
+ case "godoc":
+ b = godocBuilder{}
+ case "talks":
+ b = talksBuilder{}
+ default:
+ log.Fatalf("Unknown %v value: %q", k, os.Getenv(k))
+ }
+
+ p := &Proxy{builder: b}
go p.run()
http.Handle("/", p)
+ http.HandleFunc("/_ah/health", p.serveHealthCheck)
+
+ log.Print("Starting up")
if err := http.ListenAndServe(":8080", nil); err != nil {
p.stop()
@@ -46,12 +58,21 @@
// Proxy implements the tip.golang.org server: a reverse-proxy
// that builds and runs godoc instances showing the latest docs.
type Proxy struct {
- mu sync.Mutex // protects the followin'
- proxy http.Handler
- cur string // signature of gorepo+toolsrepo
- cmd *exec.Cmd // live godoc instance, or nil for none
- side string
- err error
+ builder Builder
+
+ mu sync.Mutex // protects the followin'
+ proxy http.Handler
+ cur string // signature of gorepo+toolsrepo
+ cmd *exec.Cmd // live godoc instance, or nil for none
+ side string
+ hostport string // host and port of the live instance
+ err error
+}
+
+type Builder interface {
+ Signature(heads map[string]string) string
+ Init(dir, hostport string, heads map[string]string) (*exec.Cmd, error)
+ HealthCheck(hostport string) error
}
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -64,7 +85,7 @@
err := p.err
p.mu.Unlock()
if proxy == nil {
- s := "tip.golang.org is starting up"
+ s := "starting up"
if err != nil {
s = err.Error()
}
@@ -80,6 +101,25 @@
fmt.Fprintf(w, "side=%v\ncurrent=%v\nerror=%v\n", p.side, p.cur, p.err)
}
+func (p *Proxy) serveHealthCheck(w http.ResponseWriter, r *http.Request) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ // NOTE: Status 502, 503, 504 are the only status codes that signify an unhealthy app.
+ // So long as this handler returns one of those codes, this instance will not be sent any requests.
+ if p.proxy == nil {
+ log.Printf("Health check: not ready")
+ http.Error(w, "Not ready", http.StatusServiceUnavailable)
+ return
+ }
+
+ if err := p.builder.HealthCheck(p.hostport); err != nil {
+ log.Printf("Health check failed: %v", err)
+ http.Error(w, "Health check failed", http.StatusServiceUnavailable)
+ return
+ }
+ io.WriteString(w, "ok")
+}
+
// run runs in its own goroutine.
func (p *Proxy) run() {
p.side = "a"
@@ -104,7 +144,7 @@
return
}
- sig := heads["go"] + "-" + heads["tools"]
+ sig := p.builder.Signature(heads)
p.mu.Lock()
changes := sig != p.cur
@@ -121,7 +161,25 @@
newSide = "a"
}
- cmd, hostport, err := initSide(newSide, heads["go"], heads["tools"])
+ dir := filepath.Join(os.TempDir(), "tip", newSide)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ p.err = err
+ return
+ }
+ hostport := "localhost:8081"
+ if newSide == "b" {
+ hostport = "localhost:8082"
+ }
+ cmd, err := p.builder.Init(dir, hostport, heads)
+ if err == nil {
+ go func() {
+ // TODO(adg,bradfitz): be smarter about dead processes
+ if err := cmd.Wait(); err != nil {
+ log.Printf("process in %v exited: %v", dir, err)
+ }
+ }()
+ err = waitReady(p.builder, hostport)
+ }
p.mu.Lock()
defer p.mu.Unlock()
@@ -139,80 +197,23 @@
}
p.proxy = httputil.NewSingleHostReverseProxy(u)
p.side = newSide
+ p.hostport = hostport
if p.cmd != nil {
p.cmd.Process.Kill()
}
p.cmd = cmd
}
-func initSide(side, goHash, toolsHash string) (godoc *exec.Cmd, hostport string, err error) {
- dir := filepath.Join(os.TempDir(), "tipgodoc", side)
- if err := os.MkdirAll(dir, 0755); err != nil {
- return nil, "", err
- }
-
- goDir := filepath.Join(dir, "go")
- toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
- if err := checkout(repoURL+"go", goHash, goDir); err != nil {
- return nil, "", err
- }
- if err := checkout(repoURL+"tools", toolsHash, toolsDir); err != nil {
- return nil, "", err
- }
-
- make := exec.Command(filepath.Join(goDir, "src/make.bash"))
- make.Dir = filepath.Join(goDir, "src")
- if err := runErr(make); err != nil {
- return nil, "", err
- }
- goBin := filepath.Join(goDir, "bin/go")
- install := exec.Command(goBin, "install", "golang.org/x/tools/cmd/godoc")
- install.Env = []string{
- "GOROOT=" + goDir,
- "GOPATH=" + filepath.Join(dir, "gopath"),
- "GOROOT_BOOTSTRAP=" + os.Getenv("GOROOT_BOOTSTRAP"),
- }
- if err := runErr(install); err != nil {
- return nil, "", err
- }
-
- godocBin := filepath.Join(goDir, "bin/godoc")
- hostport = "localhost:8081"
- if side == "b" {
- hostport = "localhost:8082"
- }
- godoc = exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s")
- godoc.Env = []string{"GOROOT=" + goDir}
- // TODO(adg): log this somewhere useful
- godoc.Stdout = os.Stdout
- godoc.Stderr = os.Stderr
- if err := godoc.Start(); err != nil {
- return nil, "", err
- }
- go func() {
- // TODO(bradfitz): tell the proxy that this side is dead
- if err := godoc.Wait(); err != nil {
- log.Printf("side %v exited: %v", side, err)
- }
- }()
-
+func waitReady(b Builder, hostport string) error {
+ var err error
deadline := time.Now().Add(startTimeout)
for time.Now().Before(deadline) {
+ if err = b.HealthCheck(hostport); err == nil {
+ return nil
+ }
time.Sleep(time.Second)
- var res *http.Response
- res, err = http.Get(fmt.Sprintf("http://%v/search?q=FALLTHROUGH", hostport))
- if err != nil {
- continue
- }
- rbody, err := ioutil.ReadAll(res.Body)
- res.Body.Close()
- if err == nil && res.StatusCode == http.StatusOK &&
- !bytes.Contains(rbody, indexingMsg) {
- return godoc, hostport, nil
- }
}
- godoc.Process.Kill()
- return nil, "", fmt.Errorf("timed out waiting for side %v at %v (%v)", side, hostport, err)
+ return fmt.Errorf("timed out waiting for process at %v: %v", hostport, err)
}
func runErr(cmd *exec.Cmd) error {
@@ -296,3 +297,19 @@
}
return m
}
+
+func getOK(url string) (body []byte, err error) {
+ res, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ body, err = ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ return nil, err
+ }
+ if res.StatusCode != http.StatusOK {
+ return nil, errors.New(res.Status)
+ }
+ return body, nil
+}
diff --git a/go/src/golang.org/x/tools/cmd/tipgodoc/README b/go/src/golang.org/x/tools/cmd/tipgodoc/README
deleted file mode 100644
index 602e546..0000000
--- a/go/src/golang.org/x/tools/cmd/tipgodoc/README
+++ /dev/null
@@ -1,3 +0,0 @@
-To deploy as an App Engine Manged VM, use gcloud:
-
- $ gcloud --project golang-org preview app deploy .
diff --git a/go/src/golang.org/x/tools/cmd/tipgodoc/app.yaml b/go/src/golang.org/x/tools/cmd/tipgodoc/app.yaml
deleted file mode 100644
index 59e5a06..0000000
--- a/go/src/golang.org/x/tools/cmd/tipgodoc/app.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-application: golang-org
-version: tip
-runtime: custom
-api_version: go1
-vm: true
-
-manual_scaling:
- instances: 1
-
-handlers:
-- url: /.*
- script: _go_app
-
-health_check:
- enable_health_check: False
diff --git a/go/src/golang.org/x/tools/cmd/vet/doc.go b/go/src/golang.org/x/tools/cmd/vet/doc.go
index a19de3f..012f88b 100644
--- a/go/src/golang.org/x/tools/cmd/vet/doc.go
+++ b/go/src/golang.org/x/tools/cmd/vet/doc.go
@@ -37,49 +37,6 @@
Available checks:
-Printf family
-
-Flag: -printf
-
-Suspicious calls to functions in the Printf family, including any functions
-with these names, disregarding case:
- Print Printf Println
- Fprint Fprintf Fprintln
- Sprint Sprintf Sprintln
- Error Errorf
- Fatal Fatalf
- Log Logf
- Panic Panicf Panicln
-If the function name ends with an 'f', the function is assumed to take
-a format descriptor string in the manner of fmt.Printf. If not, vet
-complains about arguments that look like format descriptor strings.
-
-It also checks for errors such as using a Writer as the first argument of
-Printf.
-
-Methods
-
-Flag: -methods
-
-Non-standard signatures for methods with familiar names, including:
- Format GobEncode GobDecode MarshalJSON MarshalXML
- Peek ReadByte ReadFrom ReadRune Scan Seek
- UnmarshalJSON UnreadByte UnreadRune WriteByte
- WriteTo
-
-Struct tags
-
-Flag: -structtags
-
-Struct tags that do not follow the format understood by reflect.StructTag.Get.
-Well-known encoding struct tags (json, xml) used with unexported fields.
-
-Unkeyed composite literals
-
-Flag: -composites
-
-Composite struct literals that do not use the field-keyed syntax.
-
Assembly declarations
Flag: -asmdecl
@@ -110,36 +67,92 @@
Badly formed or misplaced +build tags.
+Unkeyed composite literals
+
+Flag: -composites
+
+Composite struct literals that do not use the field-keyed syntax.
+
Copying locks
Flag: -copylocks
Locks that are erroneously passed by value.
+Documentation examples
+
+Flag: -example
+
+Mistakes involving example tests, including examples with incorrect names or
+function signatures, or that document identifiers not in the package.
+
+Methods
+
+Flag: -methods
+
+Non-standard signatures for methods with familiar names, including:
+ Format GobEncode GobDecode MarshalJSON MarshalXML
+ Peek ReadByte ReadFrom ReadRune Scan Seek
+ UnmarshalJSON UnreadByte UnreadRune WriteByte
+ WriteTo
+
Nil function comparison
Flag: -nilfunc
Comparisons between functions and nil.
+Printf family
+
+Flag: -printf
+
+Suspicious calls to functions in the Printf family, including any functions
+with these names, disregarding case:
+ Print Printf Println
+ Fprint Fprintf Fprintln
+ Sprint Sprintf Sprintln
+ Error Errorf
+ Fatal Fatalf
+ Log Logf
+ Panic Panicf Panicln
+If the function name ends with an 'f', the function is assumed to take
+a format descriptor string in the manner of fmt.Printf. If not, vet
+complains about arguments that look like format descriptor strings.
+
+It also checks for errors such as using a Writer as the first argument of
+Printf.
+
Range loop variables
Flag: -rangeloops
Incorrect uses of range loop variables in closures.
-Unreachable code
-
-Flag: -unreachable
-
-Unreachable code.
-
Shadowed variables
Flag: -shadow=false (experimental; must be set explicitly)
Variables that may have been unintentionally shadowed.
+Shifts
+
+Flag: -shift
+
+Shifts equal to or longer than the variable's length.
+
+Struct tags
+
+Flag: -structtags
+
+Struct tags that do not follow the format understood by reflect.StructTag.Get.
+Well-known encoding struct tags (json, xml) used with unexported fields.
+
+Unreachable code
+
+Flag: -unreachable
+
+Unreachable code.
+
Misuse of unsafe Pointers
Flag: -unsafeptr
@@ -159,12 +172,6 @@
fmt.Sprintf and methods like String and Error. The flags -unusedfuncs
and -unusedstringmethods control the set.
-Shifts
-
-Flag: -shift
-
-Shifts equal to or longer than the variable's length.
-
Other flags
These flags configure the behavior of vet:
diff --git a/go/src/golang.org/x/tools/cmd/vet/example.go b/go/src/golang.org/x/tools/cmd/vet/example.go
new file mode 100644
index 0000000..585d388
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/vet/example.go
@@ -0,0 +1,125 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "golang.org/x/tools/go/types"
+)
+
+func init() {
+ register("example",
+ "check for common mistaken usages of documentation examples",
+ checkExample,
+ funcDecl)
+}
+
+func isExampleSuffix(s string) bool {
+ r, size := utf8.DecodeRuneInString(s)
+ return size > 0 && unicode.IsLower(r)
+}
+
+// checkExample walks the documentation example functions checking for common
+// mistakes of misnamed functions, failure to map functions to existing
+// identifiers, etc.
+func checkExample(f *File, node ast.Node) {
+ if !strings.HasSuffix(f.name, "_test.go") {
+ return
+ }
+ var (
+ pkg = f.pkg
+ pkgName = pkg.typesPkg.Name()
+ scopes = []*types.Scope{pkg.typesPkg.Scope()}
+ lookup = func(name string) types.Object {
+ for _, scope := range scopes {
+ if o := scope.Lookup(name); o != nil {
+ return o
+ }
+ }
+ return nil
+ }
+ )
+ if strings.HasSuffix(pkgName, "_test") {
+ // Treat 'package foo_test' as an alias for 'package foo'.
+ var (
+ basePkg = strings.TrimSuffix(pkgName, "_test")
+ pkg = f.pkg
+ )
+ for _, p := range pkg.typesPkg.Imports() {
+ if p.Name() == basePkg {
+ scopes = append(scopes, p.Scope())
+ break
+ }
+ }
+ }
+ fn, ok := node.(*ast.FuncDecl)
+ if !ok {
+ // Ignore non-functions.
+ return
+ }
+ var (
+ fnName = fn.Name.Name
+ report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
+ )
+ if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") {
+ // Ignore methods and types not named "Example".
+ return
+ }
+ if params := fn.Type.Params; len(params.List) != 0 {
+ report("%s should be niladic", fnName)
+ }
+ if results := fn.Type.Results; results != nil && len(results.List) != 0 {
+ report("%s should return nothing", fnName)
+ }
+ if fnName == "Example" {
+ // Nothing more to do.
+ return
+ }
+ if filesRun && !includesNonTest {
+ // The coherence checks between a test and the package it tests
+ // will report false positives if no non-test files have
+ // been provided.
+ return
+ }
+ var (
+ exName = strings.TrimPrefix(fnName, "Example")
+ elems = strings.SplitN(exName, "_", 3)
+ ident = elems[0]
+ obj = lookup(ident)
+ )
+ if ident != "" && obj == nil {
+ // Check ExampleFoo and ExampleBadFoo.
+ report("%s refers to unknown identifier: %s", fnName, ident)
+ // Abort since obj is absent and no subsequent checks can be performed.
+ return
+ }
+ if elemCnt := strings.Count(exName, "_"); elemCnt == 0 {
+ // Nothing more to do.
+ return
+ }
+ mmbr := elems[1]
+ if ident == "" {
+ // Check Example_suffix and Example_BadSuffix.
+ if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
+ report("%s has malformed example suffix: %s", fnName, residual)
+ }
+ return
+ }
+ if !isExampleSuffix(mmbr) {
+ // Check ExampleFoo_Method and ExampleFoo_BadMethod.
+ if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
+ report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
+ }
+ }
+ if len(elems) == 3 && !isExampleSuffix(elems[2]) {
+ // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
+ report("%s has malformed example suffix: %s", fnName, elems[2])
+ }
+ return
+}
diff --git a/go/src/golang.org/x/tools/cmd/vet/main.go b/go/src/golang.org/x/tools/cmd/vet/main.go
index e4b6877..c86e13c 100644
--- a/go/src/golang.org/x/tools/cmd/vet/main.go
+++ b/go/src/golang.org/x/tools/cmd/vet/main.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// NOTE: This version of vet is retired. Bug fixes only.
+// Vet now lives in the core repository.
+
// Vet is a simple checker for static errors in Go source code.
// See doc.go for more information.
package main
@@ -53,6 +56,14 @@
// setTrueCount record how many flags are explicitly set to true.
var setTrueCount int
+// dirsRun and filesRun indicate whether the vet is applied to directory or
+// file targets. The distinction affects which checks are run.
+var dirsRun, filesRun bool
+
+// includesNonTest indicates whether the vet is applied to non-test targets.
+// Certain checks are relevant only if they touch both test and non-test files.
+var includesNonTest bool
+
// A triState is a boolean that knows whether it has been set to either true or false.
// It is used to identify if a flag appears; the standard boolean flag cannot
// distinguish missing from unset. It also satisfies flag.Value.
@@ -209,8 +220,6 @@
if flag.NArg() == 0 {
Usage()
}
- dirs := false
- files := false
for _, name := range flag.Args() {
// Is it a directory?
fi, err := os.Stat(name)
@@ -219,15 +228,18 @@
continue
}
if fi.IsDir() {
- dirs = true
+ dirsRun = true
} else {
- files = true
+ filesRun = true
+ if !strings.HasSuffix(name, "_test.go") {
+ includesNonTest = true
+ }
}
}
- if dirs && files {
+ if dirsRun && filesRun {
Usage()
}
- if dirs {
+ if dirsRun {
for _, name := range flag.Args() {
walkDir(name)
}
diff --git a/go/src/golang.org/x/tools/cmd/vet/testdata/divergent/buf.go b/go/src/golang.org/x/tools/cmd/vet/testdata/divergent/buf.go
new file mode 100644
index 0000000..0efe0f8
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/vet/testdata/divergent/buf.go
@@ -0,0 +1,17 @@
+// Test of examples with divergent packages.
+
+// Package buf ...
+package buf
+
+// Buf is a ...
+type Buf []byte
+
+// Append ...
+func (*Buf) Append([]byte) {}
+
+func (Buf) Reset() {}
+
+func (Buf) Len() int { return 0 }
+
+// DefaultBuf is a ...
+var DefaultBuf Buf
diff --git a/go/src/golang.org/x/tools/cmd/vet/testdata/divergent/buf_test.go b/go/src/golang.org/x/tools/cmd/vet/testdata/divergent/buf_test.go
new file mode 100644
index 0000000..6b9cba3
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/vet/testdata/divergent/buf_test.go
@@ -0,0 +1,35 @@
+// Test of examples with divergent packages.
+
+package buf_test
+
+func Example() {} // OK because is package-level.
+
+func Example_suffix() // OK because refers to suffix annotation.
+
+func Example_BadSuffix() // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
+
+func ExampleBuf() // OK because refers to known top-level type.
+
+func ExampleBuf_Append() {} // OK because refers to known method.
+
+func ExampleBuf_Clear() {} // ERROR "ExampleBuf_Clear refers to unknown field or method: Buf.Clear"
+
+func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
+
+func ExampleBuf_Append_Bad() {} // ERROR "ExampleBuf_Append_Bad has malformed example suffix: Bad"
+
+func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix.
+
+func ExampleDefaultBuf() {} // OK because refers to top-level identifier.
+
+func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing"
+
+func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
+
+// "Puffer" is German for "Buffer".
+
+func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffer"
+
+func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
+
+func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
diff --git a/go/src/golang.org/x/tools/cmd/vet/testdata/examples_test.go b/go/src/golang.org/x/tools/cmd/vet/testdata/examples_test.go
new file mode 100644
index 0000000..9c53672
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/vet/testdata/examples_test.go
@@ -0,0 +1,48 @@
+// Test of examples.
+
+package testdata
+
+// Buf is a ...
+type Buf []byte
+
+// Append ...
+func (*Buf) Append([]byte) {}
+
+func (Buf) Reset() {}
+
+func (Buf) Len() int { return 0 }
+
+// DefaultBuf is a ...
+var DefaultBuf Buf
+
+func Example() {} // OK because is package-level.
+
+func Example_goodSuffix() // OK because refers to suffix annotation.
+
+func Example_BadSuffix() // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
+
+func ExampleBuf() // OK because refers to known top-level type.
+
+func ExampleBuf_Append() {} // OK because refers to known method.
+
+func ExampleBuf_Clear() {} // ERROR "ExampleBuf_Clear refers to unknown field or method: Buf.Clear"
+
+func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
+
+func ExampleBuf_Append_Bad() {} // ERROR "ExampleBuf_Append_Bad has malformed example suffix: Bad"
+
+func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix.
+
+func ExampleDefaultBuf() {} // OK because refers to top-level identifier.
+
+func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing"
+
+func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
+
+// "Puffer" is German for "Buffer".
+
+func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffer"
+
+func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
+
+func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
diff --git a/go/src/golang.org/x/tools/cmd/vet/testdata/incomplete/examples_test.go b/go/src/golang.org/x/tools/cmd/vet/testdata/incomplete/examples_test.go
new file mode 100644
index 0000000..445502b
--- /dev/null
+++ b/go/src/golang.org/x/tools/cmd/vet/testdata/incomplete/examples_test.go
@@ -0,0 +1,33 @@
+// Test of examples.
+
+package testdata
+
+func Example() {} // OK because is package-level.
+
+func Example_suffix() // OK because refers to suffix annotation.
+
+func Example_BadSuffix() // OK because non-test package was excluded. No false positives wanted.
+
+func ExampleBuf() // OK because non-test package was excluded. No false positives wanted.
+
+func ExampleBuf_Append() {} // OK because non-test package was excluded. No false positives wanted.
+
+func ExampleBuf_Clear() {} // OK because non-test package was excluded. No false positives wanted.
+
+func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
+
+func ExampleBuf_Append_Bad() {} // OK because non-test package was excluded. No false positives wanted.
+
+func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix.
+
+func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing"
+
+func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
+
+// "Puffer" is German for "Buffer".
+
+func ExamplePuffer() // OK because non-test package was excluded. No false positives wanted.
+
+func ExamplePuffer_Append() // OK because non-test package was excluded. No false positives wanted.
+
+func ExamplePuffer_suffix() // OK because non-test package was excluded. No false positives wanted.
diff --git a/go/src/golang.org/x/tools/cmd/vet/vet_test.go b/go/src/golang.org/x/tools/cmd/vet/vet_test.go
index 6a09e3d..7508193 100644
--- a/go/src/golang.org/x/tools/cmd/vet/vet_test.go
+++ b/go/src/golang.org/x/tools/cmd/vet/vet_test.go
@@ -22,23 +22,46 @@
binary = "testvet.exe"
)
+func CanRun(t *testing.T) bool {
+ // Plan 9 and Windows systems can't be guaranteed to have Perl and so can't run errchk.
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Skip("skipping test; no Perl on %q", runtime.GOOS)
+ return false
+ }
+ return true
+}
+
+func Build(t *testing.T) {
+ // go build
+ cmd := exec.Command("go", "build", "-o", binary)
+ run(cmd, t)
+}
+
+func Vet(t *testing.T, files []string) {
+ errchk := filepath.Join(runtime.GOROOT(), "test", "errchk")
+ flags := []string{
+ "./" + binary,
+ "-printfuncs=Warn:1,Warnf:1",
+ "-test", // TODO: Delete once -shadow is part of -all.
+ }
+ cmd := exec.Command(errchk, append(flags, files...)...)
+ if !run(cmd, t) {
+ t.Fatal("vet command failed")
+ }
+}
+
// Run this shell script, but do it in Go so it can be run by "go test".
// go build -o testvet
// $(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s
// rm testvet
//
+
func TestVet(t *testing.T) {
- // Plan 9 and Windows systems can't be guaranteed to have Perl and so can't run errchk.
- switch runtime.GOOS {
- case "plan9", "windows":
- t.Skip("skipping test; no Perl on %q", runtime.GOOS)
+ if !CanRun(t) {
+ t.Skip("cannot run on this environment")
}
-
- // go build
- cmd := exec.Command("go", "build", "-o", binary)
- run(cmd, t)
-
- // defer removal of vet
+ Build(t)
defer os.Remove(binary)
// errchk ./testvet
@@ -51,16 +74,29 @@
t.Fatal(err)
}
files := append(gos, asms...)
- errchk := filepath.Join(runtime.GOROOT(), "test", "errchk")
- flags := []string{
- "./" + binary,
- "-printfuncs=Warn:1,Warnf:1",
- "-test", // TODO: Delete once -shadow is part of -all.
+ Vet(t, files)
+}
+
+func TestDivergentPackagesExamples(t *testing.T) {
+ if !CanRun(t) {
+ t.Skip("cannot run on this environment")
}
- cmd = exec.Command(errchk, append(flags, files...)...)
- if !run(cmd, t) {
- t.Fatal("vet command failed")
+ Build(t)
+ defer os.Remove(binary)
+
+ // errchk ./testvet
+ Vet(t, []string{"testdata/divergent/buf.go", "testdata/divergent/buf_test.go"})
+}
+
+func TestIncompleteExamples(t *testing.T) {
+ if !CanRun(t) {
+ t.Skip("cannot run on this environment")
}
+ Build(t)
+ defer os.Remove(binary)
+
+ // errchk ./testvet
+ Vet(t, []string{"testdata/incomplete/examples_test.go"})
}
func run(c *exec.Cmd, t *testing.T) bool {
@@ -83,7 +119,6 @@
cmd := exec.Command("go", "build", "-o", binary)
run(cmd, t)
- // defer removal of vet
defer os.Remove(binary)
args := []string{
diff --git a/go/src/golang.org/x/tools/cmd/vet/whitelist/whitelist.go b/go/src/golang.org/x/tools/cmd/vet/whitelist/whitelist.go
index 76a3198..d6f0dce 100644
--- a/go/src/golang.org/x/tools/cmd/vet/whitelist/whitelist.go
+++ b/go/src/golang.org/x/tools/cmd/vet/whitelist/whitelist.go
@@ -50,11 +50,4 @@
"image.Point": true,
"image.Rectangle": true,
"image.Uniform": true,
-
- // Vanadium named slice types.
- //
- // NOTE: These types were added to avoid spurious go vet warnings.
- // Make sure to add them back if you update the source.
- "v.io/v23/uniqueid.Id": true,
- "v.io/v23/rpc.ListenAddrs": true,
}
diff --git a/go/src/golang.org/x/tools/container/intsets/popcnt_amd64.go b/go/src/golang.org/x/tools/container/intsets/popcnt_amd64.go
new file mode 100644
index 0000000..99ea813
--- /dev/null
+++ b/go/src/golang.org/x/tools/container/intsets/popcnt_amd64.go
@@ -0,0 +1,20 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!appengine,!gccgo
+
+package intsets
+
+func popcnt(x word) int
+func havePOPCNT() bool
+
+var hasPOPCNT = havePOPCNT()
+
+// popcount returns the population count (number of set bits) of x.
+func popcount(x word) int {
+ if hasPOPCNT {
+ return popcnt(x)
+ }
+ return popcountTable(x) // faster than Hacker's Delight
+}
diff --git a/go/src/golang.org/x/tools/container/intsets/popcnt_amd64.s b/go/src/golang.org/x/tools/container/intsets/popcnt_amd64.s
new file mode 100644
index 0000000..05c3d6f
--- /dev/null
+++ b/go/src/golang.org/x/tools/container/intsets/popcnt_amd64.s
@@ -0,0 +1,30 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!appengine,!gccgo
+
+#include "textflag.h"
+
+// func havePOPCNT() bool
+TEXT ·havePOPCNT(SB),4,$0
+ MOVQ $1, AX
+ CPUID
+ SHRQ $23, CX
+ ANDQ $1, CX
+ MOVB CX, ret+0(FP)
+ RET
+
+// func popcnt(word) int
+TEXT ·popcnt(SB),NOSPLIT,$0-8
+ XORQ AX, AX
+ MOVQ x+0(FP), SI
+ // POPCNT (SI), AX is not recognized by Go assembler,
+ // so we assemble it ourselves.
+ BYTE $0xf3
+ BYTE $0x48
+ BYTE $0x0f
+ BYTE $0xb8
+ BYTE $0xc6
+ MOVQ AX, ret+8(FP)
+ RET
diff --git a/go/src/golang.org/x/tools/container/intsets/popcnt_gccgo.go b/go/src/golang.org/x/tools/container/intsets/popcnt_gccgo.go
new file mode 100644
index 0000000..82a8875
--- /dev/null
+++ b/go/src/golang.org/x/tools/container/intsets/popcnt_gccgo.go
@@ -0,0 +1,9 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build gccgo
+
+package intsets
+
+func popcount(x word) int
diff --git a/go/src/golang.org/x/tools/container/intsets/popcnt_gccgo_c.c b/go/src/golang.org/x/tools/container/intsets/popcnt_gccgo_c.c
new file mode 100644
index 0000000..08abb32
--- /dev/null
+++ b/go/src/golang.org/x/tools/container/intsets/popcnt_gccgo_c.c
@@ -0,0 +1,19 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build gccgo
+
+#include <errno.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define _STRINGIFY2_(x) #x
+#define _STRINGIFY_(x) _STRINGIFY2_(x)
+#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
+
+extern intptr_t popcount(uintptr_t x) __asm__(GOSYM_PREFIX GOPKGPATH ".popcount");
+
+intptr_t popcount(uintptr_t x) {
+ return __builtin_popcountl((unsigned long)(x));
+}
diff --git a/go/src/golang.org/x/tools/container/intsets/popcnt_generic.go b/go/src/golang.org/x/tools/container/intsets/popcnt_generic.go
new file mode 100644
index 0000000..3985a1d
--- /dev/null
+++ b/go/src/golang.org/x/tools/container/intsets/popcnt_generic.go
@@ -0,0 +1,33 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64 appengine
+// +build !gccgo
+
+package intsets
+
+import "runtime"
+
+// We compared three algorithms---Hacker's Delight, table lookup,
+// and AMD64's SSE4.1 hardware POPCNT---on a 2.67GHz Xeon X5550.
+//
+// % GOARCH=amd64 go test -run=NONE -bench=Popcount
+// POPCNT 5.12 ns/op
+// Table 8.53 ns/op
+// HackersDelight 9.96 ns/op
+//
+// % GOARCH=386 go test -run=NONE -bench=Popcount
+// Table 10.4 ns/op
+// HackersDelight 5.23 ns/op
+//
+// (AMD64's ABM1 hardware supports ntz and nlz too,
+// but they aren't critical.)
+
+// popcount returns the population count (number of set bits) of x.
+func popcount(x word) int {
+ if runtime.GOARCH == "386" {
+ return popcountHD(uint32(x))
+ }
+ return popcountTable(x)
+}
diff --git a/go/src/golang.org/x/tools/container/intsets/util.go b/go/src/golang.org/x/tools/container/intsets/util.go
index 76e682c..dd1db86 100644
--- a/go/src/golang.org/x/tools/container/intsets/util.go
+++ b/go/src/golang.org/x/tools/container/intsets/util.go
@@ -4,6 +4,16 @@
package intsets
+// From Hacker's Delight, fig 5.2.
+func popcountHD(x uint32) int {
+ x -= (x >> 1) & 0x55555555
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333)
+ x = (x + (x >> 4)) & 0x0f0f0f0f
+ x = x + (x >> 8)
+ x = x + (x >> 16)
+ return int(x & 0x0000003f)
+}
+
var a [1 << 8]byte
func init() {
@@ -18,8 +28,7 @@
}
}
-// popcount returns the population count (number of set bits) of x.
-func popcount(x word) int {
+func popcountTable(x word) int {
return int(a[byte(x>>(0*8))] +
a[byte(x>>(1*8))] +
a[byte(x>>(2*8))] +
diff --git a/go/src/golang.org/x/tools/container/intsets/util_test.go b/go/src/golang.org/x/tools/container/intsets/util_test.go
index 92a4bc5..e4cc659 100644
--- a/go/src/golang.org/x/tools/container/intsets/util_test.go
+++ b/go/src/golang.org/x/tools/container/intsets/util_test.go
@@ -4,7 +4,10 @@
package intsets
-import "testing"
+import (
+ "math/rand"
+ "testing"
+)
func TestNLZ(t *testing.T) {
// Test the platform-specific edge case.
@@ -23,3 +26,33 @@
// Backdoor for testing.
func (s *Sparse) Check() error { return s.check() }
+
+func dumbPopcount(x word) int {
+ var popcnt int
+ for i := uint(0); i < bitsPerWord; i++ {
+ if x&(1<<i) != 0 {
+ popcnt++
+ }
+ }
+ return popcnt
+}
+
+func TestPopcount(t *testing.T) {
+ for i := 0; i < 1e5; i++ {
+ x := word(rand.Uint32())
+ if bitsPerWord == 64 {
+ x = x | (word(rand.Uint32()) << 32)
+ }
+ want := dumbPopcount(x)
+ got := popcount(x)
+ if got != want {
+ t.Errorf("popcount(%d) = %d, want %d", x, got, want)
+ }
+ }
+}
+
+func BenchmarkPopcount(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ popcount(word(i))
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/buildutil/allpackages.go b/go/src/golang.org/x/tools/go/buildutil/allpackages.go
index 0f909ee..bee5fc1 100644
--- a/go/src/golang.org/x/tools/go/buildutil/allpackages.go
+++ b/go/src/golang.org/x/tools/go/buildutil/allpackages.go
@@ -18,9 +18,10 @@
"sync"
)
-// AllPackages returns the import path of each Go package in any source
+// AllPackages returns the package path of each Go package in any source
// directory of the specified build context (e.g. $GOROOT or an element
// of $GOPATH). Errors are ignored. The results are sorted.
+// All package paths are canonical, and thus may contain "/vendor/".
//
// The result may include import paths for directories that contain no
// *.go files, such as "archive" (in $GOROOT/src).
@@ -37,9 +38,10 @@
return list
}
-// ForEachPackage calls the found function with the import path of
+// ForEachPackage calls the found function with the package path of
// each Go package it finds in any source directory of the specified
// build context (e.g. $GOROOT or an element of $GOPATH).
+// All package paths are canonical, and thus may contain "/vendor/".
//
// If the package directory exists but could not be read, the second
// argument to the found function provides the error.
@@ -48,10 +50,6 @@
// which must be concurrency-safe.
//
func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
- // We use a counting semaphore to limit
- // the number of parallel calls to ReadDir.
- sema := make(chan bool, 20)
-
ch := make(chan item)
var wg sync.WaitGroup
@@ -59,7 +57,7 @@
root := root
wg.Add(1)
go func() {
- allPackages(ctxt, sema, root, ch)
+ allPackages(ctxt, root, ch)
wg.Done()
}()
}
@@ -79,7 +77,11 @@
err error // (optional)
}
-func allPackages(ctxt *build.Context, sema chan bool, root string, ch chan<- item) {
+// We use a process-wide counting semaphore to limit
+// the number of parallel calls to ReadDir.
+var ioLimit = make(chan bool, 20)
+
+func allPackages(ctxt *build.Context, root string, ch chan<- item) {
root = filepath.Clean(root) + string(os.PathSeparator)
var wg sync.WaitGroup
@@ -100,9 +102,9 @@
return
}
- sema <- true
+ ioLimit <- true
files, err := ReadDir(ctxt, dir)
- <-sema
+ <-ioLimit
if pkg != "" || err != nil {
ch <- item{pkg, err}
}
diff --git a/go/src/golang.org/x/tools/go/buildutil/tags.go b/go/src/golang.org/x/tools/go/buildutil/tags.go
index 9735094..525ecb6 100644
--- a/go/src/golang.org/x/tools/go/buildutil/tags.go
+++ b/go/src/golang.org/x/tools/go/buildutil/tags.go
@@ -16,7 +16,7 @@
// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag.
//
// Example:
-// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsDoc)
+// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
type TagsFlag []string
func (v *TagsFlag) Set(s string) error {
diff --git a/go/src/golang.org/x/tools/go/buildutil/util.go b/go/src/golang.org/x/tools/go/buildutil/util.go
index 60eeae2..0e093fc 100644
--- a/go/src/golang.org/x/tools/go/buildutil/util.go
+++ b/go/src/golang.org/x/tools/go/buildutil/util.go
@@ -15,6 +15,7 @@
"os"
"path"
"path/filepath"
+ "runtime"
"strings"
)
@@ -72,7 +73,7 @@
// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
for _, srcdir := range ctxt.SrcDirs() {
srcdirSlash := filepath.ToSlash(srcdir) + "/"
- if strings.HasPrefix(dirSlash, srcdirSlash) {
+ if dirHasPrefix(dirSlash, srcdirSlash) {
importPath := dirSlash[len(srcdirSlash) : len(dirSlash)-len("/")]
return ctxt.Import(importPath, dir, build.FindOnly)
}
@@ -81,6 +82,14 @@
return nil, fmt.Errorf("can't find package containing %s", filename)
}
+// dirHasPrefix tests whether the directory dir begins with prefix.
+func dirHasPrefix(dir, prefix string) bool {
+ if runtime.GOOS != "windows" {
+ return strings.HasPrefix(dir, prefix)
+ }
+ return len(dir) >= len(prefix) && strings.EqualFold(dir[:len(prefix)], prefix)
+}
+
// -- Effective methods of file system interface -------------------------
// (go/build.Context defines these as methods, but does not export them.)
diff --git a/go/src/golang.org/x/tools/go/buildutil/util_test.go b/go/src/golang.org/x/tools/go/buildutil/util_test.go
index 10dae17..dd55533 100644
--- a/go/src/golang.org/x/tools/go/buildutil/util_test.go
+++ b/go/src/golang.org/x/tools/go/buildutil/util_test.go
@@ -23,13 +23,14 @@
goroot := runtime.GOROOT()
gopath := filepath.SplitList(os.Getenv("GOPATH"))[0]
- for _, test := range [][2]string{
+ tests := [][2]string{
{goroot + "/src/fmt/print.go", "fmt"},
{goroot + "/src/encoding/json/foo.go", "encoding/json"},
{goroot + "/src/encoding/missing/foo.go", "(not found)"},
{gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go",
"golang.org/x/tools/go/buildutil"},
- } {
+ }
+ for _, test := range tests {
file, want := test[0], test[1]
bp, err := buildutil.ContainingPackage(&build.Default, ".", file)
got := bp.ImportPath
diff --git a/go/src/golang.org/x/tools/go/buildutil/util_windows_test.go b/go/src/golang.org/x/tools/go/buildutil/util_windows_test.go
new file mode 100644
index 0000000..86fe9c7
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/buildutil/util_windows_test.go
@@ -0,0 +1,48 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buildutil_test
+
+import (
+ "fmt"
+ "go/build"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/buildutil"
+)
+
+func testContainingPackageCaseFold(file, want string) error {
+ bp, err := buildutil.ContainingPackage(&build.Default, ".", file)
+ if err != nil {
+ return err
+ }
+ if got := bp.ImportPath; got != want {
+ return fmt.Errorf("ContainingPackage(%q) = %s, want %s", file, got, want)
+ }
+ return nil
+}
+
+func TestContainingPackageCaseFold(t *testing.T) {
+ path := filepath.Join(runtime.GOROOT(), `src\fmt\print.go`)
+ err := testContainingPackageCaseFold(path, "fmt")
+ if err != nil {
+ t.Error(err)
+ }
+ vol := filepath.VolumeName(path)
+ if len(vol) != 2 || vol[1] != ':' {
+ t.Fatalf("GOROOT path has unexpected volume name: %v", vol)
+ }
+ rest := path[len(vol):]
+ err = testContainingPackageCaseFold(strings.ToUpper(vol)+rest, "fmt")
+ if err != nil {
+ t.Error(err)
+ }
+ err = testContainingPackageCaseFold(strings.ToLower(vol)+rest, "fmt")
+ if err != nil {
+ t.Error(err)
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/callgraph/cha/cha.go b/go/src/golang.org/x/tools/go/callgraph/cha/cha.go
index fcdf686..e016649 100644
--- a/go/src/golang.org/x/tools/go/callgraph/cha/cha.go
+++ b/go/src/golang.org/x/tools/go/callgraph/cha/cha.go
@@ -1,3 +1,9 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
// Package cha computes the call graph of a Go program using the Class
// Hierarchy Analysis (CHA) algorithm.
//
@@ -20,10 +26,11 @@
package cha // import "golang.org/x/tools/go/callgraph/cha"
import (
+ "go/types"
+
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/go/callgraph/cha/cha14.go b/go/src/golang.org/x/tools/go/callgraph/cha/cha14.go
new file mode 100644
index 0000000..5a20c8b
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/callgraph/cha/cha14.go
@@ -0,0 +1,126 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package cha computes the call graph of a Go program using the Class
+// Hierarchy Analysis (CHA) algorithm.
+//
+// CHA was first described in "Optimization of Object-Oriented Programs
+// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove,
+// and Craig Chambers, ECOOP'95.
+//
+// CHA is related to RTA (see go/callgraph/rta); the difference is that
+// CHA conservatively computes the entire "implements" relation between
+// interfaces and concrete types ahead of time, whereas RTA uses dynamic
+// programming to construct it on the fly as it encounters new functions
+// reachable from main. CHA may thus include spurious call edges for
+// types that haven't been instantiated yet, or types that are never
+// instantiated.
+//
+// Since CHA conservatively assumes that all functions are address-taken
+// and all concrete types are put into interfaces, it is sound to run on
+// partial programs, such as libraries without a main or test function.
+//
+package cha // import "golang.org/x/tools/go/callgraph/cha"
+
+import (
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// CallGraph computes the call graph of the specified program using the
+// Class Hierarchy Analysis algorithm.
+//
+func CallGraph(prog *ssa.Program) *callgraph.Graph {
+ cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph
+
+ allFuncs := ssautil.AllFunctions(prog)
+
+ // funcsBySig contains all functions, keyed by signature. It is
+ // the effective set of address-taken functions used to resolve
+ // a dynamic call of a particular signature.
+ var funcsBySig typeutil.Map // value is []*ssa.Function
+
+ // methodsByName contains all methods,
+ // grouped by name for efficient lookup.
+ methodsByName := make(map[string][]*ssa.Function)
+
+ // methodsMemo records, for every abstract method call call I.f on
+ // interface type I, the set of concrete methods C.f of all
+ // types C that satisfy interface I.
+ methodsMemo := make(map[*types.Func][]*ssa.Function)
+ lookupMethods := func(m *types.Func) []*ssa.Function {
+ methods, ok := methodsMemo[m]
+ if !ok {
+ I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface)
+ for _, f := range methodsByName[m.Name()] {
+ C := f.Signature.Recv().Type() // named or *named
+ if types.Implements(C, I) {
+ methods = append(methods, f)
+ }
+ }
+ methodsMemo[m] = methods
+ }
+ return methods
+ }
+
+ for f := range allFuncs {
+ if f.Signature.Recv() == nil {
+ // Package initializers can never be address-taken.
+ if f.Name() == "init" && f.Synthetic == "package initializer" {
+ continue
+ }
+ funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function)
+ funcs = append(funcs, f)
+ funcsBySig.Set(f.Signature, funcs)
+ } else {
+ methodsByName[f.Name()] = append(methodsByName[f.Name()], f)
+ }
+ }
+
+ addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) {
+ gnode := cg.CreateNode(g)
+ callgraph.AddEdge(fnode, site, gnode)
+ }
+
+ addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) {
+ // Because every call to a highly polymorphic and
+ // frequently used abstract method such as
+ // (io.Writer).Write is assumed to call every concrete
+ // Write method in the program, the call graph can
+ // contain a lot of duplication.
+ //
+ // TODO(adonovan): opt: consider factoring the callgraph
+ // API so that the Callers component of each edge is a
+ // slice of nodes, not a singleton.
+ for _, g := range callees {
+ addEdge(fnode, site, g)
+ }
+ }
+
+ for f := range allFuncs {
+ fnode := cg.CreateNode(f)
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ if site, ok := instr.(ssa.CallInstruction); ok {
+ call := site.Common()
+ if call.IsInvoke() {
+ addEdges(fnode, site, lookupMethods(call.Method))
+ } else if g := call.StaticCallee(); g != nil {
+ addEdge(fnode, site, g)
+ } else if _, ok := call.Value.(*ssa.Builtin); !ok {
+ callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function)
+ addEdges(fnode, site, callees)
+ }
+ }
+ }
+ }
+ }
+
+ return cg
+}
diff --git a/go/src/golang.org/x/tools/go/callgraph/cha/cha14_test.go b/go/src/golang.org/x/tools/go/callgraph/cha/cha14_test.go
new file mode 100644
index 0000000..7f0aeeb
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/callgraph/cha/cha14_test.go
@@ -0,0 +1,112 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// No testdata on Android.
+
+// +build !android
+
+package cha_test
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "sort"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/callgraph/cha"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+var inputs = []string{
+ "testdata/func.go",
+ "testdata/iface.go",
+ "testdata/recv.go",
+}
+
+func expectation(f *ast.File) (string, token.Pos) {
+ for _, c := range f.Comments {
+ text := strings.TrimSpace(c.Text())
+ if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
+ return t, c.Pos()
+ }
+ }
+ return "", token.NoPos
+}
+
+// TestCHA runs CHA on each file in inputs, prints the dynamic edges of
+// the call graph, and compares it with the golden results embedded in
+// the WANT comment at the end of the file.
+//
+func TestCHA(t *testing.T) {
+ for _, filename := range inputs {
+ content, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Errorf("couldn't read file '%s': %s", filename, err)
+ continue
+ }
+
+ conf := loader.Config{
+ ParserMode: parser.ParseComments,
+ }
+ f, err := conf.ParseFile(filename, content)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ want, pos := expectation(f)
+ if pos == token.NoPos {
+ t.Errorf("No WANT: comment in %s", filename)
+ continue
+ }
+
+ conf.CreateFromFiles("main", f)
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ prog := ssautil.CreateProgram(iprog, 0)
+ mainPkg := prog.Package(iprog.Created[0].Pkg)
+ prog.Build()
+
+ cg := cha.CallGraph(prog)
+
+ if got := printGraph(cg, mainPkg.Pkg); got != want {
+ t.Errorf("%s: got:\n%s\nwant:\n%s",
+ prog.Fset.Position(pos), got, want)
+ }
+ }
+}
+
+func printGraph(cg *callgraph.Graph, from *types.Package) string {
+ var edges []string
+ callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
+ if strings.Contains(e.Description(), "dynamic") {
+ edges = append(edges, fmt.Sprintf("%s --> %s",
+ e.Caller.Func.RelString(from),
+ e.Callee.Func.RelString(from)))
+ }
+ return nil
+ })
+ sort.Strings(edges)
+
+ var buf bytes.Buffer
+ buf.WriteString("Dynamic calls\n")
+ for _, edge := range edges {
+ fmt.Fprintf(&buf, " %s\n", edge)
+ }
+ return strings.TrimSpace(buf.String())
+}
diff --git a/go/src/golang.org/x/tools/go/callgraph/cha/cha_test.go b/go/src/golang.org/x/tools/go/callgraph/cha/cha_test.go
index 38348c0..332758c 100644
--- a/go/src/golang.org/x/tools/go/callgraph/cha/cha_test.go
+++ b/go/src/golang.org/x/tools/go/callgraph/cha/cha_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// No testdata on Android.
// +build !android
@@ -14,6 +16,7 @@
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"io/ioutil"
"sort"
"strings"
@@ -23,7 +26,6 @@
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
var inputs = []string{
@@ -78,11 +80,11 @@
prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
- prog.BuildAll()
+ prog.Build()
cg := cha.CallGraph(prog)
- if got := printGraph(cg, mainPkg.Object); got != want {
+ if got := printGraph(cg, mainPkg.Pkg); got != want {
t.Errorf("%s: got:\n%s\nwant:\n%s",
prog.Fset.Position(pos), got, want)
}
diff --git a/go/src/golang.org/x/tools/go/callgraph/rta/rta.go b/go/src/golang.org/x/tools/go/callgraph/rta/rta.go
index 8d22da6..7c9379d 100644
--- a/go/src/golang.org/x/tools/go/callgraph/rta/rta.go
+++ b/go/src/golang.org/x/tools/go/callgraph/rta/rta.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This package provides Rapid Type Analysis (RTA) for Go, a fast
// algorithm for call graph construction and discovery of reachable code
// (and hence dead code) and runtime types. The algorithm was first
@@ -48,10 +50,10 @@
import (
"fmt"
+ "go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -197,7 +199,7 @@
func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) {
// Ascertain the concrete method of C to be called.
imethod := site.Common().Method
- cmethod := r.prog.Method(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name()))
+ cmethod := r.prog.MethodValue(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name()))
r.addEdge(site, cmethod, true)
}
@@ -361,7 +363,7 @@
if m.Exported() {
// Exported methods are always potentially callable via reflection.
- r.addReachable(r.prog.Method(sel), true)
+ r.addReachable(r.prog.MethodValue(sel), true)
}
}
diff --git a/go/src/golang.org/x/tools/go/callgraph/rta/rta14.go b/go/src/golang.org/x/tools/go/callgraph/rta/rta14.go
new file mode 100644
index 0000000..33956ad
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/callgraph/rta/rta14.go
@@ -0,0 +1,461 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This package provides Rapid Type Analysis (RTA) for Go, a fast
+// algorithm for call graph construction and discovery of reachable code
+// (and hence dead code) and runtime types. The algorithm was first
+// described in:
+//
+// David F. Bacon and Peter F. Sweeney. 1996.
+// Fast static analysis of C++ virtual function calls. (OOPSLA '96)
+// http://doi.acm.org/10.1145/236337.236371
+//
+// The algorithm uses dynamic programming to tabulate the cross-product
+// of the set of known "address taken" functions with the set of known
+// dynamic calls of the same type. As each new address-taken function
+// is discovered, call graph edges are added from each known callsite,
+// and as each new call site is discovered, call graph edges are added
+// from it to each known address-taken function.
+//
+// A similar approach is used for dynamic calls via interfaces: it
+// tabulates the cross-product of the set of known "runtime types",
+// i.e. types that may appear in an interface value, or be derived from
+// one via reflection, with the set of known "invoke"-mode dynamic
+// calls. As each new "runtime type" is discovered, call edges are
+// added from the known call sites, and as each new call site is
+// discovered, call graph edges are added to each compatible
+// method.
+//
+// In addition, we must consider all exported methods of any runtime type
+// as reachable, since they may be called via reflection.
+//
+// Each time a newly added call edge causes a new function to become
+// reachable, the code of that function is analyzed for more call sites,
+// address-taken functions, and runtime types. The process continues
+// until a fixed point is achieved.
+//
+// The resulting call graph is less precise than one produced by pointer
+// analysis, but the algorithm is much faster. For example, running the
+// cmd/callgraph tool on its own source takes ~2.1s for RTA and ~5.4s
+// for points-to analysis.
+//
+package rta // import "golang.org/x/tools/go/callgraph/rta"
+
+// TODO(adonovan): test it by connecting it to the interpreter and
+// replacing all "unreachable" functions by a special intrinsic, and
+// ensure that that intrinsic is never called.
+
+import (
+ "fmt"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// A Result holds the results of Rapid Type Analysis, which includes the
+// set of reachable functions/methods, runtime types, and the call graph.
+//
+type Result struct {
+ // CallGraph is the discovered callgraph.
+ // It does not include edges for calls made via reflection.
+ CallGraph *callgraph.Graph
+
+ // Reachable contains the set of reachable functions and methods.
+ // This includes exported methods of runtime types, since
+ // they may be accessed via reflection.
+ // The value indicates whether the function is address-taken.
+ //
+ // (We wrap the bool in a struct to avoid inadvertent use of
+ // "if Reachable[f] {" to test for set membership.)
+ Reachable map[*ssa.Function]struct{ AddrTaken bool }
+
+ // RuntimeTypes contains the set of types that are needed at
+ // runtime, for interfaces or reflection.
+ //
+ // The value indicates whether the type is inaccessible to reflection.
+ // Consider:
+ // type A struct{B}
+ // fmt.Println(new(A))
+ // Types *A, A and B are accessible to reflection, but the unnamed
+ // type struct{B} is not.
+ RuntimeTypes typeutil.Map
+}
+
+// Working state of the RTA algorithm.
+type rta struct {
+ result *Result
+
+ prog *ssa.Program
+
+ worklist []*ssa.Function // list of functions to visit
+
+ // addrTakenFuncsBySig contains all address-taken *Functions, grouped by signature.
+ // Keys are *types.Signature, values are map[*ssa.Function]bool sets.
+ addrTakenFuncsBySig typeutil.Map
+
+ // dynCallSites contains all dynamic "call"-mode call sites, grouped by signature.
+ // Keys are *types.Signature, values are unordered []ssa.CallInstruction.
+ dynCallSites typeutil.Map
+
+ // invokeSites contains all "invoke"-mode call sites, grouped by interface.
+ // Keys are *types.Interface (never *types.Named),
+ // Values are unordered []ssa.CallInstruction sets.
+ invokeSites typeutil.Map
+
+ // The following two maps together define the subset of the
+ // m:n "implements" relation needed by the algorithm.
+
+ // concreteTypes maps each concrete type to the set of interfaces that it implements.
+ // Keys are types.Type, values are unordered []*types.Interface.
+ // Only concrete types used as MakeInterface operands are included.
+ concreteTypes typeutil.Map
+
+ // interfaceTypes maps each interface type to
+ // the set of concrete types that implement it.
+ // Keys are *types.Interface, values are unordered []types.Type.
+ // Only interfaces used in "invoke"-mode CallInstructions are included.
+ interfaceTypes typeutil.Map
+}
+
+// addReachable marks a function as potentially callable at run-time,
+// and ensures that it gets processed.
+func (r *rta) addReachable(f *ssa.Function, addrTaken bool) {
+ reachable := r.result.Reachable
+ n := len(reachable)
+ v := reachable[f]
+ if addrTaken {
+ v.AddrTaken = true
+ }
+ reachable[f] = v
+ if len(reachable) > n {
+ // First time seeing f. Add it to the worklist.
+ r.worklist = append(r.worklist, f)
+ }
+}
+
+// addEdge adds the specified call graph edge, and marks it reachable.
+// addrTaken indicates whether to mark the callee as "address-taken".
+func (r *rta) addEdge(site ssa.CallInstruction, callee *ssa.Function, addrTaken bool) {
+ r.addReachable(callee, addrTaken)
+
+ if g := r.result.CallGraph; g != nil {
+ if site.Parent() == nil {
+ panic(site)
+ }
+ from := g.CreateNode(site.Parent())
+ to := g.CreateNode(callee)
+ callgraph.AddEdge(from, site, to)
+ }
+}
+
+// ---------- addrTakenFuncs × dynCallSites ----------
+
+// visitAddrTakenFunc is called each time we encounter an address-taken function f.
+func (r *rta) visitAddrTakenFunc(f *ssa.Function) {
+ // Create two-level map (Signature -> Function -> bool).
+ S := f.Signature
+ funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool)
+ if funcs == nil {
+ funcs = make(map[*ssa.Function]bool)
+ r.addrTakenFuncsBySig.Set(S, funcs)
+ }
+ if !funcs[f] {
+ // First time seeing f.
+ funcs[f] = true
+
+ // If we've seen any dyncalls of this type, mark it reachable,
+ // and add call graph edges.
+ sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction)
+ for _, site := range sites {
+ r.addEdge(site, f, true)
+ }
+ }
+}
+
+// visitDynCall is called each time we encounter a dynamic "call"-mode call.
+func (r *rta) visitDynCall(site ssa.CallInstruction) {
+ S := site.Common().Signature()
+
+ // Record the call site.
+ sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction)
+ r.dynCallSites.Set(S, append(sites, site))
+
+ // For each function of signature S that we know is address-taken,
+ // mark it reachable. We'll add the callgraph edges later.
+ funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool)
+ for g := range funcs {
+ r.addEdge(site, g, true)
+ }
+}
+
+// ---------- concrete types × invoke sites ----------
+
+// addInvokeEdge is called for each new pair (site, C) in the matrix.
+func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) {
+ // Ascertain the concrete method of C to be called.
+ imethod := site.Common().Method
+ cmethod := r.prog.MethodValue(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name()))
+ r.addEdge(site, cmethod, true)
+}
+
+// visitInvoke is called each time the algorithm encounters an "invoke"-mode call.
+func (r *rta) visitInvoke(site ssa.CallInstruction) {
+ I := site.Common().Value.Type().Underlying().(*types.Interface)
+
+ // Record the invoke site.
+ sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction)
+ r.invokeSites.Set(I, append(sites, site))
+
+ // Add callgraph edge for each existing
+ // address-taken concrete type implementing I.
+ for _, C := range r.implementations(I) {
+ r.addInvokeEdge(site, C)
+ }
+}
+
+// ---------- main algorithm ----------
+
+// visitFunc processes function f.
+func (r *rta) visitFunc(f *ssa.Function) {
+ var space [32]*ssa.Value // preallocate space for common case
+
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ rands := instr.Operands(space[:0])
+
+ switch instr := instr.(type) {
+ case ssa.CallInstruction:
+ call := instr.Common()
+ if call.IsInvoke() {
+ r.visitInvoke(instr)
+ } else if g := call.StaticCallee(); g != nil {
+ r.addEdge(instr, g, false)
+ } else if _, ok := call.Value.(*ssa.Builtin); !ok {
+ r.visitDynCall(instr)
+ }
+
+ // Ignore the call-position operand when
+ // looking for address-taken Functions.
+ // Hack: assume this is rands[0].
+ rands = rands[1:]
+
+ case *ssa.MakeInterface:
+ r.addRuntimeType(instr.X.Type(), false)
+ }
+
+ // Process all address-taken functions.
+ for _, op := range rands {
+ if g, ok := (*op).(*ssa.Function); ok {
+ r.visitAddrTakenFunc(g)
+ }
+ }
+ }
+ }
+}
+
+// Analyze performs Rapid Type Analysis, starting at the specified root
+// functions. It returns nil if no roots were specified.
+//
+// If buildCallGraph is true, Result.CallGraph will contain a call
+// graph; otherwise, only the other fields (reachable functions) are
+// populated.
+//
+func Analyze(roots []*ssa.Function, buildCallGraph bool) *Result {
+ if len(roots) == 0 {
+ return nil
+ }
+
+ r := &rta{
+ result: &Result{Reachable: make(map[*ssa.Function]struct{ AddrTaken bool })},
+ prog: roots[0].Prog,
+ }
+
+ if buildCallGraph {
+ // TODO(adonovan): change callgraph API to eliminate the
+ // notion of a distinguished root node. Some callgraphs
+ // have many roots, or none.
+ r.result.CallGraph = callgraph.New(roots[0])
+ }
+
+ hasher := typeutil.MakeHasher()
+ r.result.RuntimeTypes.SetHasher(hasher)
+ r.addrTakenFuncsBySig.SetHasher(hasher)
+ r.dynCallSites.SetHasher(hasher)
+ r.invokeSites.SetHasher(hasher)
+ r.concreteTypes.SetHasher(hasher)
+ r.interfaceTypes.SetHasher(hasher)
+
+ // Visit functions, processing their instructions, and adding
+ // new functions to the worklist, until a fixed point is
+ // reached.
+ var shadow []*ssa.Function // for efficiency, we double-buffer the worklist
+ r.worklist = append(r.worklist, roots...)
+ for len(r.worklist) > 0 {
+ shadow, r.worklist = r.worklist, shadow[:0]
+ for _, f := range shadow {
+ r.visitFunc(f)
+ }
+ }
+ return r.result
+}
+
+// interfaces(C) returns all currently known interfaces implemented by C.
+func (r *rta) interfaces(C types.Type) []*types.Interface {
+ // Ascertain set of interfaces C implements
+ // and update 'implements' relation.
+ var ifaces []*types.Interface
+ r.interfaceTypes.Iterate(func(I types.Type, concs interface{}) {
+ if I := I.(*types.Interface); types.Implements(C, I) {
+ concs, _ := concs.([]types.Type)
+ r.interfaceTypes.Set(I, append(concs, C))
+ ifaces = append(ifaces, I)
+ }
+ })
+ r.concreteTypes.Set(C, ifaces)
+ return ifaces
+}
+
+// implementations(I) returns all currently known concrete types that implement I.
+func (r *rta) implementations(I *types.Interface) []types.Type {
+ var concs []types.Type
+ if v := r.interfaceTypes.At(I); v != nil {
+ concs = v.([]types.Type)
+ } else {
+ // First time seeing this interface.
+ // Update the 'implements' relation.
+ r.concreteTypes.Iterate(func(C types.Type, ifaces interface{}) {
+ if types.Implements(C, I) {
+ ifaces, _ := ifaces.([]*types.Interface)
+ r.concreteTypes.Set(C, append(ifaces, I))
+ concs = append(concs, C)
+ }
+ })
+ r.interfaceTypes.Set(I, concs)
+ }
+ return concs
+}
+
+// addRuntimeType is called for each concrete type that can be the
+// dynamic type of some interface or reflect.Value.
+// Adapted from needMethods in go/ssa/builder.go
+//
+func (r *rta) addRuntimeType(T types.Type, skip bool) {
+ if prev, ok := r.result.RuntimeTypes.At(T).(bool); ok {
+ if skip && !prev {
+ r.result.RuntimeTypes.Set(T, skip)
+ }
+ return
+ }
+ r.result.RuntimeTypes.Set(T, skip)
+
+ mset := r.prog.MethodSets.MethodSet(T)
+
+ if _, ok := T.Underlying().(*types.Interface); !ok {
+ // T is a new concrete type.
+ for i, n := 0, mset.Len(); i < n; i++ {
+ sel := mset.At(i)
+ m := sel.Obj()
+
+ if m.Exported() {
+ // Exported methods are always potentially callable via reflection.
+ r.addReachable(r.prog.MethodValue(sel), true)
+ }
+ }
+
+ // Add callgraph edge for each existing dynamic
+ // "invoke"-mode call via that interface.
+ for _, I := range r.interfaces(T) {
+ sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction)
+ for _, site := range sites {
+ r.addInvokeEdge(site, T)
+ }
+ }
+ }
+
+ // Precondition: T is not a method signature (*Signature with Recv()!=nil).
+ // Recursive case: skip => don't call makeMethods(T).
+ // Each package maintains its own set of types it has visited.
+
+ var n *types.Named
+ switch T := T.(type) {
+ case *types.Named:
+ n = T
+ case *types.Pointer:
+ n, _ = T.Elem().(*types.Named)
+ }
+ if n != nil {
+ owner := n.Obj().Pkg()
+ if owner == nil {
+ return // built-in error type
+ }
+ }
+
+ // Recursion over signatures of each exported method.
+ for i := 0; i < mset.Len(); i++ {
+ if mset.At(i).Obj().Exported() {
+ sig := mset.At(i).Type().(*types.Signature)
+ r.addRuntimeType(sig.Params(), true) // skip the Tuple itself
+ r.addRuntimeType(sig.Results(), true) // skip the Tuple itself
+ }
+ }
+
+ switch t := T.(type) {
+ case *types.Basic:
+ // nop
+
+ case *types.Interface:
+ // nop---handled by recursion over method set.
+
+ case *types.Pointer:
+ r.addRuntimeType(t.Elem(), false)
+
+ case *types.Slice:
+ r.addRuntimeType(t.Elem(), false)
+
+ case *types.Chan:
+ r.addRuntimeType(t.Elem(), false)
+
+ case *types.Map:
+ r.addRuntimeType(t.Key(), false)
+ r.addRuntimeType(t.Elem(), false)
+
+ case *types.Signature:
+ if t.Recv() != nil {
+ panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
+ }
+ r.addRuntimeType(t.Params(), true) // skip the Tuple itself
+ r.addRuntimeType(t.Results(), true) // skip the Tuple itself
+
+ case *types.Named:
+ // A pointer-to-named type can be derived from a named
+ // type via reflection. It may have methods too.
+ r.addRuntimeType(types.NewPointer(T), false)
+
+ // Consider 'type T struct{S}' where S has methods.
+ // Reflection provides no way to get from T to struct{S},
+ // only to S, so the method set of struct{S} is unwanted,
+ // so set 'skip' flag during recursion.
+ r.addRuntimeType(t.Underlying(), true)
+
+ case *types.Array:
+ r.addRuntimeType(t.Elem(), false)
+
+ case *types.Struct:
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ r.addRuntimeType(t.Field(i).Type(), false)
+ }
+
+ case *types.Tuple:
+ for i, n := 0, t.Len(); i < n; i++ {
+ r.addRuntimeType(t.At(i).Type(), false)
+ }
+
+ default:
+ panic(T)
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/callgraph/rta/rta14_test.go b/go/src/golang.org/x/tools/go/callgraph/rta/rta14_test.go
new file mode 100644
index 0000000..fd0c71d
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/callgraph/rta/rta14_test.go
@@ -0,0 +1,141 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// No testdata on Android.
+
+// +build !android
+
+package rta_test
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "sort"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/callgraph/rta"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+var inputs = []string{
+ "testdata/func.go",
+ "testdata/rtype.go",
+ "testdata/iface.go",
+}
+
+func expectation(f *ast.File) (string, token.Pos) {
+ for _, c := range f.Comments {
+ text := strings.TrimSpace(c.Text())
+ if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
+ return t, c.Pos()
+ }
+ }
+ return "", token.NoPos
+}
+
+// TestRTA runs RTA on each file in inputs, prints the results, and
+// compares it with the golden results embedded in the WANT comment at
+// the end of the file.
+//
+// The results string consists of two parts: the set of dynamic call
+// edges, "f --> g", one per line, and the set of reachable functions,
+// one per line. Each set is sorted.
+//
+func TestRTA(t *testing.T) {
+ for _, filename := range inputs {
+ content, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Errorf("couldn't read file '%s': %s", filename, err)
+ continue
+ }
+
+ conf := loader.Config{
+ ParserMode: parser.ParseComments,
+ }
+ f, err := conf.ParseFile(filename, content)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ want, pos := expectation(f)
+ if pos == token.NoPos {
+ t.Errorf("No WANT: comment in %s", filename)
+ continue
+ }
+
+ conf.CreateFromFiles("main", f)
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ prog := ssautil.CreateProgram(iprog, 0)
+ mainPkg := prog.Package(iprog.Created[0].Pkg)
+ prog.Build()
+
+ res := rta.Analyze([]*ssa.Function{
+ mainPkg.Func("main"),
+ mainPkg.Func("init"),
+ }, true)
+
+ if got := printResult(res, mainPkg.Pkg); got != want {
+ t.Errorf("%s: got:\n%s\nwant:\n%s",
+ prog.Fset.Position(pos), got, want)
+ }
+ }
+}
+
+func printResult(res *rta.Result, from *types.Package) string {
+ var buf bytes.Buffer
+
+ writeSorted := func(ss []string) {
+ sort.Strings(ss)
+ for _, s := range ss {
+ fmt.Fprintf(&buf, " %s\n", s)
+ }
+ }
+
+ buf.WriteString("Dynamic calls\n")
+ var edges []string
+ callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error {
+ if strings.Contains(e.Description(), "dynamic") {
+ edges = append(edges, fmt.Sprintf("%s --> %s",
+ e.Caller.Func.RelString(from),
+ e.Callee.Func.RelString(from)))
+ }
+ return nil
+ })
+ writeSorted(edges)
+
+ buf.WriteString("Reachable functions\n")
+ var reachable []string
+ for f := range res.Reachable {
+ reachable = append(reachable, f.RelString(from))
+ }
+ writeSorted(reachable)
+
+ buf.WriteString("Reflect types\n")
+ var rtypes []string
+ res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) {
+ if value == false { // accessible to reflection
+ rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from)))
+ }
+ })
+ writeSorted(rtypes)
+
+ return strings.TrimSpace(buf.String())
+}
diff --git a/go/src/golang.org/x/tools/go/callgraph/rta/rta_test.go b/go/src/golang.org/x/tools/go/callgraph/rta/rta_test.go
index e5aa846..5046521 100644
--- a/go/src/golang.org/x/tools/go/callgraph/rta/rta_test.go
+++ b/go/src/golang.org/x/tools/go/callgraph/rta/rta_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// No testdata on Android.
// +build !android
@@ -14,6 +16,7 @@
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"io/ioutil"
"sort"
"strings"
@@ -24,7 +27,6 @@
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
var inputs = []string{
@@ -83,14 +85,14 @@
prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
- prog.BuildAll()
+ prog.Build()
res := rta.Analyze([]*ssa.Function{
mainPkg.Func("main"),
mainPkg.Func("init"),
}, true)
- if got := printResult(res, mainPkg.Object); got != want {
+ if got := printResult(res, mainPkg.Pkg); got != want {
t.Errorf("%s: got:\n%s\nwant:\n%s",
prog.Fset.Position(pos), got, want)
}
diff --git a/go/src/golang.org/x/tools/go/callgraph/static/static_test.go b/go/src/golang.org/x/tools/go/callgraph/static/static_test.go
index 62297f7..e1bfcd7 100644
--- a/go/src/golang.org/x/tools/go/callgraph/static/static_test.go
+++ b/go/src/golang.org/x/tools/go/callgraph/static/static_test.go
@@ -63,7 +63,7 @@
P := iprog.Created[0].Pkg
prog := ssautil.CreateProgram(iprog, 0)
- prog.BuildAll()
+ prog.Build()
cg := static.CallGraph(prog)
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/bimport.go b/go/src/golang.org/x/tools/go/gcimporter15/bimport.go
new file mode 100644
index 0000000..525e12b
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/bimport.go
@@ -0,0 +1,685 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go, tagged for go1.5.
+
+package gcimporter
+
+import (
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "sort"
+ "unicode"
+ "unicode/utf8"
+)
+
+// BImportData imports a package from the serialized package data
+// and returns the number of bytes consumed and a reference to the package.
+// If data is obviously malformed, an error is returned but in
+// general it is not recommended to call BImportData on untrusted data.
+func BImportData(imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
+ p := importer{
+ imports: imports,
+ data: data,
+ }
+ p.buf = p.bufarray[:]
+
+ // read low-level encoding format
+ switch format := p.byte(); format {
+ case 'c':
+ // compact format - nothing to do
+ case 'd':
+ p.debugFormat = true
+ default:
+ return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
+ }
+
+ // --- generic export data ---
+
+ if v := p.string(); v != "v0" {
+ return p.read, nil, fmt.Errorf("unknown version: %s", v)
+ }
+
+ // populate typList with predeclared "known" types
+ p.typList = append(p.typList, predeclared...)
+
+ // read package data
+ // TODO(gri) clean this up
+ i := p.tagOrIndex()
+ if i != packageTag {
+ panic(fmt.Sprintf("package tag expected, got %d", i))
+ }
+ name := p.string()
+ if s := p.string(); s != "" {
+ panic(fmt.Sprintf("empty path expected, got %s", s))
+ }
+ pkg := p.imports[path]
+ if pkg == nil {
+ pkg = types.NewPackage(path, name)
+ p.imports[path] = pkg
+ }
+ p.pkgList = append(p.pkgList, pkg)
+
+ if debug && p.pkgList[0] != pkg {
+ panic("imported packaged not found in pkgList[0]")
+ }
+
+ // read compiler-specific flags
+ p.string() // discard
+
+ // read consts
+ for i := p.int(); i > 0; i-- {
+ name := p.string()
+ typ := p.typ(nil)
+ val := p.value()
+ p.declare(types.NewConst(token.NoPos, pkg, name, typ, val))
+ }
+
+ // read vars
+ for i := p.int(); i > 0; i-- {
+ name := p.string()
+ typ := p.typ(nil)
+ p.declare(types.NewVar(token.NoPos, pkg, name, typ))
+ }
+
+ // read funcs
+ for i := p.int(); i > 0; i-- {
+ name := p.string()
+ sig := p.typ(nil).(*types.Signature)
+ p.int() // read and discard index of inlined function body
+ p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
+ }
+
+ // read types
+ for i := p.int(); i > 0; i-- {
+ // name is parsed as part of named type and the
+ // type object is added to scope via respective
+ // named type
+ _ = p.typ(nil).(*types.Named)
+ }
+
+ // ignore compiler-specific import data
+
+ // complete interfaces
+ for _, typ := range p.typList {
+ if it, ok := typ.(*types.Interface); ok {
+ it.Complete()
+ }
+ }
+
+ // record all referenced packages as imports
+ list := append(([]*types.Package)(nil), p.pkgList[1:]...)
+ sort.Sort(byPath(list))
+ pkg.SetImports(list)
+
+ // package was imported completely and without errors
+ pkg.MarkComplete()
+
+ return p.read, pkg, nil
+}
+
+type importer struct {
+ imports map[string]*types.Package
+ data []byte
+ buf []byte // for reading strings
+ bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
+ pkgList []*types.Package
+ typList []types.Type
+
+ debugFormat bool
+ read int // bytes read
+}
+
+func (p *importer) declare(obj types.Object) {
+ if alt := p.pkgList[0].Scope().Insert(obj); alt != nil {
+ // This can only happen if we import a package a second time.
+ panic(fmt.Sprintf("%s already declared", alt.Name()))
+ }
+}
+
+func (p *importer) pkg() *types.Package {
+ // if the package was seen before, i is its index (>= 0)
+ i := p.tagOrIndex()
+ if i >= 0 {
+ return p.pkgList[i]
+ }
+
+ // otherwise, i is the package tag (< 0)
+ if i != packageTag {
+ panic(fmt.Sprintf("unexpected package tag %d", i))
+ }
+
+ // read package data
+ name := p.string()
+ path := p.string()
+
+ // we should never see an empty package name
+ if name == "" {
+ panic("empty package name in import")
+ }
+
+ // we should never see an empty import path
+ if path == "" {
+ panic("empty import path")
+ }
+
+ // if the package was imported before, use that one; otherwise create a new one
+ pkg := p.imports[path]
+ if pkg == nil {
+ pkg = types.NewPackage(path, name)
+ p.imports[path] = pkg
+ }
+ p.pkgList = append(p.pkgList, pkg)
+
+ return pkg
+}
+
+func (p *importer) record(t types.Type) {
+ p.typList = append(p.typList, t)
+}
+
+// A dddSlice is a types.Type representing ...T parameters.
+// It only appears for parameter types and does not escape
+// the importer.
+type dddSlice struct {
+ elem types.Type
+}
+
+func (t *dddSlice) Underlying() types.Type { return t }
+func (t *dddSlice) String() string { return "..." + t.elem.String() }
+
+// parent is the package which declared the type; parent == nil means
+// the package currently imported. The parent package is needed for
+// exported struct fields and interface methods which don't contain
+// explicit package information in the export data.
+func (p *importer) typ(parent *types.Package) types.Type {
+ // if the type was seen before, i is its index (>= 0)
+ i := p.tagOrIndex()
+ if i >= 0 {
+ return p.typList[i]
+ }
+
+ // otherwise, i is the type tag (< 0)
+ switch i {
+ case namedTag:
+ // read type object
+ name := p.string()
+ parent = p.pkg()
+ scope := parent.Scope()
+ obj := scope.Lookup(name)
+
+ // if the object doesn't exist yet, create and insert it
+ if obj == nil {
+ obj = types.NewTypeName(token.NoPos, parent, name, nil)
+ scope.Insert(obj)
+ }
+
+ if _, ok := obj.(*types.TypeName); !ok {
+ panic(fmt.Sprintf("pkg = %s, name = %s => %s", parent, name, obj))
+ }
+
+ // associate new named type with obj if it doesn't exist yet
+ t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
+
+ // but record the existing type, if any
+ t := obj.Type().(*types.Named)
+ p.record(t)
+
+ // read underlying type
+ t0.SetUnderlying(p.typ(parent))
+
+ // interfaces don't have associated methods
+ if _, ok := t0.Underlying().(*types.Interface); ok {
+ return t
+ }
+
+ // read associated methods
+ for i := p.int(); i > 0; i-- {
+ name := p.string()
+ recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
+ params, isddd := p.paramList()
+ result, _ := p.paramList()
+ p.int() // read and discard index of inlined function body
+ sig := types.NewSignature(recv.At(0), params, result, isddd)
+ t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig))
+ }
+
+ return t
+
+ case arrayTag:
+ t := new(types.Array)
+ p.record(t)
+
+ n := p.int64()
+ *t = *types.NewArray(p.typ(parent), n)
+ return t
+
+ case sliceTag:
+ t := new(types.Slice)
+ p.record(t)
+
+ *t = *types.NewSlice(p.typ(parent))
+ return t
+
+ case dddTag:
+ t := new(dddSlice)
+ p.record(t)
+
+ t.elem = p.typ(parent)
+ return t
+
+ case structTag:
+ t := new(types.Struct)
+ p.record(t)
+
+ n := p.int()
+ fields := make([]*types.Var, n)
+ tags := make([]string, n)
+ for i := range fields {
+ fields[i] = p.field(parent)
+ tags[i] = p.string()
+ }
+ *t = *types.NewStruct(fields, tags)
+ return t
+
+ case pointerTag:
+ t := new(types.Pointer)
+ p.record(t)
+
+ *t = *types.NewPointer(p.typ(parent))
+ return t
+
+ case signatureTag:
+ t := new(types.Signature)
+ p.record(t)
+
+ params, isddd := p.paramList()
+ result, _ := p.paramList()
+ *t = *types.NewSignature(nil, params, result, isddd)
+ return t
+
+ case interfaceTag:
+ // Create a dummy entry in the type list. This is safe because we
+ // cannot expect the interface type to appear in a cycle, as any
+ // such cycle must contain a named type which would have been
+ // first defined earlier.
+ n := len(p.typList)
+ p.record(nil)
+
+ // no embedded interfaces with gc compiler
+ if p.int() != 0 {
+ panic("unexpected embedded interface")
+ }
+
+ // read methods
+ methods := make([]*types.Func, p.int())
+ for i := range methods {
+ pkg, name := p.fieldName(parent)
+ params, isddd := p.paramList()
+ result, _ := p.paramList()
+ sig := types.NewSignature(nil, params, result, isddd)
+ methods[i] = types.NewFunc(token.NoPos, pkg, name, sig)
+ }
+
+ t := types.NewInterface(methods, nil)
+ p.typList[n] = t
+ return t
+
+ case mapTag:
+ t := new(types.Map)
+ p.record(t)
+
+ key := p.typ(parent)
+ val := p.typ(parent)
+ *t = *types.NewMap(key, val)
+ return t
+
+ case chanTag:
+ t := new(types.Chan)
+ p.record(t)
+
+ var dir types.ChanDir
+ // tag values must match the constants in cmd/compile/internal/gc/go.go
+ switch d := p.int(); d {
+ case 1 /* Crecv */ :
+ dir = types.RecvOnly
+ case 2 /* Csend */ :
+ dir = types.SendOnly
+ case 3 /* Cboth */ :
+ dir = types.SendRecv
+ default:
+ panic(fmt.Sprintf("unexpected channel dir %d", d))
+ }
+ val := p.typ(parent)
+ *t = *types.NewChan(dir, val)
+ return t
+
+ default:
+ panic(fmt.Sprintf("unexpected type tag %d", i))
+ }
+}
+
+func (p *importer) field(parent *types.Package) *types.Var {
+ pkg, name := p.fieldName(parent)
+ typ := p.typ(parent)
+
+ anonymous := false
+ if name == "" {
+ // anonymous field - typ must be T or *T and T must be a type name
+ switch typ := deref(typ).(type) {
+ case *types.Basic: // basic types are named types
+ pkg = nil // // objects defined in Universe scope have no package
+ name = typ.Name()
+ case *types.Named:
+ name = typ.Obj().Name()
+ default:
+ panic("anonymous field expected")
+ }
+ anonymous = true
+ }
+
+ return types.NewField(token.NoPos, pkg, name, typ, anonymous)
+}
+
+func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
+ pkg := parent
+ if pkg == nil {
+ // use the imported package instead
+ pkg = p.pkgList[0]
+ }
+ name := p.string()
+ if name == "" {
+ return pkg, "" // anonymous
+ }
+ if name == "?" || name != "_" && !exported(name) {
+ // explicitly qualified field
+ if name == "?" {
+ name = "" // anonymous
+ }
+ pkg = p.pkg()
+ }
+ return pkg, name
+}
+
+func (p *importer) paramList() (*types.Tuple, bool) {
+ n := p.int()
+ if n == 0 {
+ return nil, false
+ }
+ // negative length indicates unnamed parameters
+ named := true
+ if n < 0 {
+ n = -n
+ named = false
+ }
+ // n > 0
+ params := make([]*types.Var, n)
+ isddd := false
+ for i := range params {
+ params[i], isddd = p.param(named)
+ }
+ return types.NewTuple(params...), isddd
+}
+
+func (p *importer) param(named bool) (*types.Var, bool) {
+ t := p.typ(nil)
+ td, isddd := t.(*dddSlice)
+ if isddd {
+ t = types.NewSlice(td.elem)
+ }
+
+ var name string
+ if named {
+ name = p.string()
+ if name == "" {
+ panic("expected named parameter")
+ }
+ }
+
+ // read and discard compiler-specific info
+ p.string()
+
+ return types.NewVar(token.NoPos, nil, name, t), isddd
+}
+
+func exported(name string) bool {
+ ch, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(ch)
+}
+
+func (p *importer) value() constant.Value {
+ switch tag := p.tagOrIndex(); tag {
+ case falseTag:
+ return constant.MakeBool(false)
+ case trueTag:
+ return constant.MakeBool(true)
+ case int64Tag:
+ return constant.MakeInt64(p.int64())
+ case floatTag:
+ return p.float()
+ case complexTag:
+ re := p.float()
+ im := p.float()
+ return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+ case stringTag:
+ return constant.MakeString(p.string())
+ default:
+ panic(fmt.Sprintf("unexpected value tag %d", tag))
+ }
+}
+
+func (p *importer) float() constant.Value {
+ sign := p.int()
+ if sign == 0 {
+ return constant.MakeInt64(0)
+ }
+
+ exp := p.int()
+ mant := []byte(p.string()) // big endian
+
+ // remove leading 0's if any
+ for len(mant) > 0 && mant[0] == 0 {
+ mant = mant[1:]
+ }
+
+ // convert to little endian
+ // TODO(gri) go/constant should have a more direct conversion function
+ // (e.g., once it supports a big.Float based implementation)
+ for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 {
+ mant[i], mant[j] = mant[j], mant[i]
+ }
+
+ // adjust exponent (constant.MakeFromBytes creates an integer value,
+ // but mant represents the mantissa bits such that 0.5 <= mant < 1.0)
+ exp -= len(mant) << 3
+ if len(mant) > 0 {
+ for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 {
+ exp++
+ }
+ }
+
+ x := constant.MakeFromBytes(mant)
+ switch {
+ case exp < 0:
+ d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
+ x = constant.BinaryOp(x, token.QUO, d)
+ case exp > 0:
+ x = constant.Shift(x, token.SHL, uint(exp))
+ }
+
+ if sign < 0 {
+ x = constant.UnaryOp(token.SUB, x, 0)
+ }
+ return x
+}
+
+// ----------------------------------------------------------------------------
+// Low-level decoders
+
+func (p *importer) tagOrIndex() int {
+ if p.debugFormat {
+ p.marker('t')
+ }
+
+ return int(p.rawInt64())
+}
+
+func (p *importer) int() int {
+ x := p.int64()
+ if int64(int(x)) != x {
+ panic("exported integer too large")
+ }
+ return int(x)
+}
+
+func (p *importer) int64() int64 {
+ if p.debugFormat {
+ p.marker('i')
+ }
+
+ return p.rawInt64()
+}
+
+func (p *importer) string() string {
+ if p.debugFormat {
+ p.marker('s')
+ }
+
+ if n := int(p.rawInt64()); n > 0 {
+ if cap(p.buf) < n {
+ p.buf = make([]byte, n)
+ } else {
+ p.buf = p.buf[:n]
+ }
+ for i := 0; i < n; i++ {
+ p.buf[i] = p.byte()
+ }
+ return string(p.buf)
+ }
+
+ return ""
+}
+
+func (p *importer) marker(want byte) {
+ if got := p.byte(); got != want {
+ panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read))
+ }
+
+ pos := p.read
+ if n := int(p.rawInt64()); n != pos {
+ panic(fmt.Sprintf("incorrect position: got %d; want %d", n, pos))
+ }
+}
+
+// rawInt64 should only be used by low-level decoders
+func (p *importer) rawInt64() int64 {
+ i, err := binary.ReadVarint(p)
+ if err != nil {
+ panic(fmt.Sprintf("read error: %v", err))
+ }
+ return i
+}
+
+// needed for binary.ReadVarint in rawInt64
+func (p *importer) ReadByte() (byte, error) {
+ return p.byte(), nil
+}
+
+// byte is the bottleneck interface for reading p.data.
+// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
+func (p *importer) byte() byte {
+ b := p.data[0]
+ r := 1
+ if b == '|' {
+ b = p.data[1]
+ r = 2
+ switch b {
+ case 'S':
+ b = '$'
+ case '|':
+ // nothing to do
+ default:
+ panic("unexpected escape sequence in export data")
+ }
+ }
+ p.data = p.data[r:]
+ p.read += r
+ return b
+
+}
+
+// ----------------------------------------------------------------------------
+// Export format
+
+// Tags. Must be < 0.
+const (
+ // Packages
+ packageTag = -(iota + 1)
+
+ // Types
+ namedTag
+ arrayTag
+ sliceTag
+ dddTag
+ structTag
+ pointerTag
+ signatureTag
+ interfaceTag
+ mapTag
+ chanTag
+
+ // Values
+ falseTag
+ trueTag
+ int64Tag
+ floatTag
+ fractionTag // not used by gc
+ complexTag
+ stringTag
+)
+
+var predeclared = []types.Type{
+ // basic types
+ types.Typ[types.Bool],
+ types.Typ[types.Int],
+ types.Typ[types.Int8],
+ types.Typ[types.Int16],
+ types.Typ[types.Int32],
+ types.Typ[types.Int64],
+ types.Typ[types.Uint],
+ types.Typ[types.Uint8],
+ types.Typ[types.Uint16],
+ types.Typ[types.Uint32],
+ types.Typ[types.Uint64],
+ types.Typ[types.Uintptr],
+ types.Typ[types.Float32],
+ types.Typ[types.Float64],
+ types.Typ[types.Complex64],
+ types.Typ[types.Complex128],
+ types.Typ[types.String],
+
+ // aliases
+ types.Universe.Lookup("byte").Type(),
+ types.Universe.Lookup("rune").Type(),
+
+ // error
+ types.Universe.Lookup("error").Type(),
+
+ // untyped types
+ types.Typ[types.UntypedBool],
+ types.Typ[types.UntypedInt],
+ types.Typ[types.UntypedRune],
+ types.Typ[types.UntypedFloat],
+ types.Typ[types.UntypedComplex],
+ types.Typ[types.UntypedString],
+ types.Typ[types.UntypedNil],
+
+ // package unsafe
+ types.Typ[types.UnsafePointer],
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/exportdata.go b/go/src/golang.org/x/tools/go/gcimporter15/exportdata.go
new file mode 100644
index 0000000..4e1d1e4
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/exportdata.go
@@ -0,0 +1,115 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go, tagged for go1.5.
+
+// This file implements FindExportData.
+
+package gcimporter
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 16+12+6+6+8+10+2)
+ _, err = io.ReadFull(r, hdr)
+ if err != nil {
+ return
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = errors.New("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:16]))
+ return
+}
+
+// FindExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function. The hdr result
+// is the string before the export data, either "$$" or "$$B".
+//
+func FindExportData(r *bufio.Reader) (hdr string, err error) {
+ // Read first line to make sure this is an object file.
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+
+ if string(line) == "!<arch>\n" {
+ // Archive file. Scan to __.PKGDEF.
+ var name string
+ var size int
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+
+ // Optional leading __.GOSYMDEF or __.SYMDEF.
+ // Read and discard.
+ if name == "__.SYMDEF" || name == "__.GOSYMDEF" {
+ const block = 4096
+ tmp := make([]byte, block)
+ for size > 0 {
+ n := size
+ if n > block {
+ n = block
+ }
+ if _, err = io.ReadFull(r, tmp[:n]); err != nil {
+ return
+ }
+ size -= n
+ }
+
+ if name, _, err = readGopackHeader(r); err != nil {
+ return
+ }
+ }
+
+ // First real entry should be __.PKGDEF.
+ if name != "__.PKGDEF" {
+ err = errors.New("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+
+ // Now at __.PKGDEF in archive or still at beginning of file.
+ // Either way, line should begin with "go object ".
+ if !strings.HasPrefix(string(line), "go object ") {
+ err = errors.New("not a go object file")
+ return
+ }
+
+ // Skip over object header to export data.
+ // Begins after first line starting with $$.
+ for line[0] != '$' {
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+ hdr = string(line)
+
+ return
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/gcimporter.go b/go/src/golang.org/x/tools/go/gcimporter15/gcimporter.go
new file mode 100644
index 0000000..5bc5696
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/gcimporter.go
@@ -0,0 +1,1025 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go, tagged for go1.5,
+// and minimally adjusted to make it build.
+
+// Package gcimporter15 provides various functions for reading
+// gc-generated object files that can be used to implement the
+// Importer interface defined by the Go 1.5 standard library package.
+//
+// This package serves as a stop-gap for missing features in the
+// standard library's go/importer package, specifically customizable
+// package data lookup. This package should be deleted once that
+// functionality becomes available in the standard library.
+package gcimporter // import "golang.org/x/tools/go/gcimporter15"
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "go/build"
+ exact "go/constant"
+ "go/token"
+ "go/types"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "text/scanner"
+)
+
+// debugging/development support
+const debug = false
+
+var pkgExts = [...]string{".a", ".o"}
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context).
+// If no file was found, an empty filename is returned.
+//
+func FindPkg(path, srcDir string) (filename, id string) {
+ if path == "" {
+ return
+ }
+
+ var noext string
+ switch {
+ default:
+ // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+ // Don't require the source files to be present.
+ bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+ if bp.PkgObj == "" {
+ return
+ }
+ noext = strings.TrimSuffix(bp.PkgObj, ".a")
+ id = bp.ImportPath
+
+ case build.IsLocalImport(path):
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ noext = filepath.Join(srcDir, path)
+ id = noext
+
+ case filepath.IsAbs(path):
+ // for completeness only - go/build.Import
+ // does not support absolute imports
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ id = path
+ }
+
+ if false { // for debugging
+ if path != id {
+ fmt.Printf("%s -> %s\n", path, id)
+ }
+ }
+
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+// ImportData imports a package by reading the gc-generated export data,
+// adds the corresponding package object to the packages map indexed by id,
+// and returns the object.
+//
+// The packages map must contains all packages already imported. The data
+// reader position must be the beginning of the export data section. The
+// filename is only used in error messages.
+//
+// If packages[id] contains the completely imported package, that package
+// can be used directly, and there is no need to call this function (but
+// there is also no harm but for extra time used).
+//
+func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
+ // support for parser error handling
+ defer func() {
+ switch r := recover().(type) {
+ case nil:
+ // nothing to do
+ case importError:
+ err = r
+ default:
+ panic(r) // internal error
+ }
+ }()
+
+ var p parser
+ p.init(filename, id, data, packages)
+ pkg = p.parseExport()
+
+ return
+}
+
+// Import imports a gc-generated package given its import path and srcDir, adds
+// the corresponding package object to the packages map, and returns the object.
+// The packages map must contain all packages already imported.
+//
+func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types.Package, err error) {
+ filename, id := FindPkg(path, srcDir)
+ if filename == "" {
+ if path == "unsafe" {
+ return types.Unsafe, nil
+ }
+ err = fmt.Errorf("can't find import: %s", id)
+ return
+ }
+
+ // no need to re-import if the package was imported completely before
+ if pkg = packages[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer func() {
+ f.Close()
+ if err != nil {
+ // add file name to error
+ err = fmt.Errorf("reading export data: %s: %v", filename, err)
+ }
+ }()
+
+ var hdr string
+ buf := bufio.NewReader(f)
+ if hdr, err = FindExportData(buf); err != nil {
+ return
+ }
+
+ switch hdr {
+ case "$$\n":
+ return ImportData(packages, filename, id, buf)
+ case "$$B\n":
+ var data []byte
+ data, err = ioutil.ReadAll(buf)
+ if err == nil {
+ _, pkg, err = BImportData(packages, data, path)
+ return
+ }
+ default:
+ err = fmt.Errorf("unknown export data header: %q", hdr)
+ }
+
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Parser
+
+// TODO(gri) Imported objects don't have position information.
+// Ideally use the debug table line info; alternatively
+// create some fake position (or the position of the
+// import). That way error messages referring to imported
+// objects can print meaningful information.
+
+// parser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type parser struct {
+ scanner scanner.Scanner
+ tok rune // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ id string // package id of imported package
+ sharedPkgs map[string]*types.Package // package id -> package object (across importer)
+ localPkgs map[string]*types.Package // package id -> package object (just this package)
+}
+
+func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+ p.scanner.Whitespace = 1<<'\t' | 1<<' '
+ p.scanner.Filename = filename // for good error messages
+ p.next()
+ p.id = id
+ p.sharedPkgs = packages
+ if debug {
+ // check consistency of packages map
+ for _, pkg := range packages {
+ if pkg.Name() == "" {
+ fmt.Printf("no package name for %s\n", pkg.Path())
+ }
+ }
+ }
+}
+
+func (p *parser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+ if debug {
+ fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+ }
+}
+
+func declTypeName(pkg *types.Package, name string) *types.TypeName {
+ scope := pkg.Scope()
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*types.TypeName)
+ }
+ obj := types.NewTypeName(token.NoPos, pkg, name, nil)
+ // a named type may be referred to before the underlying type
+ // is known - set it up
+ types.NewNamed(obj, nil, nil)
+ scope.Insert(obj)
+ return obj
+}
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+ pos scanner.Position
+ err error
+}
+
+func (e importError) Error() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *parser) error(err interface{}) {
+ if s, ok := err.(string); ok {
+ err = errors.New(s)
+ }
+ // panic with a runtime.Error if err is not an error
+ panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Sprintf(format, args...))
+}
+
+func (p *parser) expect(tok rune) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+func (p *parser) expectSpecial(tok string) {
+ sep := 'x' // not white space
+ i := 0
+ for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ i++
+ }
+ if i < len(tok) {
+ p.errorf("expected %q, got %q", tok, tok[0:i])
+ }
+}
+
+func (p *parser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Qualified and unqualified names
+
+// PackageId = string_lit .
+//
+func (p *parser) parsePackageId() string {
+ id, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+ // id == "" stands for the imported package id
+ // (only known at time of package installation)
+ if id == "" {
+ id = p.id
+ }
+ return id
+}
+
+// PackageName = ident .
+//
+func (p *parser) parsePackageName() string {
+ return p.expect(scanner.Ident)
+}
+
+// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
+func (p *parser) parseDotIdent() string {
+ ident := ""
+ if p.tok != scanner.Int {
+ sep := 'x' // not white space
+ for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
+ ident += p.lit
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ }
+ }
+ if ident == "" {
+ p.expect(scanner.Ident) // use expect() for error handling
+ }
+ return ident
+}
+
+// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
+//
+func (p *parser) parseQualifiedName() (id, name string) {
+ p.expect('@')
+ id = p.parsePackageId()
+ p.expect('.')
+ // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
+ if p.tok == '?' {
+ p.next()
+ } else {
+ name = p.parseDotIdent()
+ }
+ return
+}
+
+// getPkg returns the package for a given id. If the package is
+// not found, create the package and add it to the p.localPkgs
+// and p.sharedPkgs maps. name is the (expected) name of the
+// package. If name == "", the package name is expected to be
+// set later via an import clause in the export data.
+//
+// id identifies a package, usually by a canonical package path like
+// "encoding/json" but possibly by a non-canonical import path like
+// "./json".
+//
+func (p *parser) getPkg(id, name string) *types.Package {
+ // package unsafe is not in the packages maps - handle explicitly
+ if id == "unsafe" {
+ return types.Unsafe
+ }
+
+ pkg := p.localPkgs[id]
+ if pkg == nil {
+ // first import of id from this package
+ pkg = p.sharedPkgs[id]
+ if pkg == nil {
+ // first import of id by this importer;
+ // add (possibly unnamed) pkg to shared packages
+ pkg = types.NewPackage(id, name)
+ p.sharedPkgs[id] = pkg
+ }
+ // add (possibly unnamed) pkg to local packages
+ if p.localPkgs == nil {
+ p.localPkgs = make(map[string]*types.Package)
+ }
+ p.localPkgs[id] = pkg
+ } else if name != "" {
+ // package exists already and we have an expected package name;
+ // make sure names match or set package name if necessary
+ if pname := pkg.Name(); pname == "" {
+ setName(pkg, name)
+ } else if pname != name {
+ p.errorf("%s package name mismatch: %s (given) vs %s (expected)", pname, name)
+ }
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package id is resolved to an imported *types.Package.
+//
+func (p *parser) parseExportedName() (pkg *types.Package, name string) {
+ id, name := p.parseQualifiedName()
+ pkg = p.getPkg(id, "")
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *parser) parseBasicType() types.Type {
+ id := p.expect(scanner.Ident)
+ obj := types.Universe.Lookup(id)
+ if obj, ok := obj.(*types.TypeName); ok {
+ return obj.Type()
+ }
+ p.errorf("not a basic type: %s", id)
+ return nil
+}
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *parser) parseArrayType(parent *types.Package) types.Type {
+ // "[" already consumed and lookahead known not to be "]"
+ lit := p.expect(scanner.Int)
+ p.expect(']')
+ elem := p.parseType(parent)
+ n, err := strconv.ParseInt(lit, 10, 64)
+ if err != nil {
+ p.error(err)
+ }
+ return types.NewArray(elem, n)
+}
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *parser) parseMapType(parent *types.Package) types.Type {
+ p.expectKeyword("map")
+ p.expect('[')
+ key := p.parseType(parent)
+ p.expect(']')
+ elem := p.parseType(parent)
+ return types.NewMap(key, elem)
+}
+
+// Name = identifier | "?" | QualifiedName .
+//
+// For unqualified and anonymous names, the returned package is the parent
+// package unless parent == nil, in which case the returned package is the
+// package being imported. (The parent package is not nil if the the name
+// is an unqualified struct field or interface method name belonging to a
+// type declared in another package.)
+//
+// For qualified names, the returned package is nil (and not created if
+// it doesn't exist yet) unless materializePkg is set (which creates an
+// unnamed package with valid package path). In the latter case, a
+// subequent import clause is expected to provide a name for the package.
+//
+func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
+ pkg = parent
+ if pkg == nil {
+ pkg = p.sharedPkgs[p.id]
+ }
+ switch p.tok {
+ case scanner.Ident:
+ name = p.lit
+ p.next()
+ case '?':
+ // anonymous
+ p.next()
+ case '@':
+ // exported name prefixed with package path
+ pkg = nil
+ var id string
+ id, name = p.parseQualifiedName()
+ if materializePkg {
+ pkg = p.getPkg(id, "")
+ }
+ default:
+ p.error("name expected")
+ }
+ return
+}
+
+func deref(typ types.Type) types.Type {
+ if p, _ := typ.(*types.Pointer); p != nil {
+ return p.Elem()
+ }
+ return typ
+}
+
+// Field = Name Type [ string_lit ] .
+//
+func (p *parser) parseField(parent *types.Package) (*types.Var, string) {
+ pkg, name := p.parseName(parent, true)
+ typ := p.parseType(parent)
+ anonymous := false
+ if name == "" {
+ // anonymous field - typ must be T or *T and T must be a type name
+ switch typ := deref(typ).(type) {
+ case *types.Basic: // basic types are named types
+ pkg = nil // objects defined in Universe scope have no package
+ name = typ.Name()
+ case *types.Named:
+ name = typ.Obj().Name()
+ default:
+ p.errorf("anonymous field expected")
+ }
+ anonymous = true
+ }
+ tag := ""
+ if p.tok == scanner.String {
+ s := p.expect(scanner.String)
+ var err error
+ tag, err = strconv.Unquote(s)
+ if err != nil {
+ p.errorf("invalid struct tag %s: %s", s, err)
+ }
+ }
+ return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
+}
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList = Field { ";" Field } .
+//
+func (p *parser) parseStructType(parent *types.Package) types.Type {
+ var fields []*types.Var
+ var tags []string
+
+ p.expectKeyword("struct")
+ p.expect('{')
+ for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+ if i > 0 {
+ p.expect(';')
+ }
+ fld, tag := p.parseField(parent)
+ if tag != "" && tags == nil {
+ tags = make([]string, i)
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+ fields = append(fields, fld)
+ }
+ p.expect('}')
+
+ return types.NewStruct(fields, tags)
+}
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
+//
+func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
+ _, name := p.parseName(nil, false)
+ // remove gc-specific parameter numbering
+ if i := strings.Index(name, "·"); i >= 0 {
+ name = name[:i]
+ }
+ if p.tok == '.' {
+ p.expectSpecial("...")
+ isVariadic = true
+ }
+ typ := p.parseType(nil)
+ if isVariadic {
+ typ = types.NewSlice(typ)
+ }
+ // ignore argument tag (e.g. "noescape")
+ if p.tok == scanner.String {
+ p.next()
+ }
+ // TODO(gri) should we provide a package?
+ par = types.NewVar(token.NoPos, nil, name, typ)
+ return
+}
+
+// Parameters = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
+ p.expect('(')
+ for p.tok != ')' && p.tok != scanner.EOF {
+ if len(list) > 0 {
+ p.expect(',')
+ }
+ par, variadic := p.parseParameter()
+ list = append(list, par)
+ if variadic {
+ if isVariadic {
+ p.error("... not on final argument")
+ }
+ isVariadic = true
+ }
+ }
+ p.expect(')')
+
+ return
+}
+
+// Signature = Parameters [ Result ] .
+// Result = Type | Parameters .
+//
+func (p *parser) parseSignature(recv *types.Var) *types.Signature {
+ params, isVariadic := p.parseParameters()
+
+ // optional result type
+ var results []*types.Var
+ if p.tok == '(' {
+ var variadic bool
+ results, variadic = p.parseParameters()
+ if variadic {
+ p.error("... not permitted on result type")
+ }
+ }
+
+ return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
+}
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList = Method { ";" Method } .
+// Method = Name Signature .
+//
+// The methods of embedded interfaces are always "inlined"
+// by the compiler and thus embedded interfaces are never
+// visible in the export data.
+//
+func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
+ var methods []*types.Func
+
+ p.expectKeyword("interface")
+ p.expect('{')
+ for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+ if i > 0 {
+ p.expect(';')
+ }
+ pkg, name := p.parseName(parent, true)
+ sig := p.parseSignature(nil)
+ methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
+ }
+ p.expect('}')
+
+ // Complete requires the type's embedded interfaces to be fully defined,
+ // but we do not define any
+ return types.NewInterface(methods, nil).Complete()
+}
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *parser) parseChanType(parent *types.Package) types.Type {
+ dir := types.SendRecv
+ if p.tok == scanner.Ident {
+ p.expectKeyword("chan")
+ if p.tok == '<' {
+ p.expectSpecial("<-")
+ dir = types.SendOnly
+ }
+ } else {
+ p.expectSpecial("<-")
+ p.expectKeyword("chan")
+ dir = types.RecvOnly
+ }
+ elem := p.parseType(parent)
+ return types.NewChan(dir, elem)
+}
+
+// Type =
+// BasicType | TypeName | ArrayType | SliceType | StructType |
+// PointerType | FuncType | InterfaceType | MapType | ChanType |
+// "(" Type ")" .
+//
+// BasicType = ident .
+// TypeName = ExportedName .
+// SliceType = "[" "]" Type .
+// PointerType = "*" Type .
+// FuncType = "func" Signature .
+//
+func (p *parser) parseType(parent *types.Package) types.Type {
+ switch p.tok {
+ case scanner.Ident:
+ switch p.lit {
+ default:
+ return p.parseBasicType()
+ case "struct":
+ return p.parseStructType(parent)
+ case "func":
+ // FuncType
+ p.next()
+ return p.parseSignature(nil)
+ case "interface":
+ return p.parseInterfaceType(parent)
+ case "map":
+ return p.parseMapType(parent)
+ case "chan":
+ return p.parseChanType(parent)
+ }
+ case '@':
+ // TypeName
+ pkg, name := p.parseExportedName()
+ return declTypeName(pkg, name).Type()
+ case '[':
+ p.next() // look ahead
+ if p.tok == ']' {
+ // SliceType
+ p.next()
+ return types.NewSlice(p.parseType(parent))
+ }
+ return p.parseArrayType(parent)
+ case '*':
+ // PointerType
+ p.next()
+ return types.NewPointer(p.parseType(parent))
+ case '<':
+ return p.parseChanType(parent)
+ case '(':
+ // "(" Type ")"
+ p.next()
+ typ := p.parseType(parent)
+ p.expect(')')
+ return typ
+ }
+ p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// ImportDecl = "import" PackageName PackageId .
+//
+func (p *parser) parseImportDecl() {
+ p.expectKeyword("import")
+ name := p.parsePackageName()
+ p.getPkg(p.parsePackageId(), name)
+}
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *parser) parseInt() string {
+ s := ""
+ switch p.tok {
+ case '-':
+ s = "-"
+ p.next()
+ case '+':
+ p.next()
+ }
+ return s + p.expect(scanner.Int)
+}
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
+ // mantissa
+ mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
+ if mant == nil {
+ panic("invalid mantissa")
+ }
+
+ if p.lit == "p" {
+ // exponent (base 2)
+ p.next()
+ exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
+ if err != nil {
+ p.error(err)
+ }
+ if exp < 0 {
+ denom := exact.MakeInt64(1)
+ denom = exact.Shift(denom, token.SHL, uint(-exp))
+ typ = types.Typ[types.UntypedFloat]
+ val = exact.BinaryOp(mant, token.QUO, denom)
+ return
+ }
+ if exp > 0 {
+ mant = exact.Shift(mant, token.SHL, uint(exp))
+ }
+ typ = types.Typ[types.UntypedFloat]
+ val = mant
+ return
+ }
+
+ typ = types.Typ[types.UntypedInt]
+ val = mant
+ return
+}
+
+// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
+// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
+// bool_lit = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit "i" ")" .
+// rune_lit = "(" int_lit "+" int_lit ")" .
+// string_lit = `"` { unicode_char } `"` .
+//
+func (p *parser) parseConstDecl() {
+ p.expectKeyword("const")
+ pkg, name := p.parseExportedName()
+
+ var typ0 types.Type
+ if p.tok != '=' {
+ // constant types are never structured - no need for parent type
+ typ0 = p.parseType(nil)
+ }
+
+ p.expect('=')
+ var typ types.Type
+ var val exact.Value
+ switch p.tok {
+ case scanner.Ident:
+ // bool_lit
+ if p.lit != "true" && p.lit != "false" {
+ p.error("expected true or false")
+ }
+ typ = types.Typ[types.UntypedBool]
+ val = exact.MakeBool(p.lit == "true")
+ p.next()
+
+ case '-', scanner.Int:
+ // int_lit
+ typ, val = p.parseNumber()
+
+ case '(':
+ // complex_lit or rune_lit
+ p.next()
+ if p.tok == scanner.Char {
+ p.next()
+ p.expect('+')
+ typ = types.Typ[types.UntypedRune]
+ _, val = p.parseNumber()
+ p.expect(')')
+ break
+ }
+ _, re := p.parseNumber()
+ p.expect('+')
+ _, im := p.parseNumber()
+ p.expectKeyword("i")
+ p.expect(')')
+ typ = types.Typ[types.UntypedComplex]
+ val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
+
+ case scanner.Char:
+ // rune_lit
+ typ = types.Typ[types.UntypedRune]
+ val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
+ p.next()
+
+ case scanner.String:
+ // string_lit
+ typ = types.Typ[types.UntypedString]
+ val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
+ p.next()
+
+ default:
+ p.errorf("expected literal got %s", scanner.TokenString(p.tok))
+ }
+
+ if typ0 == nil {
+ typ0 = typ
+ }
+
+ pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
+}
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *parser) parseTypeDecl() {
+ p.expectKeyword("type")
+ pkg, name := p.parseExportedName()
+ obj := declTypeName(pkg, name)
+
+ // The type object may have been imported before and thus already
+ // have a type associated with it. We still need to parse the type
+ // structure, but throw it away if the object already has a type.
+ // This ensures that all imports refer to the same type object for
+ // a given type declaration.
+ typ := p.parseType(pkg)
+
+ if name := obj.Type().(*types.Named); name.Underlying() == nil {
+ name.SetUnderlying(typ)
+ }
+}
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *parser) parseVarDecl() {
+ p.expectKeyword("var")
+ pkg, name := p.parseExportedName()
+ typ := p.parseType(pkg)
+ pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
+}
+
+// Func = Signature [ Body ] .
+// Body = "{" ... "}" .
+//
+func (p *parser) parseFunc(recv *types.Var) *types.Signature {
+ sig := p.parseSignature(recv)
+ if p.tok == '{' {
+ p.next()
+ for i := 1; i > 0; p.next() {
+ switch p.tok {
+ case '{':
+ i++
+ case '}':
+ i--
+ }
+ }
+ }
+ return sig
+}
+
+// MethodDecl = "func" Receiver Name Func .
+// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *parser) parseMethodDecl() {
+ // "func" already consumed
+ p.expect('(')
+ recv, _ := p.parseParameter() // receiver
+ p.expect(')')
+
+ // determine receiver base type object
+ base := deref(recv.Type()).(*types.Named)
+
+ // parse method name, signature, and possibly inlined body
+ _, name := p.parseName(nil, false)
+ sig := p.parseFunc(recv)
+
+ // methods always belong to the same package as the base type object
+ pkg := base.Obj().Pkg()
+
+ // add method to type unless type was imported before
+ // and method exists already
+ // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
+ base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+}
+
+// FuncDecl = "func" ExportedName Func .
+//
+func (p *parser) parseFuncDecl() {
+ // "func" already consumed
+ pkg, name := p.parseExportedName()
+ typ := p.parseFunc(nil)
+ pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
+}
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *parser) parseDecl() {
+ if p.tok == scanner.Ident {
+ switch p.lit {
+ case "import":
+ p.parseImportDecl()
+ case "const":
+ p.parseConstDecl()
+ case "type":
+ p.parseTypeDecl()
+ case "var":
+ p.parseVarDecl()
+ case "func":
+ p.next() // look ahead
+ if p.tok == '(' {
+ p.parseMethodDecl()
+ } else {
+ p.parseFuncDecl()
+ }
+ }
+ }
+ p.expect('\n')
+}
+
+// ----------------------------------------------------------------------------
+// Export
+
+// Export = "PackageClause { Decl } "$$" .
+// PackageClause = "package" PackageName [ "safe" ] "\n" .
+//
+func (p *parser) parseExport() *types.Package {
+ p.expectKeyword("package")
+ name := p.parsePackageName()
+ if p.tok == scanner.Ident && p.lit == "safe" {
+ // package was compiled with -u option - ignore
+ p.next()
+ }
+ p.expect('\n')
+
+ pkg := p.getPkg(p.id, name)
+
+ for p.tok != '$' && p.tok != scanner.EOF {
+ p.parseDecl()
+ }
+
+ if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
+ // don't call next()/expect() since reading past the
+ // export data may cause scanner errors (e.g. NUL chars)
+ p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
+ }
+
+ if n := p.scanner.ErrorCount; n != 0 {
+ p.errorf("expected no scanner errors, got %d", n)
+ }
+
+ // Record all locally referenced packages as imports.
+ var imports []*types.Package
+ for id, pkg2 := range p.localPkgs {
+ if pkg2.Name() == "" {
+ p.errorf("%s package has no name", id)
+ }
+ if id == p.id {
+ continue // avoid self-edge
+ }
+ imports = append(imports, pkg2)
+ }
+ sort.Sort(byPath(imports))
+ pkg.SetImports(imports)
+
+ // package was imported completely and without errors
+ pkg.MarkComplete()
+
+ return pkg
+}
+
+type byPath []*types.Package
+
+func (a byPath) Len() int { return len(a) }
+func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/gcimporter16_test.go b/go/src/golang.org/x/tools/go/gcimporter15/gcimporter16_test.go
new file mode 100644
index 0000000..ac94d99
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/gcimporter16_test.go
@@ -0,0 +1,55 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.6
+
+package gcimporter
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+// TODO(gri) Remove this function once we switched to new export format by default.
+func compileNewExport(t *testing.T, dirname, filename string) string {
+ /* testenv. */ MustHaveGoBuild(t)
+ cmd := exec.Command("go", "tool", "compile", "-newexport", filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("go tool compile %s failed: %s", filename, err)
+ }
+ // filename should end with ".go"
+ return filepath.Join(dirname, filename[:len(filename)-2]+"o")
+}
+
+// TODO(gri) Remove this function once we switched to new export format by default
+// (and update the comment and want list in TestImportTestdata).
+func TestImportTestdataNewExport(t *testing.T) {
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ if outFn := compileNewExport(t, "testdata", "exports.go"); outFn != "" {
+ defer os.Remove(outFn)
+ }
+
+ if pkg := testPath(t, "./testdata/exports", "."); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by exports.go, plus all packages
+ // referenced indirectly via exported objects in exports.go.
+ want := `[package ast ("go/ast") package token ("go/token")]`
+ got := fmt.Sprint(pkg.Imports())
+ if got != want {
+ t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want)
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/gcimporter_test.go b/go/src/golang.org/x/tools/go/gcimporter15/gcimporter_test.go
new file mode 100644
index 0000000..873ef46
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/gcimporter_test.go
@@ -0,0 +1,365 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/gcimporter_test.go, tagged for go1.5,
+// and minimally adjusted to make it build with code from (std lib) internal/testenv copied.
+
+package gcimporter
+
+import (
+ "fmt"
+ "go/types"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+// ----------------------------------------------------------------------------
+// The following three functions (Builder, HasGoBuild, MustHaveGoBuild) were
+// copied from $GOROOT/src/internal/testenv since that package is not available
+// in x/tools.
+
+// Builder reports the name of the builder running this test
+// (for example, "linux-amd64" or "windows-386-gce").
+// If the test is not running on the build infrastructure,
+// Builder returns the empty string.
+func Builder() string {
+ return os.Getenv("GO_BUILDER_NAME")
+}
+
+// HasGoBuild reports whether the current system can build programs with ``go build''
+// and then run them with os.StartProcess or exec.Command.
+func HasGoBuild() bool {
+ switch runtime.GOOS {
+ case "android", "nacl":
+ return false
+ case "darwin":
+ if strings.HasPrefix(runtime.GOARCH, "arm") {
+ return false
+ }
+ }
+ return true
+}
+
+// MustHaveGoBuild checks that the current system can build programs with ``go build''
+// and then run them with os.StartProcess or exec.Command.
+// If not, MustHaveGoBuild calls t.Skip with an explanation.
+func MustHaveGoBuild(t *testing.T) {
+ if !HasGoBuild() {
+ t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+// skipSpecialPlatforms causes the test to be skipped for platforms where
+// builders (build.golang.org) don't have access to compiled packages for
+// import.
+func skipSpecialPlatforms(t *testing.T) {
+ switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
+ case "nacl-amd64p32",
+ "nacl-386",
+ "nacl-arm",
+ "darwin-arm",
+ "darwin-arm64":
+ t.Skipf("no compiled packages available for import on %s", platform)
+ }
+}
+
+func compile(t *testing.T, dirname, filename string) string {
+ /* testenv. */ MustHaveGoBuild(t)
+ cmd := exec.Command("go", "tool", "compile", filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("go tool compile %s failed: %s", filename, err)
+ }
+ // filename should end with ".go"
+ return filepath.Join(dirname, filename[:len(filename)-2]+"o")
+}
+
+func testPath(t *testing.T, path, srcDir string) *types.Package {
+ t0 := time.Now()
+ pkg, err := Import(make(map[string]*types.Package), path, srcDir)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return nil
+ }
+ t.Logf("testPath(%s): %v", path, time.Since(t0))
+ return pkg
+}
+
+const maxTime = 30 * time.Second
+
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
+ dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
+ list, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case !f.IsDir():
+ // try extensions
+ for _, ext := range pkgExts {
+ if strings.HasSuffix(f.Name(), ext) {
+ name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
+ if testPath(t, filepath.Join(dir, name), dir) != nil {
+ nimports++
+ }
+ }
+ }
+ case f.IsDir():
+ nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
+ }
+ }
+ return
+}
+
+func TestImportTestdata(t *testing.T) {
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ if outFn := compile(t, "testdata", "exports.go"); outFn != "" {
+ defer os.Remove(outFn)
+ }
+
+ if pkg := testPath(t, "./testdata/exports", "."); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by exports.go, plus all packages
+ // referenced indirectly via exported objects in exports.go.
+ // With the textual export format, the list may also include
+ // additional packages that are not strictly required for
+ // import processing alone (they are exported to err "on
+ // the safe side").
+ got := fmt.Sprint(pkg.Imports())
+ for _, want := range []string{"go/ast", "go/token"} {
+ if !strings.Contains(got, want) {
+ t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ }
+ }
+ }
+}
+
+func TestImportStdLib(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ dt := maxTime
+ if testing.Short() && /* testenv. */ Builder() == "" {
+ dt = 10 * time.Millisecond
+ }
+ nimports := testDir(t, "", time.Now().Add(dt)) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ want string
+}{
+ {"math.Pi", "const Pi untyped float"},
+ {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
+ {"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
+ {"math.Sin", "func Sin(x float64) float64"},
+ // TODO(gri) add more tests
+}
+
+func TestImportedTypes(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ for _, test := range importedObjectTests {
+ s := strings.Split(test.name, ".")
+ if len(s) != 2 {
+ t.Fatal("inconsistent test data")
+ }
+ importPath := s[0]
+ objName := s[1]
+
+ pkg, err := Import(make(map[string]*types.Package), importPath, ".")
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup(objName)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ continue
+ }
+
+ got := types.ObjectString(obj, types.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+ }
+}
+
+func TestIssue5815(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ pkg, err := Import(make(map[string]*types.Package), "strings", ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Pkg() == nil {
+ t.Errorf("no pkg for %s", obj)
+ }
+ if tname, _ := obj.(*types.TypeName); tname != nil {
+ named := tname.Type().(*types.Named)
+ for i := 0; i < named.NumMethods(); i++ {
+ m := named.Method(i)
+ if m.Pkg() == nil {
+ t.Errorf("no pkg for %s", m)
+ }
+ }
+ }
+ }
+}
+
+// Smoke test to ensure that imported methods get the correct package.
+func TestCorrectMethodPackage(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ imports := make(map[string]*types.Package)
+ _, err := Import(imports, "net/http", ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
+ mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
+ sel := mset.Lookup(nil, "Lock")
+ lock := sel.Obj().(*types.Func)
+ if got, want := lock.Pkg().Path(), "sync"; got != want {
+ t.Errorf("got package path %q; want %q", got, want)
+ }
+}
+
+func TestIssue13566(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ if f := compile(t, "testdata", "a.go"); f != "" {
+ defer os.Remove(f)
+ }
+ if f := compile(t, "testdata", "b.go"); f != "" {
+ defer os.Remove(f)
+ }
+
+ // import must succeed (test for issue at hand)
+ pkg, err := Import(make(map[string]*types.Package), "./testdata/b", ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // make sure all indirectly imported packages have names
+ for _, imp := range pkg.Imports() {
+ if imp.Name() == "" {
+ t.Errorf("no name for %s package", imp.Path())
+ }
+ }
+}
+
+func TestIssue13898(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ // import go/internal/gcimporter which imports go/types partially
+ imports := make(map[string]*types.Package)
+ _, err := Import(imports, "go/internal/gcimporter", ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // look for go/types package
+ var goTypesPkg *types.Package
+ for path, pkg := range imports {
+ if path == "go/types" {
+ goTypesPkg = pkg
+ break
+ }
+ }
+ if goTypesPkg == nil {
+ t.Fatal("go/types not found")
+ }
+
+ // look for go/types.Object type
+ obj := goTypesPkg.Scope().Lookup("Object")
+ if obj == nil {
+ t.Fatal("go/types.Object not found")
+ }
+ typ, ok := obj.Type().(*types.Named)
+ if !ok {
+ t.Fatalf("go/types.Object type is %v; wanted named type", typ)
+ }
+
+ // lookup go/types.Object.Pkg method
+ m, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
+ if m == nil {
+ t.Fatal("go/types.Object.Pkg not found")
+ }
+
+ // the method must belong to go/types
+ if m.Pkg().Path() != "go/types" {
+ t.Fatalf("found %v; want go/types", m.Pkg())
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/setname15.go b/go/src/golang.org/x/tools/go/gcimporter15/setname15.go
new file mode 100644
index 0000000..8f78f54
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/setname15.go
@@ -0,0 +1,31 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5,!go1.6
+
+package gcimporter
+
+import (
+ "go/types"
+ "unsafe"
+)
+
+func setName(pkg *types.Package, name string) {
+ (*types_Package)(unsafe.Pointer(pkg)).name = name
+}
+
+// The underlying type of types_Package is identical to
+// the underlying type of types.Package. We use it with
+// package unsafe to set the name field since 1.5 does
+// not have the Package.SetName method.
+// TestSetName verifies that the layout with respect to
+// the name field is correct.
+type types_Package struct {
+ path string
+ name string
+ scope *types.Scope
+ complete bool
+ imports []*types.Package
+ fake bool
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/setname16.go b/go/src/golang.org/x/tools/go/gcimporter15/setname16.go
new file mode 100644
index 0000000..d318e7c
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/setname16.go
@@ -0,0 +1,13 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.6
+
+package gcimporter
+
+import "go/types"
+
+func setName(pkg *types.Package, name string) {
+ pkg.SetName(name)
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/setname_test.go b/go/src/golang.org/x/tools/go/gcimporter15/setname_test.go
new file mode 100644
index 0000000..a8a33f7
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/setname_test.go
@@ -0,0 +1,28 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
+package gcimporter
+
+import (
+ "go/types"
+ "testing"
+)
+
+func TestSetName(t *testing.T) {
+ pkg := types.NewPackage("path", "foo")
+ scope := pkg.Scope()
+
+ // verify setName
+ setName(pkg, "bar")
+ if name := pkg.Name(); name != "bar" {
+ t.Fatalf(`got package name %q; want "bar"`, name)
+ }
+
+ // verify no other fields are changed
+ if pkg.Path() != "path" || pkg.Scope() != scope || pkg.Complete() || pkg.Imports() != nil {
+ t.Fatalf("setName changed other fields")
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/testdata/a.go b/go/src/golang.org/x/tools/go/gcimporter15/testdata/a.go
new file mode 100644
index 0000000..56e4292
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/testdata/a.go
@@ -0,0 +1,14 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Input for TestIssue13566
+
+package a
+
+import "encoding/json"
+
+type A struct {
+ a *A
+ json json.RawMessage
+}
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/testdata/b.go b/go/src/golang.org/x/tools/go/gcimporter15/testdata/b.go
new file mode 100644
index 0000000..4196678
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/testdata/b.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Input for TestIssue13566
+
+package b
+
+import "./a"
+
+type A a.A
diff --git a/go/src/golang.org/x/tools/go/gcimporter15/testdata/exports.go b/go/src/golang.org/x/tools/go/gcimporter15/testdata/exports.go
new file mode 100644
index 0000000..8ee28b0
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/gcimporter15/testdata/exports.go
@@ -0,0 +1,89 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import (
+ "go/ast"
+)
+
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
+const (
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456E+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+)
+
+type (
+ T1 int
+ T2 [10]int
+ T3 []int
+ T4 *int
+ T5 chan int
+ T6a chan<- int
+ T6b chan (<-chan int)
+ T6c chan<- (chan int)
+ T7 <-chan *ast.File
+ T8 struct{}
+ T9 struct {
+ a int
+ b, c float32
+ d []string `go:"tag"`
+ }
+ T10 struct {
+ T8
+ T9
+ _ *T10
+ }
+ T11 map[int]string
+ T12 interface{}
+ T13 interface {
+ m1()
+ m2(int) float32
+ }
+ T14 interface {
+ T12
+ T13
+ m3(x ...struct{}) []T9
+ }
+ T15 func()
+ T16 func(int)
+ T17 func(x int)
+ T18 func() float32
+ T19 func() (x float32)
+ T20 func(...interface{})
+ T21 struct{ next *T21 }
+ T22 struct{ link *T23 }
+ T23 struct{ link *T22 }
+ T24 *T24
+ T25 *T26
+ T26 *T27
+ T27 *T25
+ T28 func(T28) T28
+)
+
+var (
+ V0 int
+ V1 = -991.0
+)
+
+func F1() {}
+func F2(x int) {}
+func F3() int { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+
+func (p *T1) M1()
diff --git a/go/src/golang.org/x/tools/go/importer/import_test.go b/go/src/golang.org/x/tools/go/importer/import_test.go
index 3a2560c..95c373d 100644
--- a/go/src/golang.org/x/tools/go/importer/import_test.go
+++ b/go/src/golang.org/x/tools/go/importer/import_test.go
@@ -119,6 +119,11 @@
if testing.Short() && time.Since(start) >= 750*time.Millisecond {
return
}
+ if lib == "cmd/internal/objfile" || lib == "net/http" {
+ // gcimporter doesn't support vendored imports.
+ // TODO(gri): fix.
+ continue
+ }
pkg, err := pkgForPath(lib)
switch err := err.(type) {
diff --git a/go/src/golang.org/x/tools/go/loader/cgo.go b/go/src/golang.org/x/tools/go/loader/cgo.go
index fb39e53..f698197 100644
--- a/go/src/golang.org/x/tools/go/loader/cgo.go
+++ b/go/src/golang.org/x/tools/go/loader/cgo.go
@@ -1,3 +1,9 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
package loader
// This file handles cgo preprocessing of files containing `import "C"`.
diff --git a/go/src/golang.org/x/tools/go/loader/cgo14.go b/go/src/golang.org/x/tools/go/loader/cgo14.go
new file mode 100644
index 0000000..1dac420
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/cgo14.go
@@ -0,0 +1,205 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package loader
+
+// This file handles cgo preprocessing of files containing `import "C"`.
+//
+// DESIGN
+//
+// The approach taken is to run the cgo processor on the package's
+// CgoFiles and parse the output, faking the filenames of the
+// resulting ASTs so that the synthetic file containing the C types is
+// called "C" (e.g. "~/go/src/net/C") and the preprocessed files
+// have their original names (e.g. "~/go/src/net/cgo_unix.go"),
+// not the names of the actual temporary files.
+//
+// The advantage of this approach is its fidelity to 'go build'. The
+// downside is that the token.Position.Offset for each AST node is
+// incorrect, being an offset within the temporary file. Line numbers
+// should still be correct because of the //line comments.
+//
+// The logic of this file is mostly plundered from the 'go build'
+// tool, which also invokes the cgo preprocessor.
+//
+//
+// REJECTED ALTERNATIVE
+//
+// An alternative approach that we explored is to extend go/types'
+// Importer mechanism to provide the identity of the importing package
+// so that each time `import "C"` appears it resolves to a different
+// synthetic package containing just the objects needed in that case.
+// The loader would invoke cgo but parse only the cgo_types.go file
+// defining the package-level objects, discarding the other files
+// resulting from preprocessing.
+//
+// The benefit of this approach would have been that source-level
+// syntax information would correspond exactly to the original cgo
+// file, with no preprocessing involved, making source tools like
+// godoc, oracle, and eg happy. However, the approach was rejected
+// due to the additional complexity it would impose on go/types. (It
+// made for a beautiful demo, though.)
+//
+// cgo files, despite their *.go extension, are not legal Go source
+// files per the specification since they may refer to unexported
+// members of package "C" such as C.int. Also, a function such as
+// C.getpwent has in effect two types, one matching its C type and one
+// which additionally returns (errno C.int). The cgo preprocessor
+// uses name mangling to distinguish these two functions in the
+// processed code, but go/types would need to duplicate this logic in
+// its handling of function calls, analogous to the treatment of map
+// lookups in which y=m[k] and y,ok=m[k] are both legal.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+// processCgoFiles invokes the cgo preprocessor on bp.CgoFiles, parses
+// the output and returns the resulting ASTs.
+//
+func processCgoFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) {
+ tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C")
+ if err != nil {
+ return nil, err
+ }
+ defer os.RemoveAll(tmpdir)
+
+ pkgdir := bp.Dir
+ if DisplayPath != nil {
+ pkgdir = DisplayPath(pkgdir)
+ }
+
+ cgoFiles, cgoDisplayFiles, err := runCgo(bp, pkgdir, tmpdir)
+ if err != nil {
+ return nil, err
+ }
+ var files []*ast.File
+ for i := range cgoFiles {
+ rd, err := os.Open(cgoFiles[i])
+ if err != nil {
+ return nil, err
+ }
+ display := filepath.Join(bp.Dir, cgoDisplayFiles[i])
+ f, err := parser.ParseFile(fset, display, rd, mode)
+ rd.Close()
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, f)
+ }
+ return files, nil
+}
+
+var cgoRe = regexp.MustCompile(`[/\\:]`)
+
+// runCgo invokes the cgo preprocessor on bp.CgoFiles and returns two
+// lists of files: the resulting processed files (in temporary
+// directory tmpdir) and the corresponding names of the unprocessed files.
+//
+// runCgo is adapted from (*builder).cgo in
+// $GOROOT/src/cmd/go/build.go, but these features are unsupported:
+// pkg-config, Objective C, CGOPKGPATH, CGO_FLAGS.
+//
+func runCgo(bp *build.Package, pkgdir, tmpdir string) (files, displayFiles []string, err error) {
+ cgoCPPFLAGS, _, _, _ := cflags(bp, true)
+ _, cgoexeCFLAGS, _, _ := cflags(bp, false)
+
+ if len(bp.CgoPkgConfig) > 0 {
+ return nil, nil, fmt.Errorf("cgo pkg-config not supported")
+ }
+
+ // Allows including _cgo_export.h from .[ch] files in the package.
+ cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir)
+
+ // _cgo_gotypes.go (displayed "C") contains the type definitions.
+ files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go"))
+ displayFiles = append(displayFiles, "C")
+ for _, fn := range bp.CgoFiles {
+ // "foo.cgo1.go" (displayed "foo.go") is the processed Go source.
+ f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_")
+ files = append(files, filepath.Join(tmpdir, f+"cgo1.go"))
+ displayFiles = append(displayFiles, fn)
+ }
+
+ var cgoflags []string
+ if bp.Goroot && bp.ImportPath == "runtime/cgo" {
+ cgoflags = append(cgoflags, "-import_runtime_cgo=false")
+ }
+ if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" {
+ cgoflags = append(cgoflags, "-import_syscall=false")
+ }
+
+ args := stringList(
+ "go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--",
+ cgoCPPFLAGS, cgoexeCFLAGS, bp.CgoFiles,
+ )
+ if false {
+ log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir)
+ }
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = pkgdir
+ cmd.Stdout = os.Stderr
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err)
+ }
+
+ return files, displayFiles, nil
+}
+
+// -- unmodified from 'go build' ---------------------------------------
+
+// Return the flags to use when invoking the C or C++ compilers, or cgo.
+func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
+ var defaults string
+ if def {
+ defaults = "-g -O2"
+ }
+
+ cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
+ cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
+ cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
+ ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
+ return
+}
+
+// envList returns the value of the given environment variable broken
+// into fields, using the default value when the variable is empty.
+func envList(key, def string) []string {
+ v := os.Getenv(key)
+ if v == "" {
+ v = def
+ }
+ return strings.Fields(v)
+}
+
+// stringList's arguments should be a sequence of string or []string values.
+// stringList flattens them into a single []string.
+func stringList(args ...interface{}) []string {
+ var x []string
+ for _, arg := range args {
+ switch arg := arg.(type) {
+ case []string:
+ x = append(x, arg...)
+ case string:
+ x = append(x, arg)
+ default:
+ panic("stringList: invalid argument")
+ }
+ }
+ return x
+}
diff --git a/go/src/golang.org/x/tools/go/loader/doc.go b/go/src/golang.org/x/tools/go/loader/doc.go
index 1ff4b15..600cd1a 100644
--- a/go/src/golang.org/x/tools/go/loader/doc.go
+++ b/go/src/golang.org/x/tools/go/loader/doc.go
@@ -50,6 +50,11 @@
//
// CONCEPTS AND TERMINOLOGY
//
+// The WORKSPACE is the set of packages accessible to the loader. The
+// workspace is defined by Config.Build, a *build.Context. The
+// default context treats subdirectories of $GOROOT and $GOPATH as
+// packages, but this behavior may be overridden.
+//
// An AD HOC package is one specified as a set of source files on the
// command line. In the simplest case, it may consist of a single file
// such as $GOROOT/src/net/http/triv.go.
@@ -59,14 +64,25 @@
// same directory. (go/build.Package calls these files XTestFiles.)
//
// An IMPORTABLE package is one that can be referred to by some import
-// spec. The Path() of each importable package is unique within a
-// Program.
+// spec. Every importable package is uniquely identified by its
+// PACKAGE PATH or just PATH, a string such as "fmt", "encoding/json",
+// or "cmd/vendor/golang.org/x/arch/x86/x86asm". A package path
+// typically denotes a subdirectory of the workspace.
+//
+// An import declaration uses an IMPORT PATH to refer to a package.
+// Most import declarations use the package path as the import path.
+//
+// Due to VENDORING (https://golang.org/s/go15vendor), the
+// interpretation of an import path may depend on the directory in which
+// it appears. To resolve an import path to a package path, go/build
+// must search the enclosing directories for a subdirectory named
+// "vendor".
//
// ad hoc packages and external test packages are NON-IMPORTABLE. The
-// Path() of an ad hoc package is inferred from the package
+// path of an ad hoc package is inferred from the package
// declarations of its files and is therefore not a unique package key.
// For example, Config.CreatePkgs may specify two initial ad hoc
-// packages both called "main".
+// packages, both with path "main".
//
// An AUGMENTED package is an importable package P plus all the
// *_test.go files with same 'package foo' declaration as P.
@@ -125,7 +141,7 @@
// Let us define the import dependency graph as follows. Each node is a
// list of files passed to (Checker).Files at once. Many of these lists
// are the production code of an importable Go package, so those nodes
-// are labelled by the package's import path. The remaining nodes are
+// are labelled by the package's path. The remaining nodes are
// ad hoc packages and lists of in-package *_test.go files that augment
// an importable package; those nodes have no label.
//
diff --git a/go/src/golang.org/x/tools/go/loader/example14_test.go b/go/src/golang.org/x/tools/go/loader/example14_test.go
index 76d5ea6..67dbefa 100644
--- a/go/src/golang.org/x/tools/go/loader/example14_test.go
+++ b/go/src/golang.org/x/tools/go/loader/example14_test.go
@@ -83,7 +83,7 @@
// created: []
// imported: [errors runtime unicode/utf8]
// initial: [errors runtime unicode/utf8]
- // all: [errors runtime unicode/utf8]
+ // all: [errors runtime unicode/utf8 unsafe]
}
// This example creates and type-checks a single package (without tests)
@@ -139,7 +139,7 @@
// created: [hello]
// imported: []
// initial: [hello]
- // all: [errors fmt hello io math os reflect runtime strconv sync sync/atomic syscall time unicode/utf8]
+ // all: [errors fmt hello io math os reflect runtime strconv sync sync/atomic syscall time unicode/utf8 unsafe]
// strconv.Files: [atob.go atof.go atoi.go decimal.go extfloat.go ftoa.go isprint.go itoa.go quote.go]
}
@@ -167,7 +167,7 @@
// created: [strconv_test]
// imported: [errors strconv unicode/utf8]
// initial: [errors strconv strconv_test unicode/utf8]
- // all: [bufio bytes errors flag fmt io math math/rand os reflect runtime runtime/pprof sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8]
+ // all: [bufio bytes errors flag fmt io math math/rand os reflect runtime runtime/pprof sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8 unsafe]
// strconv.Files: [atob.go atof.go atoi.go decimal.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go]
// strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go fp_test.go ftoa_test.go itoa_test.go quote_example_test.go quote_test.go strconv_test.go]
}
diff --git a/go/src/golang.org/x/tools/go/loader/example15_test.go b/go/src/golang.org/x/tools/go/loader/example15_test.go
new file mode 100644
index 0000000..5cf365b
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/example15_test.go
@@ -0,0 +1,177 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5,!go1.6
+// +build !windows
+
+package loader_test
+
+import (
+ "fmt"
+ "go/token"
+ "log"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/loader"
+)
+
+func printProgram(prog *loader.Program) {
+ // Created packages are the initial packages specified by a call
+ // to CreateFromFilenames or CreateFromFiles.
+ var names []string
+ for _, info := range prog.Created {
+ names = append(names, info.Pkg.Path())
+ }
+ fmt.Printf("created: %s\n", names)
+
+ // Imported packages are the initial packages specified by a
+ // call to Import or ImportWithTests.
+ names = nil
+ for _, info := range prog.Imported {
+ if strings.Contains(info.Pkg.Path(), "internal") {
+ continue // skip, to reduce fragility
+ }
+ names = append(names, info.Pkg.Path())
+ }
+ sort.Strings(names)
+ fmt.Printf("imported: %s\n", names)
+
+ // InitialPackages contains the union of created and imported.
+ names = nil
+ for _, info := range prog.InitialPackages() {
+ names = append(names, info.Pkg.Path())
+ }
+ sort.Strings(names)
+ fmt.Printf("initial: %s\n", names)
+
+ // AllPackages contains all initial packages and their dependencies.
+ names = nil
+ for pkg := range prog.AllPackages {
+ names = append(names, pkg.Path())
+ }
+ sort.Strings(names)
+ fmt.Printf("all: %s\n", names)
+}
+
+func printFilenames(fset *token.FileSet, info *loader.PackageInfo) {
+ var names []string
+ for _, f := range info.Files {
+ names = append(names, filepath.Base(fset.File(f.Pos()).Name()))
+ }
+ fmt.Printf("%s.Files: %s\n", info.Pkg.Path(), names)
+}
+
+// This example loads a set of packages and all of their dependencies
+// from a typical command-line. FromArgs parses a command line and
+// makes calls to the other methods of Config shown in the examples that
+// follow.
+func ExampleConfig_FromArgs() {
+ args := []string{"mytool", "unicode/utf8", "errors", "runtime", "--", "foo", "bar"}
+ const wantTests = false
+
+ var conf loader.Config
+ rest, err := conf.FromArgs(args[1:], wantTests)
+ prog, err := conf.Load()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Printf("rest: %s\n", rest)
+ printProgram(prog)
+ // Output:
+ // rest: [foo bar]
+ // created: []
+ // imported: [errors runtime unicode/utf8]
+ // initial: [errors runtime unicode/utf8]
+ // all: [errors runtime unicode/utf8 unsafe]
+}
+
+// This example creates and type-checks a single package (without tests)
+// from a list of filenames, and loads all of its dependencies.
+func ExampleConfig_CreateFromFilenames() {
+ var conf loader.Config
+ filename := filepath.Join(runtime.GOROOT(), "src/container/heap/heap.go")
+ conf.CreateFromFilenames("container/heap", filename)
+ prog, err := conf.Load()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ printProgram(prog)
+ // Output:
+ // created: [container/heap]
+ // imported: []
+ // initial: [container/heap]
+ // all: [container/heap sort]
+}
+
+// In the examples below, for stability, the chosen packages are
+// relatively small, platform-independent, and low-level (and thus
+// infrequently changing).
+// The strconv package has internal and external tests.
+
+const hello = `package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello, world.")
+}
+`
+
+// This example creates and type-checks a package from a list of
+// already-parsed files, and loads all its dependencies.
+func ExampleConfig_CreateFromFiles() {
+ var conf loader.Config
+ f, err := conf.ParseFile("hello.go", hello)
+ if err != nil {
+ log.Fatal(err)
+ }
+ conf.CreateFromFiles("hello", f)
+ prog, err := conf.Load()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ printProgram(prog)
+ printFilenames(prog.Fset, prog.Package("strconv"))
+ // Output:
+ // created: [hello]
+ // imported: []
+ // initial: [hello]
+ // all: [errors fmt hello io math os reflect runtime strconv sync sync/atomic syscall time unicode/utf8 unsafe]
+ // strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go]
+}
+
+// This example imports three packages, including the tests for one of
+// them, and loads all their dependencies.
+func ExampleConfig_Import() {
+ // ImportWithTest("strconv") causes strconv to include
+ // internal_test.go, and creates an external test package,
+ // strconv_test.
+ // (Compare with the example of CreateFromFiles.)
+
+ var conf loader.Config
+ conf.Import("unicode/utf8")
+ conf.Import("errors")
+ conf.ImportWithTests("strconv")
+ prog, err := conf.Load()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ printProgram(prog)
+ printFilenames(prog.Fset, prog.Package("strconv"))
+ printFilenames(prog.Fset, prog.Package("strconv_test"))
+ // Output:
+ // created: [strconv_test]
+ // imported: [errors strconv unicode/utf8]
+ // initial: [errors strconv strconv_test unicode/utf8]
+ // all: [bufio bytes errors flag fmt io log math math/rand os reflect runtime runtime/pprof runtime/trace sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8 unsafe]
+ // strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go]
+ // strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go example_test.go fp_test.go ftoa_test.go itoa_test.go quote_test.go strconv_test.go]
+}
diff --git a/go/src/golang.org/x/tools/go/loader/example_test.go b/go/src/golang.org/x/tools/go/loader/example_test.go
index ce5afdb..66ddb92 100644
--- a/go/src/golang.org/x/tools/go/loader/example_test.go
+++ b/go/src/golang.org/x/tools/go/loader/example_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build go1.5
+// +build go1.6
// +build !windows
package loader_test
@@ -14,6 +14,7 @@
"path/filepath"
"runtime"
"sort"
+ "strings"
"golang.org/x/tools/go/loader"
)
@@ -31,6 +32,9 @@
// call to Import or ImportWithTests.
names = nil
for _, info := range prog.Imported {
+ if strings.Contains(info.Pkg.Path(), "internal") {
+ continue // skip, to reduce fragility
+ }
names = append(names, info.Pkg.Path())
}
sort.Strings(names)
@@ -83,7 +87,7 @@
// created: []
// imported: [errors runtime unicode/utf8]
// initial: [errors runtime unicode/utf8]
- // all: [errors runtime unicode/utf8]
+ // all: [errors runtime runtime/internal/atomic runtime/internal/sys unicode/utf8 unsafe]
}
// This example creates and type-checks a single package (without tests)
@@ -139,7 +143,7 @@
// created: [hello]
// imported: []
// initial: [hello]
- // all: [errors fmt hello io math os reflect runtime strconv sync sync/atomic syscall time unicode/utf8]
+ // all: [errors fmt hello internal/race io math os reflect runtime runtime/internal/atomic runtime/internal/sys strconv sync sync/atomic syscall time unicode/utf8 unsafe]
// strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go]
}
@@ -167,7 +171,7 @@
// created: [strconv_test]
// imported: [errors strconv unicode/utf8]
// initial: [errors strconv strconv_test unicode/utf8]
- // all: [bufio bytes errors flag fmt io log math math/rand os reflect runtime runtime/pprof runtime/trace sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8]
+ // all: [bufio bytes errors flag fmt internal/race io log math math/rand os reflect runtime runtime/debug runtime/internal/atomic runtime/internal/sys runtime/pprof runtime/trace sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8 unsafe]
// strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go]
// strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go example_test.go fp_test.go ftoa_test.go itoa_test.go quote_test.go strconv_test.go]
}
diff --git a/go/src/golang.org/x/tools/go/loader/go16.go b/go/src/golang.org/x/tools/go/loader/go16.go
new file mode 100644
index 0000000..c0ed50f
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/go16.go
@@ -0,0 +1,13 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.6
+
+package loader
+
+import "go/build"
+
+func init() {
+ ignoreVendor = build.IgnoreVendor
+}
diff --git a/go/src/golang.org/x/tools/go/loader/go16_test.go b/go/src/golang.org/x/tools/go/loader/go16_test.go
new file mode 100644
index 0000000..71f89b2
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/go16_test.go
@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.6
+// +build !android
+
+package loader_test
+
+func init() {
+ go16 = true
+}
diff --git a/go/src/golang.org/x/tools/go/loader/loader.go b/go/src/golang.org/x/tools/go/loader/loader.go
index 1d3ead0..b0f8336 100644
--- a/go/src/golang.org/x/tools/go/loader/loader.go
+++ b/go/src/golang.org/x/tools/go/loader/loader.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package loader
// See doc.go for package documentation and implementation notes.
@@ -13,6 +15,7 @@
"go/build"
"go/parser"
"go/token"
+ "go/types"
"os"
"sort"
"strings"
@@ -20,9 +23,10 @@
"time"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
)
+var ignoreVendor build.ImportMode
+
const trace = false // show timing info for type-checking
// Config specifies the configuration for loading a whole program from
@@ -45,13 +49,13 @@
// The supplied Import function is not used either.
TypeChecker types.Config
- // TypeCheckFuncBodies is a predicate over package import
- // paths. A package for which the predicate is false will
+ // TypeCheckFuncBodies is a predicate over package paths.
+ // A package for which the predicate is false will
// have its package-level declarations type checked, but not
// its function bodies; this can be used to quickly load
// dependencies from source. If nil, all func bodies are type
// checked.
- TypeCheckFuncBodies func(string) bool
+ TypeCheckFuncBodies func(path string) bool
// If Build is non-nil, it is used to locate source packages.
// Otherwise &build.Default is used.
@@ -84,9 +88,8 @@
// the corresponding elements of the Program.Created slice.
CreatePkgs []PkgSpec
- // ImportPkgs specifies a set of initial packages to load from
- // source. The map keys are package import paths, used to
- // locate the package relative to $GOROOT.
+ // ImportPkgs specifies a set of initial packages to load.
+ // The map keys are package paths.
//
// The map value indicates whether to load tests. If true, Load
// will add and type-check two lists of files to the package:
@@ -96,13 +99,14 @@
ImportPkgs map[string]bool
// FindPackage is called during Load to create the build.Package
- // for a given import path. If nil, a default implementation
- // based on ctxt.Import is used. A client may use this hook to
- // adapt to a proprietary build system that does not follow the
- // "go build" layout conventions, for example.
+ // for a given import path from a given directory.
+ // If FindPackage is nil, (*build.Context).Import is used.
+ // A client may use this hook to adapt to a proprietary build
+ // system that does not follow the "go build" layout
+ // conventions, for example.
//
// It must be safe to call concurrently from multiple goroutines.
- FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error)
+ FindPackage func(ctxt *build.Context, fromDir, importPath string, mode build.ImportMode) (*build.Package, error)
}
// A PkgSpec specifies a non-importable package to be created by Load.
@@ -110,7 +114,7 @@
// Filenames is provided. The path needn't be globally unique.
//
type PkgSpec struct {
- Path string // import path ("" => use package declaration)
+ Path string // package path ("" => use package declaration)
Files []*ast.File // ASTs of already-parsed files
Filenames []string // names of files to be parsed
}
@@ -123,6 +127,15 @@
// filenames were supplied by Config.CreatePkgs[i], followed by
// the external test package, if any, of each package in
// Config.ImportPkgs ordered by ImportPath.
+ //
+ // NOTE: these files must not import "C". Cgo preprocessing is
+ // only performed on imported packages, not ad hoc packages.
+ //
+ // TODO(adonovan): we need to copy and adapt the logic of
+ // goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make
+ // Config.Import and Config.Create methods return the same kind
+ // of entity, essentially a build.Package.
+ // Perhaps we can even reuse that type directly.
Created []*PackageInfo
// Imported contains the initially imported packages,
@@ -134,7 +147,7 @@
// dependencies, including incomplete ones.
AllPackages map[*types.Package]*PackageInfo
- // importMap is the canonical mapping of import paths to
+ // importMap is the canonical mapping of package paths to
// packages. It contains all Imported initial packages, but not
// Created ones, and all imported dependencies.
importMap map[string]*types.Package
@@ -152,6 +165,7 @@
Files []*ast.File // syntax trees for the package's files
Errors []error // non-nil if the package had errors
types.Info // type-checker deductions.
+ dir string // package directory
checker *types.Checker // transient type-checker state
errorFunc func(error)
@@ -294,7 +308,7 @@
func (conf *Config) Import(path string) { conf.addImport(path, false) }
func (conf *Config) addImport(path string, tests bool) {
- if path == "C" || path == "unsafe" {
+ if path == "C" {
return // ignore; not a real package
}
if conf.ImportPkgs == nil {
@@ -366,6 +380,10 @@
progMu sync.Mutex // guards prog
prog *Program // the resulting program
+ // findpkg is a memoization of FindPackage.
+ findpkgMu sync.Mutex // guards findpkg
+ findpkg map[findpkgKey]*findpkgValue
+
importedMu sync.Mutex // guards imported
imported map[string]*importInfo // all imported packages (incl. failures) by import path
@@ -378,6 +396,18 @@
graph map[string]map[string]bool
}
+type findpkgKey struct {
+ importPath string
+ fromDir string
+ mode build.ImportMode
+}
+
+type findpkgValue struct {
+ ready chan struct{} // closed to broadcast readiness
+ bp *build.Package
+ err error
+}
+
// importInfo tracks the success or failure of a single import.
//
// Upon completion, exactly one of info and err is non-nil:
@@ -385,35 +415,30 @@
// A successful package may still contain type errors.
//
type importInfo struct {
- path string // import path
- mu sync.Mutex // guards the following fields prior to completion
- info *PackageInfo // results of typechecking (including errors)
- err error // reason for failure to create a package
- complete sync.Cond // complete condition is that one of info, err is non-nil.
+ path string // import path
+ info *PackageInfo // results of typechecking (including errors)
+ complete chan struct{} // closed to broadcast that info is set.
}
// awaitCompletion blocks until ii is complete,
-// i.e. the info and err fields are safe to inspect without a lock.
-// It is concurrency-safe and idempotent.
+// i.e. the info field is safe to inspect.
func (ii *importInfo) awaitCompletion() {
- ii.mu.Lock()
- for ii.info == nil && ii.err == nil {
- ii.complete.Wait()
- }
- ii.mu.Unlock()
+ <-ii.complete // wait for close
}
// Complete marks ii as complete.
// Its info and err fields will not be subsequently updated.
-func (ii *importInfo) Complete(info *PackageInfo, err error) {
- if info == nil && err == nil {
- panic("Complete(nil, nil)")
+func (ii *importInfo) Complete(info *PackageInfo) {
+ if info == nil {
+ panic("info == nil")
}
- ii.mu.Lock()
ii.info = info
- ii.err = err
- ii.complete.Broadcast()
- ii.mu.Unlock()
+ close(ii.complete)
+}
+
+type importError struct {
+ path string // import path
+ err error // reason for failure to create a package
}
// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
@@ -446,17 +471,7 @@
// Install default FindPackage hook using go/build logic.
if conf.FindPackage == nil {
- conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) {
- // TODO(adonovan): cache calls to build.Import
- // so we don't do it three times per test package.
- ioLimit <- true
- bp, err := ctxt.Import(path, conf.Cwd, 0)
- <-ioLimit
- if _, ok := err.(*build.NoGoError); ok {
- return bp, nil // empty directory is not an error
- }
- return bp, err
- }
+ conf.FindPackage = (*build.Context).Import
}
prog := &Program{
@@ -469,6 +484,7 @@
imp := importer{
conf: conf,
prog: prog,
+ findpkg: make(map[findpkgKey]*findpkgValue),
imported: make(map[string]*importInfo),
start: time.Now(),
graph: make(map[string]map[string]bool),
@@ -480,24 +496,26 @@
// Load the initially imported packages and their dependencies,
// in parallel.
- for _, ii := range imp.loadAll("", conf.ImportPkgs) {
- if ii.err != nil {
- conf.TypeChecker.Error(ii.err) // failed to create package
- errpkgs = append(errpkgs, ii.path)
- continue
- }
- prog.Imported[ii.info.Pkg.Path()] = ii.info
+ // No vendor check on packages imported from the command line.
+ infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, ignoreVendor)
+ for _, ie := range importErrors {
+ conf.TypeChecker.Error(ie.err) // failed to create package
+ errpkgs = append(errpkgs, ie.path)
+ }
+ for _, info := range infos {
+ prog.Imported[info.Pkg.Path()] = info
}
// Augment the designated initial packages by their tests.
// Dependencies are loaded in parallel.
var xtestPkgs []*build.Package
- for path, augment := range conf.ImportPkgs {
+ for importPath, augment := range conf.ImportPkgs {
if !augment {
continue
}
- bp, err := conf.FindPackage(conf.build(), path)
+ // No vendor check on packages imported from command line.
+ bp, err := imp.findPackage(importPath, conf.Cwd, ignoreVendor)
if err != nil {
// Package not found, or can't even parse package declaration.
// Already reported by previous loop; ignore it.
@@ -509,12 +527,14 @@
xtestPkgs = append(xtestPkgs, bp)
}
+ // Consult the cache using the canonical package path.
+ path := bp.ImportPath
imp.importedMu.Lock() // (unnecessary, we're sequential here)
ii, ok := imp.imported[path]
// Paranoid checks added due to issue #11012.
if !ok {
// Unreachable.
- // The previous loop called loadAll and thus
+ // The previous loop called importAll and thus
// startLoad for each path in ImportPkgs, which
// populates imp.imported[path] with a non-zero value.
panic(fmt.Sprintf("imported[%q] not found", path))
@@ -526,19 +546,10 @@
// that at least one of ii.err and ii.info is non-nil.
panic(fmt.Sprintf("imported[%q] == nil", path))
}
- if ii.err != nil {
- // The sole possible cause is failure of the
- // FindPackage call in (*importer).load,
- // but we rechecked that condition above.
- // Perhaps the state of the file system changed
- // in between? Seems unlikely.
- panic(fmt.Sprintf("imported[%q].err = %v", path, ii.err))
- }
if ii.info == nil {
// Unreachable.
- // Complete has this postcondition:
- // ii.err != nil || ii.info != nil
- // and we know that ii.err == nil here.
+ // awaitCompletion has the postcondition
+ // ii.info != nil.
panic(fmt.Sprintf("imported[%q].info = nil", path))
}
info := ii.info
@@ -557,7 +568,8 @@
}
createPkg := func(path string, files []*ast.File, errs []error) {
- info := imp.newPackageInfo(path)
+ // TODO(adonovan): fix: use dirname of files, not cwd.
+ info := imp.newPackageInfo(path, conf.Cwd)
for _, err := range errs {
info.appendError(err)
}
@@ -699,6 +711,9 @@
// 'x': include external *_test.go source files. (XTestGoFiles)
//
func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
+ if bp.Goroot && bp.ImportPath == "unsafe" {
+ return nil, nil
+ }
var filenames []string
switch which {
case 'g':
@@ -729,8 +744,6 @@
// doImport imports the package denoted by path.
// It implements the types.Importer signature.
//
-// imports is the type-checker's package canonicalization map.
-//
// It returns an error if a package could not be created
// (e.g. go/build or parse error), but type errors are reported via
// the types.Config.Error callback (the first of which is also saved
@@ -739,11 +752,6 @@
// Idempotent.
//
func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
- // Package unsafe is handled specially, and has no PackageInfo.
- // TODO(adonovan): move this check into go/types?
- if to == "unsafe" {
- return types.Unsafe, nil
- }
if to == "C" {
// This should be unreachable, but ad hoc packages are
// not currently subject to cgo preprocessing.
@@ -752,14 +760,24 @@
from.Pkg.Path())
}
+ bp, err := imp.findPackage(to, from.dir, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ // The standard unsafe package is handled specially,
+ // and has no PackageInfo.
+ if bp.Goroot && bp.ImportPath == "unsafe" {
+ return types.Unsafe, nil
+ }
+
+ // Look for the package in the cache using its canonical path.
+ path := bp.ImportPath
imp.importedMu.Lock()
- ii := imp.imported[to]
+ ii := imp.imported[path]
imp.importedMu.Unlock()
if ii == nil {
- panic("internal error: unexpected import: " + to)
- }
- if ii.err != nil {
- return nil, ii.err
+ panic("internal error: unexpected import: " + path)
}
if ii.info != nil {
return ii.info.Pkg, nil
@@ -767,7 +785,7 @@
// Import of incomplete package: this indicates a cycle.
fromPath := from.Pkg.Path()
- if cycle := imp.findPath(to, fromPath); cycle != nil {
+ if cycle := imp.findPath(path, fromPath); cycle != nil {
cycle = append([]string{fromPath}, cycle...)
return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
}
@@ -775,16 +793,62 @@
panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
}
-// loadAll loads, parses, and type-checks the specified packages in
+// findPackage locates the package denoted by the importPath in the
+// specified directory.
+func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) {
+ // We use a non-blocking duplicate-suppressing cache (gopl.io §9.7)
+ // to avoid holding the lock around FindPackage.
+ key := findpkgKey{importPath, fromDir, mode}
+ imp.findpkgMu.Lock()
+ v, ok := imp.findpkg[key]
+ if ok {
+ // cache hit
+ imp.findpkgMu.Unlock()
+
+ <-v.ready // wait for entry to become ready
+ } else {
+ // Cache miss: this goroutine becomes responsible for
+ // populating the map entry and broadcasting its readiness.
+ v = &findpkgValue{ready: make(chan struct{})}
+ imp.findpkg[key] = v
+ imp.findpkgMu.Unlock()
+
+ ioLimit <- true
+ v.bp, v.err = imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode)
+ <-ioLimit
+
+ if _, ok := v.err.(*build.NoGoError); ok {
+ v.err = nil // empty directory is not an error
+ }
+
+ close(v.ready) // broadcast ready condition
+ }
+ return v.bp, v.err
+}
+
+// importAll loads, parses, and type-checks the specified packages in
// parallel and returns their completed importInfos in unspecified order.
//
-// fromPath is the import path of the importing package, if it is
+// fromPath is the package path of the importing package, if it is
// importable, "" otherwise. It is used for cycle detection.
//
-func (imp *importer) loadAll(fromPath string, paths map[string]bool) []*importInfo {
- result := make([]*importInfo, 0, len(paths))
- for path := range paths {
- result = append(result, imp.startLoad(path))
+// fromDir is the directory containing the import declaration that
+// caused these imports.
+//
+func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) {
+ // TODO(adonovan): opt: do the loop in parallel once
+ // findPackage is non-blocking.
+ var pending []*importInfo
+ for importPath := range imports {
+ bp, err := imp.findPackage(importPath, fromDir, mode)
+ if err != nil {
+ errors = append(errors, importError{
+ path: importPath,
+ err: err,
+ })
+ continue
+ }
+ pending = append(pending, imp.startLoad(bp))
}
if fromPath != "" {
@@ -798,13 +862,13 @@
deps = make(map[string]bool)
imp.graph[fromPath] = deps
}
- for path := range paths {
- deps[path] = true
+ for _, ii := range pending {
+ deps[ii.path] = true
}
imp.graphMu.Unlock()
}
- for _, ii := range result {
+ for _, ii := range pending {
if fromPath != "" {
if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
// Cycle-forming import: we must not await its
@@ -822,8 +886,10 @@
}
}
ii.awaitCompletion()
+ infos = append(infos, ii.info)
}
- return result
+
+ return infos, errors
}
// findPath returns an arbitrary path from 'from' to 'to' in the import
@@ -856,20 +922,20 @@
// specified package and its dependencies, if it has not already begun.
//
// It returns an importInfo, not necessarily in a completed state. The
-// caller must call awaitCompletion() before accessing its info and err
-// fields.
+// caller must call awaitCompletion() before accessing its info field.
//
// startLoad is concurrency-safe and idempotent.
//
-func (imp *importer) startLoad(path string) *importInfo {
+func (imp *importer) startLoad(bp *build.Package) *importInfo {
+ path := bp.ImportPath
imp.importedMu.Lock()
ii, ok := imp.imported[path]
if !ok {
- ii = &importInfo{path: path}
- ii.complete.L = &ii.mu
+ ii = &importInfo{path: path, complete: make(chan struct{})}
imp.imported[path] = ii
go func() {
- ii.Complete(imp.load(path))
+ info := imp.load(bp)
+ ii.Complete(info)
}()
}
imp.importedMu.Unlock()
@@ -879,13 +945,8 @@
// load implements package loading by parsing Go source files
// located by go/build.
-//
-func (imp *importer) load(path string) (*PackageInfo, error) {
- bp, err := imp.conf.FindPackage(imp.conf.build(), path)
- if err != nil {
- return nil, err // package not found
- }
- info := imp.newPackageInfo(bp.ImportPath)
+func (imp *importer) load(bp *build.Package) *PackageInfo {
+ info := imp.newPackageInfo(bp.ImportPath, bp.Dir)
info.Importable = true
files, errs := imp.conf.parsePackageFiles(bp, 'g')
for _, err := range errs {
@@ -895,10 +956,10 @@
imp.addFiles(info, files, true)
imp.progMu.Lock()
- imp.prog.importMap[path] = info.Pkg
+ imp.prog.importMap[bp.ImportPath] = info.Pkg
imp.progMu.Unlock()
- return info, nil
+ return info
}
// addFiles adds and type-checks the specified files to info, loading
@@ -917,7 +978,9 @@
if cycleCheck {
fromPath = info.Pkg.Path()
}
- imp.loadAll(fromPath, scanImports(files))
+ // TODO(adonovan): opt: make the caller do scanImports.
+ // Callers with a build.Package can skip it.
+ imp.importAll(fromPath, info.dir, scanImports(files), 0)
if trace {
fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
@@ -934,7 +997,7 @@
}
}
-func (imp *importer) newPackageInfo(path string) *PackageInfo {
+func (imp *importer) newPackageInfo(path, dir string) *PackageInfo {
pkg := types.NewPackage(path, "")
info := &PackageInfo{
Pkg: pkg,
@@ -947,6 +1010,7 @@
Selections: make(map[*ast.SelectorExpr]*types.Selection),
},
errorFunc: imp.conf.TypeChecker.Error,
+ dir: dir,
}
// Copy the types.Config so we can vary it across PackageInfos.
@@ -955,9 +1019,7 @@
if f := imp.conf.TypeCheckFuncBodies; f != nil {
tc.IgnoreFuncBodies = !f(path)
}
- tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) {
- return imp.doImport(info, to)
- }
+ tc.Importer = closure{imp, info}
tc.Error = info.appendError // appendError wraps the user's Error function
info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
@@ -966,3 +1028,10 @@
imp.progMu.Unlock()
return info
}
+
+type closure struct {
+ imp *importer
+ info *PackageInfo
+}
+
+func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) }
diff --git a/go/src/golang.org/x/tools/go/loader/loader14.go b/go/src/golang.org/x/tools/go/loader/loader14.go
new file mode 100644
index 0000000..d3fb157
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/loader14.go
@@ -0,0 +1,1019 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package loader
+
+// See doc.go for package documentation and implementation notes.
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "os"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/types"
+)
+
+const trace = false // show timing info for type-checking
+
+// Config specifies the configuration for loading a whole program from
+// Go source code.
+// The zero value for Config is a ready-to-use default configuration.
+type Config struct {
+ // Fset is the file set for the parser to use when loading the
+ // program. If nil, it may be lazily initialized by any
+ // method of Config.
+ Fset *token.FileSet
+
+ // ParserMode specifies the mode to be used by the parser when
+ // loading source packages.
+ ParserMode parser.Mode
+
+ // TypeChecker contains options relating to the type checker.
+ //
+ // The supplied IgnoreFuncBodies is not used; the effective
+ // value comes from the TypeCheckFuncBodies func below.
+ // The supplied Import function is not used either.
+ TypeChecker types.Config
+
+ // TypeCheckFuncBodies is a predicate over package paths.
+ // A package for which the predicate is false will
+ // have its package-level declarations type checked, but not
+ // its function bodies; this can be used to quickly load
+ // dependencies from source. If nil, all func bodies are type
+ // checked.
+ TypeCheckFuncBodies func(path string) bool
+
+ // If Build is non-nil, it is used to locate source packages.
+ // Otherwise &build.Default is used.
+ //
+ // By default, cgo is invoked to preprocess Go files that
+ // import the fake package "C". This behaviour can be
+ // disabled by setting CGO_ENABLED=0 in the environment prior
+ // to startup, or by setting Build.CgoEnabled=false.
+ Build *build.Context
+
+ // The current directory, used for resolving relative package
+ // references such as "./go/loader". If empty, os.Getwd will be
+ // used instead.
+ Cwd string
+
+ // If DisplayPath is non-nil, it is used to transform each
+ // file name obtained from Build.Import(). This can be used
+ // to prevent a virtualized build.Config's file names from
+ // leaking into the user interface.
+ DisplayPath func(path string) string
+
+ // If AllowErrors is true, Load will return a Program even
+ // if some of the its packages contained I/O, parser or type
+ // errors; such errors are accessible via PackageInfo.Errors. If
+ // false, Load will fail if any package had an error.
+ AllowErrors bool
+
+ // CreatePkgs specifies a list of non-importable initial
+ // packages to create. The resulting packages will appear in
+ // the corresponding elements of the Program.Created slice.
+ CreatePkgs []PkgSpec
+
+ // ImportPkgs specifies a set of initial packages to load.
+ // The map keys are package paths.
+ //
+ // The map value indicates whether to load tests. If true, Load
+ // will add and type-check two lists of files to the package:
+ // non-test files followed by in-package *_test.go files. In
+ // addition, it will append the external test package (if any)
+ // to Program.Created.
+ ImportPkgs map[string]bool
+
+ // FindPackage is called during Load to create the build.Package
+ // for a given import path from a given directory.
+ // If FindPackage is nil, a default implementation
+ // based on ctxt.Import is used. A client may use this hook to
+ // adapt to a proprietary build system that does not follow the
+ // "go build" layout conventions, for example.
+ //
+ // It must be safe to call concurrently from multiple goroutines.
+ FindPackage func(ctxt *build.Context, fromDir, importPath string, mode build.ImportMode) (*build.Package, error)
+}
+
+// A PkgSpec specifies a non-importable package to be created by Load.
+// Files are processed first, but typically only one of Files and
+// Filenames is provided. The path needn't be globally unique.
+//
+type PkgSpec struct {
+ Path string // package path ("" => use package declaration)
+ Files []*ast.File // ASTs of already-parsed files
+ Filenames []string // names of files to be parsed
+}
+
+// A Program is a Go program loaded from source as specified by a Config.
+type Program struct {
+ Fset *token.FileSet // the file set for this program
+
+ // Created[i] contains the initial package whose ASTs or
+ // filenames were supplied by Config.CreatePkgs[i], followed by
+ // the external test package, if any, of each package in
+ // Config.ImportPkgs ordered by ImportPath.
+ //
+ // NOTE: these files must not import "C". Cgo preprocessing is
+ // only performed on imported packages, not ad hoc packages.
+ //
+ // TODO(adonovan): we need to copy and adapt the logic of
+ // goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make
+ // Config.Import and Config.Create methods return the same kind
+ // of entity, essentially a build.Package.
+ // Perhaps we can even reuse that type directly.
+ Created []*PackageInfo
+
+ // Imported contains the initially imported packages,
+ // as specified by Config.ImportPkgs.
+ Imported map[string]*PackageInfo
+
+ // AllPackages contains the PackageInfo of every package
+ // encountered by Load: all initial packages and all
+ // dependencies, including incomplete ones.
+ AllPackages map[*types.Package]*PackageInfo
+
+ // importMap is the canonical mapping of package paths to
+ // packages. It contains all Imported initial packages, but not
+ // Created ones, and all imported dependencies.
+ importMap map[string]*types.Package
+}
+
+// PackageInfo holds the ASTs and facts derived by the type-checker
+// for a single package.
+//
+// Not mutated once exposed via the API.
+//
+type PackageInfo struct {
+ Pkg *types.Package
+ Importable bool // true if 'import "Pkg.Path()"' would resolve to this
+ TransitivelyErrorFree bool // true if Pkg and all its dependencies are free of errors
+ Files []*ast.File // syntax trees for the package's files
+ Errors []error // non-nil if the package had errors
+ types.Info // type-checker deductions.
+ dir string // package directory
+
+ checker *types.Checker // transient type-checker state
+ errorFunc func(error)
+}
+
+func (info *PackageInfo) String() string { return info.Pkg.Path() }
+
+func (info *PackageInfo) appendError(err error) {
+ if info.errorFunc != nil {
+ info.errorFunc(err)
+ } else {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ info.Errors = append(info.Errors, err)
+}
+
+func (conf *Config) fset() *token.FileSet {
+ if conf.Fset == nil {
+ conf.Fset = token.NewFileSet()
+ }
+ return conf.Fset
+}
+
+// ParseFile is a convenience function (intended for testing) that invokes
+// the parser using the Config's FileSet, which is initialized if nil.
+//
+// src specifies the parser input as a string, []byte, or io.Reader, and
+// filename is its apparent name. If src is nil, the contents of
+// filename are read from the file system.
+//
+func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
+ // TODO(adonovan): use conf.build() etc like parseFiles does.
+ return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
+}
+
+// FromArgsUsage is a partial usage message that applications calling
+// FromArgs may wish to include in their -help output.
+const FromArgsUsage = `
+<args> is a list of arguments denoting a set of initial packages.
+It may take one of two forms:
+
+1. A list of *.go source files.
+
+ All of the specified files are loaded, parsed and type-checked
+ as a single package. All the files must belong to the same directory.
+
+2. A list of import paths, each denoting a package.
+
+ The package's directory is found relative to the $GOROOT and
+ $GOPATH using similar logic to 'go build', and the *.go files in
+ that directory are loaded, parsed and type-checked as a single
+ package.
+
+ In addition, all *_test.go files in the directory are then loaded
+ and parsed. Those files whose package declaration equals that of
+ the non-*_test.go files are included in the primary package. Test
+ files whose package declaration ends with "_test" are type-checked
+ as another package, the 'external' test package, so that a single
+ import path may denote two packages. (Whether this behaviour is
+ enabled is tool-specific, and may depend on additional flags.)
+
+A '--' argument terminates the list of packages.
+`
+
+// FromArgs interprets args as a set of initial packages to load from
+// source and updates the configuration. It returns the list of
+// unconsumed arguments.
+//
+// It is intended for use in command-line interfaces that require a
+// set of initial packages to be specified; see FromArgsUsage message
+// for details.
+//
+// Only superficial errors are reported at this stage; errors dependent
+// on I/O are detected during Load.
+//
+func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
+ var rest []string
+ for i, arg := range args {
+ if arg == "--" {
+ rest = args[i+1:]
+ args = args[:i]
+ break // consume "--" and return the remaining args
+ }
+ }
+
+ if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
+ // Assume args is a list of a *.go files
+ // denoting a single ad hoc package.
+ for _, arg := range args {
+ if !strings.HasSuffix(arg, ".go") {
+ return nil, fmt.Errorf("named files must be .go files: %s", arg)
+ }
+ }
+ conf.CreateFromFilenames("", args...)
+ } else {
+ // Assume args are directories each denoting a
+ // package and (perhaps) an external test, iff xtest.
+ for _, arg := range args {
+ if xtest {
+ conf.ImportWithTests(arg)
+ } else {
+ conf.Import(arg)
+ }
+ }
+ }
+
+ return rest, nil
+}
+
+// CreateFromFilenames is a convenience function that adds
+// a conf.CreatePkgs entry to create a package of the specified *.go
+// files.
+//
+func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
+ conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
+}
+
+// CreateFromFiles is a convenience function that adds a conf.CreatePkgs
+// entry to create package of the specified path and parsed files.
+//
+func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
+ conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
+}
+
+// ImportWithTests is a convenience function that adds path to
+// ImportPkgs, the set of initial source packages located relative to
+// $GOPATH. The package will be augmented by any *_test.go files in
+// its directory that contain a "package x" (not "package x_test")
+// declaration.
+//
+// In addition, if any *_test.go files contain a "package x_test"
+// declaration, an additional package comprising just those files will
+// be added to CreatePkgs.
+//
+func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
+
+// Import is a convenience function that adds path to ImportPkgs, the
+// set of initial packages that will be imported from source.
+//
+func (conf *Config) Import(path string) { conf.addImport(path, false) }
+
+func (conf *Config) addImport(path string, tests bool) {
+ if path == "C" {
+ return // ignore; not a real package
+ }
+ if conf.ImportPkgs == nil {
+ conf.ImportPkgs = make(map[string]bool)
+ }
+ conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
+}
+
+// PathEnclosingInterval returns the PackageInfo and ast.Node that
+// contain source interval [start, end), and all the node's ancestors
+// up to the AST root. It searches all ast.Files of all packages in prog.
+// exact is defined as for astutil.PathEnclosingInterval.
+//
+// The zero value is returned if not found.
+//
+func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
+ for _, info := range prog.AllPackages {
+ for _, f := range info.Files {
+ if f.Pos() == token.NoPos {
+ // This can happen if the parser saw
+ // too many errors and bailed out.
+ // (Use parser.AllErrors to prevent that.)
+ continue
+ }
+ if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) {
+ continue
+ }
+ if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
+ return info, path, exact
+ }
+ }
+ }
+ return nil, nil, false
+}
+
+// InitialPackages returns a new slice containing the set of initial
+// packages (Created + Imported) in unspecified order.
+//
+func (prog *Program) InitialPackages() []*PackageInfo {
+ infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
+ infos = append(infos, prog.Created...)
+ for _, info := range prog.Imported {
+ infos = append(infos, info)
+ }
+ return infos
+}
+
+// Package returns the ASTs and results of type checking for the
+// specified package.
+func (prog *Program) Package(path string) *PackageInfo {
+ if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
+ return info
+ }
+ for _, info := range prog.Created {
+ if path == info.Pkg.Path() {
+ return info
+ }
+ }
+ return nil
+}
+
+// ---------- Implementation ----------
+
+// importer holds the working state of the algorithm.
+type importer struct {
+ conf *Config // the client configuration
+ start time.Time // for logging
+
+ progMu sync.Mutex // guards prog
+ prog *Program // the resulting program
+
+ // findpkg is a memoization of FindPackage.
+ findpkgMu sync.Mutex // guards findpkg
+ findpkg map[findpkgKey]findpkgValue
+
+ importedMu sync.Mutex // guards imported
+ imported map[string]*importInfo // all imported packages (incl. failures) by import path
+
+ // import dependency graph: graph[x][y] => x imports y
+ //
+ // Since non-importable packages cannot be cyclic, we ignore
+ // their imports, thus we only need the subgraph over importable
+ // packages. Nodes are identified by their import paths.
+ graphMu sync.Mutex
+ graph map[string]map[string]bool
+}
+
+type findpkgKey struct {
+ importPath string
+ fromDir string
+ mode build.ImportMode
+}
+
+type findpkgValue struct {
+ bp *build.Package
+ err error
+}
+
+// importInfo tracks the success or failure of a single import.
+//
+// Upon completion, exactly one of info and err is non-nil:
+// info on successful creation of a package, err otherwise.
+// A successful package may still contain type errors.
+//
+type importInfo struct {
+ path string // import path
+ info *PackageInfo // results of typechecking (including errors)
+ complete chan struct{} // closed to broadcast that info is set.
+}
+
+// awaitCompletion blocks until ii is complete,
+// i.e. the info field is safe to inspect.
+func (ii *importInfo) awaitCompletion() {
+ <-ii.complete // wait for close
+}
+
+// Complete marks ii as complete.
+// Its info and err fields will not be subsequently updated.
+func (ii *importInfo) Complete(info *PackageInfo) {
+ if info == nil {
+ panic("info == nil")
+ }
+ ii.info = info
+ close(ii.complete)
+}
+
+type importError struct {
+ path string // import path
+ err error // reason for failure to create a package
+}
+
+// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
+// loading their dependencies packages as needed.
+//
+// On success, Load returns a Program containing a PackageInfo for
+// each package. On failure, it returns an error.
+//
+// If AllowErrors is true, Load will return a Program even if some
+// packages contained I/O, parser or type errors, or if dependencies
+// were missing. (Such errors are accessible via PackageInfo.Errors. If
+// false, Load will fail if any package had an error.
+//
+// It is an error if no packages were loaded.
+//
+func (conf *Config) Load() (*Program, error) {
+ // Create a simple default error handler for parse/type errors.
+ if conf.TypeChecker.Error == nil {
+ conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
+ }
+
+ // Set default working directory for relative package references.
+ if conf.Cwd == "" {
+ var err error
+ conf.Cwd, err = os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Install default FindPackage hook using go/build logic.
+ if conf.FindPackage == nil {
+ conf.FindPackage = func(ctxt *build.Context, path, fromDir string, mode build.ImportMode) (*build.Package, error) {
+ ioLimit <- true
+ bp, err := ctxt.Import(path, fromDir, mode)
+ <-ioLimit
+ if _, ok := err.(*build.NoGoError); ok {
+ return bp, nil // empty directory is not an error
+ }
+ return bp, err
+ }
+ }
+
+ prog := &Program{
+ Fset: conf.fset(),
+ Imported: make(map[string]*PackageInfo),
+ importMap: make(map[string]*types.Package),
+ AllPackages: make(map[*types.Package]*PackageInfo),
+ }
+
+ imp := importer{
+ conf: conf,
+ prog: prog,
+ findpkg: make(map[findpkgKey]findpkgValue),
+ imported: make(map[string]*importInfo),
+ start: time.Now(),
+ graph: make(map[string]map[string]bool),
+ }
+
+ // -- loading proper (concurrent phase) --------------------------------
+
+ var errpkgs []string // packages that contained errors
+
+ // Load the initially imported packages and their dependencies,
+ // in parallel.
+ infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, 0)
+ for _, ie := range importErrors {
+ conf.TypeChecker.Error(ie.err) // failed to create package
+ errpkgs = append(errpkgs, ie.path)
+ }
+ for _, info := range infos {
+ prog.Imported[info.Pkg.Path()] = info
+ }
+
+ // Augment the designated initial packages by their tests.
+ // Dependencies are loaded in parallel.
+ var xtestPkgs []*build.Package
+ for importPath, augment := range conf.ImportPkgs {
+ if !augment {
+ continue
+ }
+
+ bp, err := imp.findPackage(importPath, conf.Cwd, 0)
+ if err != nil {
+ // Package not found, or can't even parse package declaration.
+ // Already reported by previous loop; ignore it.
+ continue
+ }
+
+ // Needs external test package?
+ if len(bp.XTestGoFiles) > 0 {
+ xtestPkgs = append(xtestPkgs, bp)
+ }
+
+ // Consult the cache using the canonical package path.
+ path := bp.ImportPath
+ imp.importedMu.Lock() // (unnecessary, we're sequential here)
+ ii, ok := imp.imported[path]
+ // Paranoid checks added due to issue #11012.
+ if !ok {
+ // Unreachable.
+ // The previous loop called importAll and thus
+ // startLoad for each path in ImportPkgs, which
+ // populates imp.imported[path] with a non-zero value.
+ panic(fmt.Sprintf("imported[%q] not found", path))
+ }
+ if ii == nil {
+ // Unreachable.
+ // The ii values in this loop are the same as in
+ // the previous loop, which enforced the invariant
+ // that at least one of ii.err and ii.info is non-nil.
+ panic(fmt.Sprintf("imported[%q] == nil", path))
+ }
+ if ii.info == nil {
+ // Unreachable.
+ // awaitCompletion has the postcondition
+ // ii.info != nil.
+ panic(fmt.Sprintf("imported[%q].info = nil", path))
+ }
+ info := ii.info
+ imp.importedMu.Unlock()
+
+ // Parse the in-package test files.
+ files, errs := imp.conf.parsePackageFiles(bp, 't')
+ for _, err := range errs {
+ info.appendError(err)
+ }
+
+ // The test files augmenting package P cannot be imported,
+ // but may import packages that import P,
+ // so we must disable the cycle check.
+ imp.addFiles(info, files, false)
+ }
+
+ createPkg := func(path string, files []*ast.File, errs []error) {
+ // TODO(adonovan): fix: use dirname of files, not cwd.
+ info := imp.newPackageInfo(path, conf.Cwd)
+ for _, err := range errs {
+ info.appendError(err)
+ }
+
+ // Ad hoc packages are non-importable,
+ // so no cycle check is needed.
+ // addFiles loads dependencies in parallel.
+ imp.addFiles(info, files, false)
+ prog.Created = append(prog.Created, info)
+ }
+
+ // Create packages specified by conf.CreatePkgs.
+ for _, cp := range conf.CreatePkgs {
+ files, errs := parseFiles(conf.fset(), conf.build(), nil, ".", cp.Filenames, conf.ParserMode)
+ files = append(files, cp.Files...)
+
+ path := cp.Path
+ if path == "" {
+ if len(files) > 0 {
+ path = files[0].Name.Name
+ } else {
+ path = "(unnamed)"
+ }
+ }
+ createPkg(path, files, errs)
+ }
+
+ // Create external test packages.
+ sort.Sort(byImportPath(xtestPkgs))
+ for _, bp := range xtestPkgs {
+ files, errs := imp.conf.parsePackageFiles(bp, 'x')
+ createPkg(bp.ImportPath+"_test", files, errs)
+ }
+
+ // -- finishing up (sequential) ----------------------------------------
+
+ if len(prog.Imported)+len(prog.Created) == 0 {
+ return nil, errors.New("no initial packages were loaded")
+ }
+
+ // Create infos for indirectly imported packages.
+ // e.g. incomplete packages without syntax, loaded from export data.
+ for _, obj := range prog.importMap {
+ info := prog.AllPackages[obj]
+ if info == nil {
+ prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
+ } else {
+ // finished
+ info.checker = nil
+ info.errorFunc = nil
+ }
+ }
+
+ if !conf.AllowErrors {
+ // Report errors in indirectly imported packages.
+ for _, info := range prog.AllPackages {
+ if len(info.Errors) > 0 {
+ errpkgs = append(errpkgs, info.Pkg.Path())
+ }
+ }
+ if errpkgs != nil {
+ var more string
+ if len(errpkgs) > 3 {
+ more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
+ errpkgs = errpkgs[:3]
+ }
+ return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
+ strings.Join(errpkgs, ", "), more)
+ }
+ }
+
+ markErrorFreePackages(prog.AllPackages)
+
+ return prog, nil
+}
+
+type byImportPath []*build.Package
+
+func (b byImportPath) Len() int { return len(b) }
+func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
+func (b byImportPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+
+// markErrorFreePackages sets the TransitivelyErrorFree flag on all
+// applicable packages.
+func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
+ // Build the transpose of the import graph.
+ importedBy := make(map[*types.Package]map[*types.Package]bool)
+ for P := range allPackages {
+ for _, Q := range P.Imports() {
+ clients, ok := importedBy[Q]
+ if !ok {
+ clients = make(map[*types.Package]bool)
+ importedBy[Q] = clients
+ }
+ clients[P] = true
+ }
+ }
+
+ // Find all packages reachable from some error package.
+ reachable := make(map[*types.Package]bool)
+ var visit func(*types.Package)
+ visit = func(p *types.Package) {
+ if !reachable[p] {
+ reachable[p] = true
+ for q := range importedBy[p] {
+ visit(q)
+ }
+ }
+ }
+ for _, info := range allPackages {
+ if len(info.Errors) > 0 {
+ visit(info.Pkg)
+ }
+ }
+
+ // Mark the others as "transitively error-free".
+ for _, info := range allPackages {
+ if !reachable[info.Pkg] {
+ info.TransitivelyErrorFree = true
+ }
+ }
+}
+
+// build returns the effective build context.
+func (conf *Config) build() *build.Context {
+ if conf.Build != nil {
+ return conf.Build
+ }
+ return &build.Default
+}
+
+// parsePackageFiles enumerates the files belonging to package path,
+// then loads, parses and returns them, plus a list of I/O or parse
+// errors that were encountered.
+//
+// 'which' indicates which files to include:
+// 'g': include non-test *.go source files (GoFiles + processed CgoFiles)
+// 't': include in-package *_test.go source files (TestGoFiles)
+// 'x': include external *_test.go source files. (XTestGoFiles)
+//
+func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
+ if bp.Goroot && bp.ImportPath == "unsafe" {
+ return nil, nil
+ }
+ var filenames []string
+ switch which {
+ case 'g':
+ filenames = bp.GoFiles
+ case 't':
+ filenames = bp.TestGoFiles
+ case 'x':
+ filenames = bp.XTestGoFiles
+ default:
+ panic(which)
+ }
+
+ files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
+
+ // Preprocess CgoFiles and parse the outputs (sequentially).
+ if which == 'g' && bp.CgoFiles != nil {
+ cgofiles, err := processCgoFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
+ if err != nil {
+ errs = append(errs, err)
+ } else {
+ files = append(files, cgofiles...)
+ }
+ }
+
+ return files, errs
+}
+
+// doImport imports the package denoted by path.
+// It implements the types.Importer signature.
+//
+// It returns an error if a package could not be created
+// (e.g. go/build or parse error), but type errors are reported via
+// the types.Config.Error callback (the first of which is also saved
+// in the package's PackageInfo).
+//
+// Idempotent.
+//
+func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
+ if to == "C" {
+ // This should be unreachable, but ad hoc packages are
+ // not currently subject to cgo preprocessing.
+ // See https://github.com/golang/go/issues/11627.
+ return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`,
+ from.Pkg.Path())
+ }
+
+ bp, err := imp.findPackage(to, from.dir, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ // The standard unsafe package is handled specially,
+ // and has no PackageInfo.
+ if bp.Goroot && bp.ImportPath == "unsafe" {
+ return types.Unsafe, nil
+ }
+
+ // Look for the package in the cache using its canonical path.
+ path := bp.ImportPath
+ imp.importedMu.Lock()
+ ii := imp.imported[path]
+ imp.importedMu.Unlock()
+ if ii == nil {
+ panic("internal error: unexpected import: " + path)
+ }
+ if ii.info != nil {
+ return ii.info.Pkg, nil
+ }
+
+ // Import of incomplete package: this indicates a cycle.
+ fromPath := from.Pkg.Path()
+ if cycle := imp.findPath(path, fromPath); cycle != nil {
+ cycle = append([]string{fromPath}, cycle...)
+ return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
+ }
+
+ panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
+}
+
+// findPackage locates the package denoted by the importPath in the
+// specified directory.
+func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) {
+ // TODO(adonovan): opt: non-blocking duplicate-suppressing cache.
+ // i.e. don't hold the lock around FindPackage.
+ key := findpkgKey{importPath, fromDir, mode}
+ imp.findpkgMu.Lock()
+ defer imp.findpkgMu.Unlock()
+ v, ok := imp.findpkg[key]
+ if !ok {
+ bp, err := imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode)
+ v = findpkgValue{bp, err}
+ imp.findpkg[key] = v
+ }
+ return v.bp, v.err
+}
+
+// importAll loads, parses, and type-checks the specified packages in
+// parallel and returns their completed importInfos in unspecified order.
+//
+// fromPath is the package path of the importing package, if it is
+// importable, "" otherwise. It is used for cycle detection.
+//
+// fromDir is the directory containing the import declaration that
+// caused these imports.
+//
+func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) {
+ // TODO(adonovan): opt: do the loop in parallel once
+ // findPackage is non-blocking.
+ var pending []*importInfo
+ for importPath := range imports {
+ bp, err := imp.findPackage(importPath, fromDir, mode)
+ if err != nil {
+ errors = append(errors, importError{
+ path: importPath,
+ err: err,
+ })
+ continue
+ }
+ pending = append(pending, imp.startLoad(bp))
+ }
+
+ if fromPath != "" {
+ // We're loading a set of imports.
+ //
+ // We must record graph edges from the importing package
+ // to its dependencies, and check for cycles.
+ imp.graphMu.Lock()
+ deps, ok := imp.graph[fromPath]
+ if !ok {
+ deps = make(map[string]bool)
+ imp.graph[fromPath] = deps
+ }
+ for _, ii := range pending {
+ deps[ii.path] = true
+ }
+ imp.graphMu.Unlock()
+ }
+
+ for _, ii := range pending {
+ if fromPath != "" {
+ if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
+ // Cycle-forming import: we must not await its
+ // completion since it would deadlock.
+ //
+ // We don't record the error in ii since
+ // the error is really associated with the
+ // cycle-forming edge, not the package itself.
+ // (Also it would complicate the
+ // invariants of importPath completion.)
+ if trace {
+ fmt.Fprintln(os.Stderr, "import cycle: %q", cycle)
+ }
+ continue
+ }
+ }
+ ii.awaitCompletion()
+ infos = append(infos, ii.info)
+ }
+
+ return infos, errors
+}
+
+// findPath returns an arbitrary path from 'from' to 'to' in the import
+// graph, or nil if there was none.
+func (imp *importer) findPath(from, to string) []string {
+ imp.graphMu.Lock()
+ defer imp.graphMu.Unlock()
+
+ seen := make(map[string]bool)
+ var search func(stack []string, importPath string) []string
+ search = func(stack []string, importPath string) []string {
+ if !seen[importPath] {
+ seen[importPath] = true
+ stack = append(stack, importPath)
+ if importPath == to {
+ return stack
+ }
+ for x := range imp.graph[importPath] {
+ if p := search(stack, x); p != nil {
+ return p
+ }
+ }
+ }
+ return nil
+ }
+ return search(make([]string, 0, 20), from)
+}
+
+// startLoad initiates the loading, parsing and type-checking of the
+// specified package and its dependencies, if it has not already begun.
+//
+// It returns an importInfo, not necessarily in a completed state. The
+// caller must call awaitCompletion() before accessing its info field.
+//
+// startLoad is concurrency-safe and idempotent.
+//
+func (imp *importer) startLoad(bp *build.Package) *importInfo {
+ path := bp.ImportPath
+ imp.importedMu.Lock()
+ ii, ok := imp.imported[path]
+ if !ok {
+ ii = &importInfo{path: path, complete: make(chan struct{})}
+ imp.imported[path] = ii
+ go func() {
+ info := imp.load(bp)
+ ii.Complete(info)
+ }()
+ }
+ imp.importedMu.Unlock()
+
+ return ii
+}
+
+// load implements package loading by parsing Go source files
+// located by go/build.
+func (imp *importer) load(bp *build.Package) *PackageInfo {
+ info := imp.newPackageInfo(bp.ImportPath, bp.Dir)
+ info.Importable = true
+ files, errs := imp.conf.parsePackageFiles(bp, 'g')
+ for _, err := range errs {
+ info.appendError(err)
+ }
+
+ imp.addFiles(info, files, true)
+
+ imp.progMu.Lock()
+ imp.prog.importMap[bp.ImportPath] = info.Pkg
+ imp.progMu.Unlock()
+
+ return info
+}
+
+// addFiles adds and type-checks the specified files to info, loading
+// their dependencies if needed. The order of files determines the
+// package initialization order. It may be called multiple times on the
+// same package. Errors are appended to the info.Errors field.
+//
+// cycleCheck determines whether the imports within files create
+// dependency edges that should be checked for potential cycles.
+//
+func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
+ info.Files = append(info.Files, files...)
+
+ // Ensure the dependencies are loaded, in parallel.
+ var fromPath string
+ if cycleCheck {
+ fromPath = info.Pkg.Path()
+ }
+ // TODO(adonovan): opt: make the caller do scanImports.
+ // Callers with a build.Package can skip it.
+ imp.importAll(fromPath, info.dir, scanImports(files), 0)
+
+ if trace {
+ fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
+ time.Since(imp.start), info.Pkg.Path(), len(files))
+ }
+
+ // Ignore the returned (first) error since we
+ // already collect them all in the PackageInfo.
+ info.checker.Files(files)
+
+ if trace {
+ fmt.Fprintf(os.Stderr, "%s: stop %q\n",
+ time.Since(imp.start), info.Pkg.Path())
+ }
+}
+
+func (imp *importer) newPackageInfo(path, dir string) *PackageInfo {
+ pkg := types.NewPackage(path, "")
+ info := &PackageInfo{
+ Pkg: pkg,
+ Info: types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ Implicits: make(map[ast.Node]types.Object),
+ Scopes: make(map[ast.Node]*types.Scope),
+ Selections: make(map[*ast.SelectorExpr]*types.Selection),
+ },
+ errorFunc: imp.conf.TypeChecker.Error,
+ dir: dir,
+ }
+
+ // Copy the types.Config so we can vary it across PackageInfos.
+ tc := imp.conf.TypeChecker
+ tc.IgnoreFuncBodies = false
+ if f := imp.conf.TypeCheckFuncBodies; f != nil {
+ tc.IgnoreFuncBodies = !f(path)
+ }
+ tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) {
+ return imp.doImport(info, to)
+ }
+ tc.Error = info.appendError // appendError wraps the user's Error function
+
+ info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
+ imp.progMu.Lock()
+ imp.prog.AllPackages[pkg] = info
+ imp.progMu.Unlock()
+ return info
+}
diff --git a/go/src/golang.org/x/tools/go/loader/loader14_test.go b/go/src/golang.org/x/tools/go/loader/loader14_test.go
new file mode 100644
index 0000000..fab3170
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/loader14_test.go
@@ -0,0 +1,676 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// No testdata on Android.
+
+// +build !android
+
+package loader_test
+
+import (
+ "fmt"
+ "go/build"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
+ "sync"
+ "testing"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+)
+
+// TestFromArgs checks that conf.FromArgs populates conf correctly.
+// It does no I/O.
+func TestFromArgs(t *testing.T) {
+ type result struct {
+ Err string
+ Rest []string
+ ImportPkgs map[string]bool
+ CreatePkgs []loader.PkgSpec
+ }
+ for _, test := range []struct {
+ args []string
+ tests bool
+ want result
+ }{
+ // Mix of existing and non-existent packages.
+ {
+ args: []string{"nosuchpkg", "errors"},
+ want: result{
+ ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false},
+ },
+ },
+ // Same, with -test flag.
+ {
+ args: []string{"nosuchpkg", "errors"},
+ tests: true,
+ want: result{
+ ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true},
+ },
+ },
+ // Surplus arguments.
+ {
+ args: []string{"fmt", "errors", "--", "surplus"},
+ want: result{
+ Rest: []string{"surplus"},
+ ImportPkgs: map[string]bool{"errors": false, "fmt": false},
+ },
+ },
+ // Ad hoc package specified as *.go files.
+ {
+ args: []string{"foo.go", "bar.go"},
+ want: result{CreatePkgs: []loader.PkgSpec{{
+ Filenames: []string{"foo.go", "bar.go"},
+ }}},
+ },
+ // Mixture of *.go and import paths.
+ {
+ args: []string{"foo.go", "fmt"},
+ want: result{
+ Err: "named files must be .go files: fmt",
+ },
+ },
+ } {
+ var conf loader.Config
+ rest, err := conf.FromArgs(test.args, test.tests)
+ got := result{
+ Rest: rest,
+ ImportPkgs: conf.ImportPkgs,
+ CreatePkgs: conf.CreatePkgs,
+ }
+ if err != nil {
+ got.Err = err.Error()
+ }
+ if !reflect.DeepEqual(got, test.want) {
+ t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want)
+ }
+ }
+}
+
+func TestLoad_NoInitialPackages(t *testing.T) {
+ var conf loader.Config
+
+ const wantErr = "no initial packages were loaded"
+
+ prog, err := conf.Load()
+ if err == nil {
+ t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
+ } else if err.Error() != wantErr {
+ t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
+ }
+ if prog != nil {
+ t.Errorf("Load unexpectedly returned a Program")
+ }
+}
+
+func TestLoad_MissingInitialPackage(t *testing.T) {
+ var conf loader.Config
+ conf.Import("nosuchpkg")
+ conf.Import("errors")
+
+ const wantErr = "couldn't load packages due to errors: nosuchpkg"
+
+ prog, err := conf.Load()
+ if err == nil {
+ t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
+ } else if err.Error() != wantErr {
+ t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
+ }
+ if prog != nil {
+ t.Errorf("Load unexpectedly returned a Program")
+ }
+}
+
+func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) {
+ var conf loader.Config
+ conf.AllowErrors = true
+ conf.Import("nosuchpkg")
+ conf.ImportWithTests("errors")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed unexpectedly: %v", err)
+ }
+ if prog == nil {
+ t.Fatalf("Load returned a nil Program")
+ }
+ if got, want := created(prog), "errors_test"; got != want {
+ t.Errorf("Created = %s, want %s", got, want)
+ }
+ if got, want := imported(prog), "errors"; got != want {
+ t.Errorf("Imported = %s, want %s", got, want)
+ }
+}
+
+func TestCreateUnnamedPackage(t *testing.T) {
+ var conf loader.Config
+ conf.CreateFromFilenames("")
+ prog, err := conf.Load()
+ if err != nil {
+ t.Fatalf("Load failed: %v", err)
+ }
+ if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
+ t.Errorf("InitialPackages = %s, want %s", got, want)
+ }
+}
+
+func TestLoad_MissingFileInCreatedPackage(t *testing.T) {
+ var conf loader.Config
+ conf.CreateFromFilenames("", "missing.go")
+
+ const wantErr = "couldn't load packages due to errors: (unnamed)"
+
+ prog, err := conf.Load()
+ if prog != nil {
+ t.Errorf("Load unexpectedly returned a Program")
+ }
+ if err == nil {
+ t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
+ }
+ if err.Error() != wantErr {
+ t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
+ }
+}
+
+func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) {
+ conf := loader.Config{AllowErrors: true}
+ conf.CreateFromFilenames("", "missing.go")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed: %v", err)
+ }
+ if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
+ t.Fatalf("InitialPackages = %s, want %s", got, want)
+ }
+}
+
+func TestLoad_ParseError(t *testing.T) {
+ var conf loader.Config
+ conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
+
+ const wantErr = "couldn't load packages due to errors: badpkg"
+
+ prog, err := conf.Load()
+ if prog != nil {
+ t.Errorf("Load unexpectedly returned a Program")
+ }
+ if err == nil {
+ t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
+ }
+ if err.Error() != wantErr {
+ t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
+ }
+}
+
+func TestLoad_ParseError_AllowErrors(t *testing.T) {
+ var conf loader.Config
+ conf.AllowErrors = true
+ conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed unexpectedly: %v", err)
+ }
+ if prog == nil {
+ t.Fatalf("Load returned a nil Program")
+ }
+ if got, want := created(prog), "badpkg"; got != want {
+ t.Errorf("Created = %s, want %s", got, want)
+ }
+
+ badpkg := prog.Created[0]
+ if len(badpkg.Files) != 1 {
+ t.Errorf("badpkg has %d files, want 1", len(badpkg.Files))
+ }
+ wantErr := filepath.Join("testdata", "badpkgdecl.go") + ":1:34: expected 'package', found 'EOF'"
+ if !hasError(badpkg.Errors, wantErr) {
+ t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr)
+ }
+}
+
+func TestLoad_FromSource_Success(t *testing.T) {
+ var conf loader.Config
+ conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed unexpectedly: %v", err)
+ }
+ if prog == nil {
+ t.Fatalf("Load returned a nil Program")
+ }
+ if got, want := created(prog), "P"; got != want {
+ t.Errorf("Created = %s, want %s", got, want)
+ }
+}
+
+func TestLoad_FromImports_Success(t *testing.T) {
+ var conf loader.Config
+ conf.ImportWithTests("fmt")
+ conf.ImportWithTests("errors")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed unexpectedly: %v", err)
+ }
+ if prog == nil {
+ t.Fatalf("Load returned a nil Program")
+ }
+ if got, want := created(prog), "errors_test fmt_test"; got != want {
+ t.Errorf("Created = %q, want %s", got, want)
+ }
+ if got, want := imported(prog), "errors fmt"; got != want {
+ t.Errorf("Imported = %s, want %s", got, want)
+ }
+ // Check set of transitive packages.
+ // There are >30 and the set may grow over time, so only check a few.
+ want := map[string]bool{
+ "strings": true,
+ "time": true,
+ "runtime": true,
+ "testing": true,
+ "unicode": true,
+ }
+ for _, path := range all(prog) {
+ delete(want, path)
+ }
+ if len(want) > 0 {
+ t.Errorf("AllPackages is missing these keys: %q", keys(want))
+ }
+}
+
+func TestLoad_MissingIndirectImport(t *testing.T) {
+ pkgs := map[string]string{
+ "a": `package a; import _ "b"`,
+ "b": `package b; import _ "c"`,
+ }
+ conf := loader.Config{Build: fakeContext(pkgs)}
+ conf.Import("a")
+
+ const wantErr = "couldn't load packages due to errors: b"
+
+ prog, err := conf.Load()
+ if err == nil {
+ t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
+ } else if err.Error() != wantErr {
+ t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
+ }
+ if prog != nil {
+ t.Errorf("Load unexpectedly returned a Program")
+ }
+}
+
+func TestLoad_BadDependency_AllowErrors(t *testing.T) {
+ for _, test := range []struct {
+ descr string
+ pkgs map[string]string
+ wantPkgs string
+ }{
+
+ {
+ descr: "missing dependency",
+ pkgs: map[string]string{
+ "a": `package a; import _ "b"`,
+ "b": `package b; import _ "c"`,
+ },
+ wantPkgs: "a b",
+ },
+ {
+ descr: "bad package decl in dependency",
+ pkgs: map[string]string{
+ "a": `package a; import _ "b"`,
+ "b": `package b; import _ "c"`,
+ "c": `package`,
+ },
+ wantPkgs: "a b",
+ },
+ {
+ descr: "parse error in dependency",
+ pkgs: map[string]string{
+ "a": `package a; import _ "b"`,
+ "b": `package b; import _ "c"`,
+ "c": `package c; var x = `,
+ },
+ wantPkgs: "a b c",
+ },
+ } {
+ conf := loader.Config{
+ AllowErrors: true,
+ Build: fakeContext(test.pkgs),
+ }
+ conf.Import("a")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err)
+ }
+ if prog == nil {
+ t.Fatalf("%s: Load returned a nil Program", test.descr)
+ }
+
+ if got, want := imported(prog), "a"; got != want {
+ t.Errorf("%s: Imported = %s, want %s", test.descr, got, want)
+ }
+ if got := all(prog); strings.Join(got, " ") != test.wantPkgs {
+ t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs)
+ }
+ }
+}
+
+func TestCwd(t *testing.T) {
+ ctxt := fakeContext(map[string]string{"one/two/three": `package three`})
+ for _, test := range []struct {
+ cwd, arg, want string
+ }{
+ {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"},
+ {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"},
+ {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"},
+ {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"},
+ {cwd: "/go/src/one", arg: "two/three", want: ""},
+ } {
+ conf := loader.Config{
+ Cwd: test.cwd,
+ Build: ctxt,
+ }
+ conf.Import(test.arg)
+
+ var got string
+ prog, err := conf.Load()
+ if prog != nil {
+ got = imported(prog)
+ }
+ if got != test.want {
+ t.Errorf("Load(%s) from %s: Imported = %s, want %s",
+ test.arg, test.cwd, got, test.want)
+ if err != nil {
+ t.Errorf("Load failed: %v", err)
+ }
+ }
+ }
+}
+
+// TODO(adonovan): more Load tests:
+//
+// failures:
+// - to parse package decl of *_test.go files
+// - to parse package decl of external *_test.go files
+// - to parse whole of *_test.go files
+// - to parse whole of external *_test.go files
+// - to open a *.go file during import scanning
+// - to import from binary
+
+// features:
+// - InitialPackages
+// - PackageCreated hook
+// - TypeCheckFuncBodies hook
+
+func TestTransitivelyErrorFreeFlag(t *testing.T) {
+ // Create an minimal custom build.Context
+ // that fakes the following packages:
+ //
+ // a --> b --> c! c has an error
+ // \ d and e are transitively error-free.
+ // e --> d
+ //
+ // Each package [a-e] consists of one file, x.go.
+ pkgs := map[string]string{
+ "a": `package a; import (_ "b"; _ "e")`,
+ "b": `package b; import _ "c"`,
+ "c": `package c; func f() { _ = int(false) }`, // type error within function body
+ "d": `package d;`,
+ "e": `package e; import _ "d"`,
+ }
+ conf := loader.Config{
+ AllowErrors: true,
+ Build: fakeContext(pkgs),
+ }
+ conf.Import("a")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed: %s", err)
+ }
+ if prog == nil {
+ t.Fatalf("Load returned nil *Program")
+ }
+
+ for pkg, info := range prog.AllPackages {
+ var wantErr, wantTEF bool
+ switch pkg.Path() {
+ case "a", "b":
+ case "c":
+ wantErr = true
+ case "d", "e":
+ wantTEF = true
+ default:
+ t.Errorf("unexpected package: %q", pkg.Path())
+ continue
+ }
+
+ if (info.Errors != nil) != wantErr {
+ if wantErr {
+ t.Errorf("Package %q.Error = nil, want error", pkg.Path())
+ } else {
+ t.Errorf("Package %q has unexpected Errors: %v",
+ pkg.Path(), info.Errors)
+ }
+ }
+
+ if info.TransitivelyErrorFree != wantTEF {
+ t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t",
+ pkg.Path(), info.TransitivelyErrorFree, wantTEF)
+ }
+ }
+}
+
+// Test that syntax (scan/parse), type, and loader errors are recorded
+// (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error).
+func TestErrorReporting(t *testing.T) {
+ pkgs := map[string]string{
+ "a": `package a; import (_ "b"; _ "c"); var x int = false`,
+ "b": `package b; 'syntax error!`,
+ }
+ conf := loader.Config{
+ AllowErrors: true,
+ Build: fakeContext(pkgs),
+ }
+ var mu sync.Mutex
+ var allErrors []error
+ conf.TypeChecker.Error = func(err error) {
+ mu.Lock()
+ allErrors = append(allErrors, err)
+ mu.Unlock()
+ }
+ conf.Import("a")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed: %s", err)
+ }
+ if prog == nil {
+ t.Fatalf("Load returned nil *Program")
+ }
+
+ // TODO(adonovan): test keys of ImportMap.
+
+ // Check errors recorded in each PackageInfo.
+ for pkg, info := range prog.AllPackages {
+ switch pkg.Path() {
+ case "a":
+ if !hasError(info.Errors, "cannot convert false") {
+ t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors)
+ }
+ if !hasError(info.Errors, "could not import c") {
+ t.Errorf("a.Errors = %v, want import (loader) error", info.Errors)
+ }
+ case "b":
+ if !hasError(info.Errors, "rune literal not terminated") {
+ t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors)
+ }
+ }
+ }
+
+ // Check errors reported via error handler.
+ if !hasError(allErrors, "cannot convert false") ||
+ !hasError(allErrors, "rune literal not terminated") ||
+ !hasError(allErrors, "could not import c") {
+ t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors)
+ }
+}
+
+func TestCycles(t *testing.T) {
+ for _, test := range []struct {
+ descr string
+ ctxt *build.Context
+ wantErr string
+ }{
+ {
+ "self-cycle",
+ fakeContext(map[string]string{
+ "main": `package main; import _ "selfcycle"`,
+ "selfcycle": `package selfcycle; import _ "selfcycle"`,
+ }),
+ `import cycle: selfcycle -> selfcycle`,
+ },
+ {
+ "three-package cycle",
+ fakeContext(map[string]string{
+ "main": `package main; import _ "a"`,
+ "a": `package a; import _ "b"`,
+ "b": `package b; import _ "c"`,
+ "c": `package c; import _ "a"`,
+ }),
+ `import cycle: c -> a -> b -> c`,
+ },
+ {
+ "self-cycle in dependency of test file",
+ buildutil.FakeContext(map[string]map[string]string{
+ "main": {
+ "main.go": `package main`,
+ "main_test.go": `package main; import _ "a"`,
+ },
+ "a": {
+ "a.go": `package a; import _ "a"`,
+ },
+ }),
+ `import cycle: a -> a`,
+ },
+ // TODO(adonovan): fix: these fail
+ // {
+ // "two-package cycle in dependency of test file",
+ // buildutil.FakeContext(map[string]map[string]string{
+ // "main": {
+ // "main.go": `package main`,
+ // "main_test.go": `package main; import _ "a"`,
+ // },
+ // "a": {
+ // "a.go": `package a; import _ "main"`,
+ // },
+ // }),
+ // `import cycle: main -> a -> main`,
+ // },
+ // {
+ // "self-cycle in augmented package",
+ // buildutil.FakeContext(map[string]map[string]string{
+ // "main": {
+ // "main.go": `package main`,
+ // "main_test.go": `package main; import _ "main"`,
+ // },
+ // }),
+ // `import cycle: main -> main`,
+ // },
+ } {
+ conf := loader.Config{
+ AllowErrors: true,
+ Build: test.ctxt,
+ }
+ var mu sync.Mutex
+ var allErrors []error
+ conf.TypeChecker.Error = func(err error) {
+ mu.Lock()
+ allErrors = append(allErrors, err)
+ mu.Unlock()
+ }
+ conf.ImportWithTests("main")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("%s: Load failed: %s", test.descr, err)
+ }
+ if prog == nil {
+ t.Fatalf("%s: Load returned nil *Program", test.descr)
+ }
+
+ if !hasError(allErrors, test.wantErr) {
+ t.Errorf("%s: Load() errors = %q, want %q",
+ test.descr, allErrors, test.wantErr)
+ }
+ }
+
+ // TODO(adonovan):
+ // - Test that in a legal test cycle, none of the symbols
+ // defined by augmentation are visible via import.
+}
+
+// ---- utilities ----
+
+// Simplifying wrapper around buildutil.FakeContext for single-file packages.
+func fakeContext(pkgs map[string]string) *build.Context {
+ pkgs2 := make(map[string]map[string]string)
+ for path, content := range pkgs {
+ pkgs2[path] = map[string]string{"x.go": content}
+ }
+ return buildutil.FakeContext(pkgs2)
+}
+
+func hasError(errors []error, substr string) bool {
+ for _, err := range errors {
+ if strings.Contains(err.Error(), substr) {
+ return true
+ }
+ }
+ return false
+}
+
+func keys(m map[string]bool) (keys []string) {
+ for key := range m {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ return
+}
+
+// Returns all loaded packages.
+func all(prog *loader.Program) []string {
+ var pkgs []string
+ for _, info := range prog.AllPackages {
+ pkgs = append(pkgs, info.Pkg.Path())
+ }
+ sort.Strings(pkgs)
+ return pkgs
+}
+
+// Returns initially imported packages, as a string.
+func imported(prog *loader.Program) string {
+ var pkgs []string
+ for _, info := range prog.Imported {
+ pkgs = append(pkgs, info.Pkg.Path())
+ }
+ sort.Strings(pkgs)
+ return strings.Join(pkgs, " ")
+}
+
+// Returns initially created packages, as a string.
+func created(prog *loader.Program) string {
+ var pkgs []string
+ for _, info := range prog.Created {
+ pkgs = append(pkgs, info.Pkg.Path())
+ }
+ return strings.Join(pkgs, " ")
+}
diff --git a/go/src/golang.org/x/tools/go/loader/loader_test.go b/go/src/golang.org/x/tools/go/loader/loader_test.go
index 602590e..1f05e18 100644
--- a/go/src/golang.org/x/tools/go/loader/loader_test.go
+++ b/go/src/golang.org/x/tools/go/loader/loader_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// No testdata on Android.
// +build !android
@@ -22,6 +24,8 @@
"golang.org/x/tools/go/loader"
)
+var go16 bool // Go version >= go1.6
+
// TestFromArgs checks that conf.FromArgs populates conf correctly.
// It does no I/O.
func TestFromArgs(t *testing.T) {
@@ -393,6 +397,86 @@
}
}
+func TestLoad_vendor(t *testing.T) {
+ if !go16 {
+ // TODO(adonovan): delete in due course.
+ t.Skipf("vendoring requires Go 1.6")
+ }
+ pkgs := map[string]string{
+ "a": `package a; import _ "x"`,
+ "a/vendor": ``, // mkdir a/vendor
+ "a/vendor/x": `package xa`,
+ "b": `package b; import _ "x"`,
+ "b/vendor": ``, // mkdir b/vendor
+ "b/vendor/x": `package xb`,
+ "c": `package c; import _ "x"`,
+ "x": `package xc`,
+ }
+ conf := loader.Config{Build: fakeContext(pkgs)}
+ conf.Import("a")
+ conf.Import("b")
+ conf.Import("c")
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Check that a, b, and c see different versions of x.
+ for _, r := range "abc" {
+ name := string(r)
+ got := prog.Package(name).Pkg.Imports()[0]
+ want := "x" + name
+ if got.Name() != want {
+ t.Errorf("package %s import %q = %s, want %s",
+ name, "x", got.Name(), want)
+ }
+ }
+}
+
+func TestVendorCwd(t *testing.T) {
+ if !go16 {
+ // TODO(adonovan): delete in due course.
+ t.Skipf("vendoring requires Go 1.6")
+ }
+ // Test the interaction of cwd and vendor directories.
+ ctxt := fakeContext(map[string]string{
+ "net": ``, // mkdir net
+ "net/http": `package http; import _ "hpack"`,
+ "vendor": ``, // mkdir vendor
+ "vendor/hpack": `package vendorhpack`,
+ "hpack": `package hpack`,
+ })
+ for i, test := range []struct {
+ cwd, arg, want string
+ }{
+ {cwd: "/go/src/net", arg: "http"}, // not found
+ {cwd: "/go/src/net", arg: "./http", want: "net/http vendor/hpack"},
+ {cwd: "/go/src/net", arg: "hpack", want: "hpack"},
+ {cwd: "/go/src/vendor", arg: "hpack", want: "hpack"},
+ {cwd: "/go/src/vendor", arg: "./hpack", want: "vendor/hpack"},
+ } {
+ conf := loader.Config{
+ Cwd: test.cwd,
+ Build: ctxt,
+ }
+ conf.Import(test.arg)
+
+ var got string
+ prog, err := conf.Load()
+ if prog != nil {
+ got = strings.Join(all(prog), " ")
+ }
+ if got != test.want {
+ t.Errorf("#%d: Load(%s) from %s: got %s, want %s",
+ i, test.arg, test.cwd, got, test.want)
+ if err != nil {
+ t.Errorf("Load failed: %v", err)
+ }
+ }
+ }
+}
+
// TODO(adonovan): more Load tests:
//
// failures:
diff --git a/go/src/golang.org/x/tools/go/loader/stdlib14_test.go b/go/src/golang.org/x/tools/go/loader/stdlib14_test.go
new file mode 100644
index 0000000..c3a2987
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/loader/stdlib14_test.go
@@ -0,0 +1,197 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package loader_test
+
+// This file enumerates all packages beneath $GOROOT, loads them, plus
+// their external tests if any, runs the type checker on them, and
+// prints some summary information.
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/token"
+ "io/ioutil"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+)
+
+func TestStdlib(t *testing.T) {
+ if runtime.GOOS == "android" {
+ t.Skipf("incomplete std lib on %s", runtime.GOOS)
+ }
+
+ runtime.GC()
+ t0 := time.Now()
+ var memstats runtime.MemStats
+ runtime.ReadMemStats(&memstats)
+ alloc := memstats.Alloc
+
+ // Load, parse and type-check the program.
+ ctxt := build.Default // copy
+ ctxt.GOPATH = "" // disable GOPATH
+ conf := loader.Config{Build: &ctxt}
+ for _, path := range buildutil.AllPackages(conf.Build) {
+ conf.ImportWithTests(path)
+ }
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Fatalf("Load failed: %v", err)
+ }
+
+ t1 := time.Now()
+ runtime.GC()
+ runtime.ReadMemStats(&memstats)
+
+ numPkgs := len(prog.AllPackages)
+ if want := 205; numPkgs < want {
+ t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
+ }
+
+ // Dump package members.
+ if false {
+ for pkg := range prog.AllPackages {
+ fmt.Printf("Package %s:\n", pkg.Path())
+ scope := pkg.Scope()
+ qualifier := types.RelativeTo(pkg)
+ for _, name := range scope.Names() {
+ if ast.IsExported(name) {
+ fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier))
+ }
+ }
+ fmt.Println()
+ }
+ }
+
+ // Check that Test functions for io/ioutil, regexp and
+ // compress/bzip2 are all simultaneously present.
+ // (The apparent cycle formed when augmenting all three of
+ // these packages by their tests was the original motivation
+ // for reporting b/7114.)
+ //
+ // compress/bzip2.TestBitReader in bzip2_test.go imports io/ioutil
+ // io/ioutil.TestTempFile in tempfile_test.go imports regexp
+ // regexp.TestRE2Search in exec_test.go imports compress/bzip2
+ for _, test := range []struct{ pkg, fn string }{
+ {"io/ioutil", "TestTempFile"},
+ {"regexp", "TestRE2Search"},
+ {"compress/bzip2", "TestBitReader"},
+ } {
+ info := prog.Imported[test.pkg]
+ if info == nil {
+ t.Errorf("failed to load package %q", test.pkg)
+ continue
+ }
+ obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func)
+ if obj == nil {
+ t.Errorf("package %q has no func %q", test.pkg, test.fn)
+ continue
+ }
+ }
+
+ // Dump some statistics.
+
+ // determine line count
+ var lineCount int
+ prog.Fset.Iterate(func(f *token.File) bool {
+ lineCount += f.LineCount()
+ return true
+ })
+
+ t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0))
+ t.Log("#Source lines: ", lineCount)
+ t.Log("Load/parse/typecheck: ", t1.Sub(t0))
+ t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000)
+}
+
+func TestCgoOption(t *testing.T) {
+ switch runtime.GOOS {
+ // On these systems, the net and os/user packages don't use cgo
+ // or the std library is incomplete (Android).
+ case "android", "plan9", "solaris", "windows":
+ t.Skipf("no cgo or incomplete std lib on %s", runtime.GOOS)
+ }
+ // In nocgo builds (e.g. linux-amd64-nocgo),
+ // there is no "runtime/cgo" package,
+ // so cgo-generated Go files will have a failing import.
+ if !build.Default.CgoEnabled {
+ return
+ }
+ // Test that we can load cgo-using packages with
+ // CGO_ENABLED=[01], which causes go/build to select pure
+ // Go/native implementations, respectively, based on build
+ // tags.
+ //
+ // Each entry specifies a package-level object and the generic
+ // file expected to define it when cgo is disabled.
+ // When cgo is enabled, the exact file is not specified (since
+ // it varies by platform), but must differ from the generic one.
+ //
+ // The test also loads the actual file to verify that the
+ // object is indeed defined at that location.
+ for _, test := range []struct {
+ pkg, name, genericFile string
+ }{
+ {"net", "cgoLookupHost", "cgo_stub.go"},
+ {"os/user", "lookupId", "lookup_stubs.go"},
+ } {
+ ctxt := build.Default
+ for _, ctxt.CgoEnabled = range []bool{false, true} {
+ conf := loader.Config{Build: &ctxt}
+ conf.Import(test.pkg)
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("Load failed: %v", err)
+ continue
+ }
+ info := prog.Imported[test.pkg]
+ if info == nil {
+ t.Errorf("package %s not found", test.pkg)
+ continue
+ }
+ obj := info.Pkg.Scope().Lookup(test.name)
+ if obj == nil {
+ t.Errorf("no object %s.%s", test.pkg, test.name)
+ continue
+ }
+ posn := prog.Fset.Position(obj.Pos())
+ t.Logf("%s: %s (CgoEnabled=%t)", posn, obj, ctxt.CgoEnabled)
+
+ gotFile := filepath.Base(posn.Filename)
+ filesMatch := gotFile == test.genericFile
+
+ if ctxt.CgoEnabled && filesMatch {
+ t.Errorf("CGO_ENABLED=1: %s found in %s, want native file",
+ obj, gotFile)
+ } else if !ctxt.CgoEnabled && !filesMatch {
+ t.Errorf("CGO_ENABLED=0: %s found in %s, want %s",
+ obj, gotFile, test.genericFile)
+ }
+
+ // Load the file and check the object is declared at the right place.
+ b, err := ioutil.ReadFile(posn.Filename)
+ if err != nil {
+ t.Errorf("can't read %s: %s", posn.Filename, err)
+ continue
+ }
+ line := string(bytes.Split(b, []byte("\n"))[posn.Line-1])
+ ident := line[posn.Column-1:]
+ if !strings.HasPrefix(ident, test.name) {
+ t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, ident)
+ }
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/loader/stdlib_test.go b/go/src/golang.org/x/tools/go/loader/stdlib_test.go
index b62ecfb..46cd8a2 100644
--- a/go/src/golang.org/x/tools/go/loader/stdlib_test.go
+++ b/go/src/golang.org/x/tools/go/loader/stdlib_test.go
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package loader_test
// This file enumerates all packages beneath $GOROOT, loads them, plus
// their external tests if any, runs the type checker on them, and
// prints some summary information.
-//
-// Run test with GOMAXPROCS=8.
import (
"bytes"
@@ -16,6 +16,7 @@
"go/ast"
"go/build"
"go/token"
+ "go/types"
"io/ioutil"
"path/filepath"
"runtime"
@@ -25,13 +26,15 @@
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
)
func TestStdlib(t *testing.T) {
if runtime.GOOS == "android" {
t.Skipf("incomplete std lib on %s", runtime.GOOS)
}
+ if testing.Short() {
+ t.Skip("skipping in short mode; uses tons of memory (golang.org/issue/14113)")
+ }
runtime.GC()
t0 := time.Now()
@@ -118,6 +121,9 @@
}
func TestCgoOption(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode; uses tons of memory (golang.org/issue/14113)")
+ }
switch runtime.GOOS {
// On these systems, the net and os/user packages don't use cgo
// or the std library is incomplete (Android).
diff --git a/go/src/golang.org/x/tools/go/loader/util.go b/go/src/golang.org/x/tools/go/loader/util.go
index 3b64856..7f38dd7 100644
--- a/go/src/golang.org/x/tools/go/loader/util.go
+++ b/go/src/golang.org/x/tools/go/loader/util.go
@@ -88,7 +88,7 @@
return parsed, errors
}
-// scanImports returns the set of all package import paths from all
+// scanImports returns the set of all import paths from all
// import specs in the specified files.
func scanImports(files []*ast.File) map[string]bool {
imports := make(map[string]bool)
@@ -103,8 +103,8 @@
if err != nil {
continue // quietly ignore the error
}
- if path == "C" || path == "unsafe" {
- continue // skip pseudo packages
+ if path == "C" {
+ continue // skip pseudopackage
}
imports[path] = true
}
diff --git a/go/src/golang.org/x/tools/go/pointer/analysis.go b/go/src/golang.org/x/tools/go/pointer/analysis.go
index d02e536..9f3476c 100644
--- a/go/src/golang.org/x/tools/go/pointer/analysis.go
+++ b/go/src/golang.org/x/tools/go/pointer/analysis.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
// This file defines the main datatypes and Analyze function of the pointer analysis.
@@ -9,6 +11,7 @@
import (
"fmt"
"go/token"
+ "go/types"
"io"
"os"
"reflect"
@@ -18,7 +21,6 @@
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -254,17 +256,17 @@
for _, pkg := range a.prog.AllPackages() {
// (This only checks that the package scope is complete,
// not that func bodies exist, but it's a good signal.)
- if !pkg.Object.Complete() {
- return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Object.Path())
+ if !pkg.Pkg.Complete() {
+ return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Pkg.Path())
}
}
if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
- rV := reflect.Object.Scope().Lookup("Value")
+ rV := reflect.Pkg.Scope().Lookup("Value")
a.reflectValueObj = rV
a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call")
- a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named)
- a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
+ a.reflectType = reflect.Pkg.Scope().Lookup("Type").Type().(*types.Named)
+ a.reflectRtypeObj = reflect.Pkg.Scope().Lookup("rtype")
a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
// Override flattening of reflect.Value, treating it like a basic type.
diff --git a/go/src/golang.org/x/tools/go/pointer/analysis14.go b/go/src/golang.org/x/tools/go/pointer/analysis14.go
new file mode 100644
index 0000000..70761fe
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/analysis14.go
@@ -0,0 +1,449 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+// This file defines the main datatypes and Analyze function of the pointer analysis.
+
+import (
+ "fmt"
+ "go/token"
+ "io"
+ "os"
+ "reflect"
+ "runtime"
+ "runtime/debug"
+ "sort"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+const (
+ // optimization options; enable all when committing
+ optRenumber = true // enable renumbering optimization (makes logs hard to read)
+ optHVN = true // enable pointer equivalence via Hash-Value Numbering
+
+ // debugging options; disable all when committing
+ debugHVN = false // enable assertions in HVN
+ debugHVNVerbose = false // enable extra HVN logging
+ debugHVNCrossCheck = false // run solver with/without HVN and compare (caveats below)
+ debugTimers = false // show running time of each phase
+)
+
+// object.flags bitmask values.
+const (
+ otTagged = 1 << iota // type-tagged object
+ otIndirect // type-tagged object with indirect payload
+ otFunction // function object
+)
+
+// An object represents a contiguous block of memory to which some
+// (generalized) pointer may point.
+//
+// (Note: most variables called 'obj' are not *objects but nodeids
+// such that a.nodes[obj].obj != nil.)
+//
+type object struct {
+ // flags is a bitset of the node type (ot*) flags defined above.
+ flags uint32
+
+ // Number of following nodes belonging to the same "object"
+ // allocation. Zero for all other nodes.
+ size uint32
+
+ // data describes this object; it has one of these types:
+ //
+ // ssa.Value for an object allocated by an SSA operation.
+ // types.Type for an rtype instance object or *rtype-tagged object.
+ // string for an instrinsic object, e.g. the array behind os.Args.
+ // nil for an object allocated by an instrinsic.
+ // (cgn provides the identity of the intrinsic.)
+ data interface{}
+
+ // The call-graph node (=context) in which this object was allocated.
+ // May be nil for global objects: Global, Const, some Functions.
+ cgn *cgnode
+}
+
+// nodeid denotes a node.
+// It is an index within analysis.nodes.
+// We use small integers, not *node pointers, for many reasons:
+// - they are smaller on 64-bit systems.
+// - sets of them can be represented compactly in bitvectors or BDDs.
+// - order matters; a field offset can be computed by simple addition.
+type nodeid uint32
+
+// A node is an equivalence class of memory locations.
+// Nodes may be pointers, pointed-to locations, neither, or both.
+//
+// Nodes that are pointed-to locations ("labels") have an enclosing
+// object (see analysis.enclosingObject).
+//
+type node struct {
+ // If non-nil, this node is the start of an object
+ // (addressable memory location).
+ // The following obj.size nodes implicitly belong to the object;
+ // they locate their object by scanning back.
+ obj *object
+
+ // The type of the field denoted by this node. Non-aggregate,
+ // unless this is an tagged.T node (i.e. the thing
+ // pointed to by an interface) in which case typ is that type.
+ typ types.Type
+
+ // subelement indicates which directly embedded subelement of
+ // an object of aggregate type (struct, tuple, array) this is.
+ subelement *fieldInfo // e.g. ".a.b[*].c"
+
+ // Solver state for the canonical node of this pointer-
+ // equivalence class. Each node is created with its own state
+ // but they become shared after HVN.
+ solve *solverState
+}
+
+// An analysis instance holds the state of a single pointer analysis problem.
+type analysis struct {
+ config *Config // the client's control/observer interface
+ prog *ssa.Program // the program being analyzed
+ log io.Writer // log stream; nil to disable
+ panicNode nodeid // sink for panic, source for recover
+ nodes []*node // indexed by nodeid
+ flattenMemo map[types.Type][]*fieldInfo // memoization of flatten()
+ trackTypes map[types.Type]bool // memoization of shouldTrack()
+ constraints []constraint // set of constraints
+ cgnodes []*cgnode // all cgnodes
+ genq []*cgnode // queue of functions to generate constraints for
+ intrinsics map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns
+ globalval map[ssa.Value]nodeid // node for each global ssa.Value
+ globalobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
+ localval map[ssa.Value]nodeid // node for each local ssa.Value
+ localobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
+ atFuncs map[*ssa.Function]bool // address-taken functions (for presolver)
+ mapValues []nodeid // values of makemap objects (indirect in HVN)
+ work nodeset // solver's worklist
+ result *Result // results of the analysis
+ track track // pointerlike types whose aliasing we track
+ deltaSpace []int // working space for iterating over PTS deltas
+
+ // Reflection & intrinsics:
+ hasher typeutil.Hasher // cache of type hashes
+ reflectValueObj types.Object // type symbol for reflect.Value (if present)
+ reflectValueCall *ssa.Function // (reflect.Value).Call
+ reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
+ reflectRtypePtr *types.Pointer // *reflect.rtype
+ reflectType *types.Named // reflect.Type
+ rtypes typeutil.Map // nodeid of canonical *rtype-tagged object for type T
+ reflectZeros typeutil.Map // nodeid of canonical T-tagged object for zero value
+ runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer
+}
+
+// enclosingObj returns the first node of the addressable memory
+// object that encloses node id. Panic ensues if that node does not
+// belong to any object.
+func (a *analysis) enclosingObj(id nodeid) nodeid {
+ // Find previous node with obj != nil.
+ for i := id; i >= 0; i-- {
+ n := a.nodes[i]
+ if obj := n.obj; obj != nil {
+ if i+nodeid(obj.size) <= id {
+ break // out of bounds
+ }
+ return i
+ }
+ }
+ panic("node has no enclosing object")
+}
+
+// labelFor returns the Label for node id.
+// Panic ensues if that node is not addressable.
+func (a *analysis) labelFor(id nodeid) *Label {
+ return &Label{
+ obj: a.nodes[a.enclosingObj(id)].obj,
+ subelement: a.nodes[id].subelement,
+ }
+}
+
+func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) {
+ msg := fmt.Sprintf(format, args...)
+ if a.log != nil {
+ fmt.Fprintf(a.log, "%s: warning: %s\n", a.prog.Fset.Position(pos), msg)
+ }
+ a.result.Warnings = append(a.result.Warnings, Warning{pos, msg})
+}
+
+// computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries.
+func (a *analysis) computeTrackBits() {
+ var queryTypes []types.Type
+ for v := range a.config.Queries {
+ queryTypes = append(queryTypes, v.Type())
+ }
+ for v := range a.config.IndirectQueries {
+ queryTypes = append(queryTypes, mustDeref(v.Type()))
+ }
+ for _, t := range queryTypes {
+ switch t.Underlying().(type) {
+ case *types.Chan:
+ a.track |= trackChan
+ case *types.Map:
+ a.track |= trackMap
+ case *types.Pointer:
+ a.track |= trackPtr
+ case *types.Slice:
+ a.track |= trackSlice
+ case *types.Interface:
+ a.track = trackAll
+ return
+ }
+ if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) {
+ a.track = trackAll
+ return
+ }
+ }
+}
+
+// Analyze runs the pointer analysis with the scope and options
+// specified by config, and returns the (synthetic) root of the callgraph.
+//
+// Pointer analysis of a transitively closed well-typed program should
+// always succeed. An error can occur only due to an internal bug.
+//
+func Analyze(config *Config) (result *Result, err error) {
+ if config.Mains == nil {
+ return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)")
+ }
+ defer func() {
+ if p := recover(); p != nil {
+ err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p)
+ fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:")
+ debug.PrintStack()
+ }
+ }()
+
+ a := &analysis{
+ config: config,
+ log: config.Log,
+ prog: config.prog(),
+ globalval: make(map[ssa.Value]nodeid),
+ globalobj: make(map[ssa.Value]nodeid),
+ flattenMemo: make(map[types.Type][]*fieldInfo),
+ trackTypes: make(map[types.Type]bool),
+ atFuncs: make(map[*ssa.Function]bool),
+ hasher: typeutil.MakeHasher(),
+ intrinsics: make(map[*ssa.Function]intrinsic),
+ result: &Result{
+ Queries: make(map[ssa.Value]Pointer),
+ IndirectQueries: make(map[ssa.Value]Pointer),
+ },
+ deltaSpace: make([]int, 0, 100),
+ }
+
+ if false {
+ a.log = os.Stderr // for debugging crashes; extremely verbose
+ }
+
+ if a.log != nil {
+ fmt.Fprintln(a.log, "==== Starting analysis")
+ }
+
+ // Pointer analysis requires a complete program for soundness.
+ // Check to prevent accidental misconfiguration.
+ for _, pkg := range a.prog.AllPackages() {
+ // (This only checks that the package scope is complete,
+ // not that func bodies exist, but it's a good signal.)
+ if !pkg.Pkg.Complete() {
+ return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Pkg.Path())
+ }
+ }
+
+ if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
+ rV := reflect.Pkg.Scope().Lookup("Value")
+ a.reflectValueObj = rV
+ a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call")
+ a.reflectType = reflect.Pkg.Scope().Lookup("Type").Type().(*types.Named)
+ a.reflectRtypeObj = reflect.Pkg.Scope().Lookup("rtype")
+ a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
+
+ // Override flattening of reflect.Value, treating it like a basic type.
+ tReflectValue := a.reflectValueObj.Type()
+ a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}}
+
+ // Override shouldTrack of reflect.Value and *reflect.rtype.
+ // Always track pointers of these types.
+ a.trackTypes[tReflectValue] = true
+ a.trackTypes[a.reflectRtypePtr] = true
+
+ a.rtypes.SetHasher(a.hasher)
+ a.reflectZeros.SetHasher(a.hasher)
+ }
+ if runtime := a.prog.ImportedPackage("runtime"); runtime != nil {
+ a.runtimeSetFinalizer = runtime.Func("SetFinalizer")
+ }
+ a.computeTrackBits()
+
+ a.generate()
+ a.showCounts()
+
+ if optRenumber {
+ a.renumber()
+ }
+
+ N := len(a.nodes) // excludes solver-created nodes
+
+ if optHVN {
+ if debugHVNCrossCheck {
+ // Cross-check: run the solver once without
+ // optimization, once with, and compare the
+ // solutions.
+ savedConstraints := a.constraints
+
+ a.solve()
+ a.dumpSolution("A.pts", N)
+
+ // Restore.
+ a.constraints = savedConstraints
+ for _, n := range a.nodes {
+ n.solve = new(solverState)
+ }
+ a.nodes = a.nodes[:N]
+
+ // rtypes is effectively part of the solver state.
+ a.rtypes = typeutil.Map{}
+ a.rtypes.SetHasher(a.hasher)
+ }
+
+ a.hvn()
+ }
+
+ if debugHVNCrossCheck {
+ runtime.GC()
+ runtime.GC()
+ }
+
+ a.solve()
+
+ // Compare solutions.
+ if optHVN && debugHVNCrossCheck {
+ a.dumpSolution("B.pts", N)
+
+ if !diff("A.pts", "B.pts") {
+ return nil, fmt.Errorf("internal error: optimization changed solution")
+ }
+ }
+
+ // Create callgraph.Nodes in deterministic order.
+ if cg := a.result.CallGraph; cg != nil {
+ for _, caller := range a.cgnodes {
+ cg.CreateNode(caller.fn)
+ }
+ }
+
+ // Add dynamic edges to call graph.
+ var space [100]int
+ for _, caller := range a.cgnodes {
+ for _, site := range caller.sites {
+ for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) {
+ a.callEdge(caller, site, nodeid(callee))
+ }
+ }
+ }
+
+ return a.result, nil
+}
+
+// callEdge is called for each edge in the callgraph.
+// calleeid is the callee's object node (has otFunction flag).
+//
+func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) {
+ obj := a.nodes[calleeid].obj
+ if obj.flags&otFunction == 0 {
+ panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid))
+ }
+ callee := obj.cgn
+
+ if cg := a.result.CallGraph; cg != nil {
+ // TODO(adonovan): opt: I would expect duplicate edges
+ // (to wrappers) to arise due to the elimination of
+ // context information, but I haven't observed any.
+ // Understand this better.
+ callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn))
+ }
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee)
+ }
+
+ // Warn about calls to non-intrinsic external functions.
+ // TODO(adonovan): de-dup these messages.
+ if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil {
+ a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn)
+ a.warnf(fn.Pos(), " (declared here)")
+ }
+}
+
+// dumpSolution writes the PTS solution to the specified file.
+//
+// It only dumps the nodes that existed before solving. The order in
+// which solver-created nodes are created depends on pre-solver
+// optimization, so we can't include them in the cross-check.
+//
+func (a *analysis) dumpSolution(filename string, N int) {
+ f, err := os.Create(filename)
+ if err != nil {
+ panic(err)
+ }
+ for id, n := range a.nodes[:N] {
+ if _, err := fmt.Fprintf(f, "pts(n%d) = {", id); err != nil {
+ panic(err)
+ }
+ var sep string
+ for _, l := range n.solve.pts.AppendTo(a.deltaSpace) {
+ if l >= N {
+ break
+ }
+ fmt.Fprintf(f, "%s%d", sep, l)
+ sep = " "
+ }
+ fmt.Fprintf(f, "} : %s\n", n.typ)
+ }
+ if err := f.Close(); err != nil {
+ panic(err)
+ }
+}
+
+// showCounts logs the size of the constraint system. A typical
+// optimized distribution is 65% copy, 13% load, 11% addr, 5%
+// offsetAddr, 4% store, 2% others.
+//
+func (a *analysis) showCounts() {
+ if a.log != nil {
+ counts := make(map[reflect.Type]int)
+ for _, c := range a.constraints {
+ counts[reflect.TypeOf(c)]++
+ }
+ fmt.Fprintf(a.log, "# constraints:\t%d\n", len(a.constraints))
+ var lines []string
+ for t, n := range counts {
+ line := fmt.Sprintf("%7d (%2d%%)\t%s", n, 100*n/len(a.constraints), t)
+ lines = append(lines, line)
+ }
+ sort.Sort(sort.Reverse(sort.StringSlice(lines)))
+ for _, line := range lines {
+ fmt.Fprintf(a.log, "\t%s\n", line)
+ }
+
+ fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
+
+ // Show number of pointer equivalence classes.
+ m := make(map[*solverState]bool)
+ for _, n := range a.nodes {
+ m[n.solve] = true
+ }
+ fmt.Fprintf(a.log, "# ptsets:\t%d\n", len(m))
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/api.go b/go/src/golang.org/x/tools/go/pointer/api.go
index 8f9ae0a..077ef56 100644
--- a/go/src/golang.org/x/tools/go/pointer/api.go
+++ b/go/src/golang.org/x/tools/go/pointer/api.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
import (
diff --git a/go/src/golang.org/x/tools/go/pointer/api14.go b/go/src/golang.org/x/tools/go/pointer/api14.go
new file mode 100644
index 0000000..5481732
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/api14.go
@@ -0,0 +1,247 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "io"
+
+ "golang.org/x/tools/container/intsets"
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// A Config formulates a pointer analysis problem for Analyze().
+type Config struct {
+ // Mains contains the set of 'main' packages to analyze
+ // Clients must provide the analysis with at least one
+ // package defining a main() function.
+ //
+ // Non-main packages in the ssa.Program that are not
+ // dependencies of any main package may still affect the
+ // analysis result, because they contribute runtime types and
+ // thus methods.
+ // TODO(adonovan): investigate whether this is desirable.
+ Mains []*ssa.Package
+
+ // Reflection determines whether to handle reflection
+ // operators soundly, which is currently rather slow since it
+ // causes constraint to be generated during solving
+ // proportional to the number of constraint variables, which
+ // has not yet been reduced by presolver optimisation.
+ Reflection bool
+
+ // BuildCallGraph determines whether to construct a callgraph.
+ // If enabled, the graph will be available in Result.CallGraph.
+ BuildCallGraph bool
+
+ // The client populates Queries[v] or IndirectQueries[v]
+ // for each ssa.Value v of interest, to request that the
+ // points-to sets pts(v) or pts(*v) be computed. If the
+ // client needs both points-to sets, v may appear in both
+ // maps.
+ //
+ // (IndirectQueries is typically used for Values corresponding
+ // to source-level lvalues, e.g. an *ssa.Global.)
+ //
+ // The analysis populates the corresponding
+ // Result.{Indirect,}Queries map when it creates the pointer
+ // variable for v or *v. Upon completion the client can
+ // inspect that map for the results.
+ //
+ // TODO(adonovan): this API doesn't scale well for batch tools
+ // that want to dump the entire solution. Perhaps optionally
+ // populate a map[*ssa.DebugRef]Pointer in the Result, one
+ // entry per source expression.
+ //
+ Queries map[ssa.Value]struct{}
+ IndirectQueries map[ssa.Value]struct{}
+
+ // If Log is non-nil, log messages are written to it.
+ // Logging is extremely verbose.
+ Log io.Writer
+}
+
+type track uint32
+
+const (
+ trackChan track = 1 << iota // track 'chan' references
+ trackMap // track 'map' references
+ trackPtr // track regular pointers
+ trackSlice // track slice references
+
+ trackAll = ^track(0)
+)
+
+// AddQuery adds v to Config.Queries.
+// Precondition: CanPoint(v.Type()).
+// TODO(adonovan): consider returning a new Pointer for this query,
+// which will be initialized during analysis. That avoids the needs
+// for the corresponding ssa.Value-keyed maps in Config and Result.
+func (c *Config) AddQuery(v ssa.Value) {
+ if !CanPoint(v.Type()) {
+ panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type()))
+ }
+ if c.Queries == nil {
+ c.Queries = make(map[ssa.Value]struct{})
+ }
+ c.Queries[v] = struct{}{}
+}
+
+// AddQuery adds v to Config.IndirectQueries.
+// Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()).
+func (c *Config) AddIndirectQuery(v ssa.Value) {
+ if c.IndirectQueries == nil {
+ c.IndirectQueries = make(map[ssa.Value]struct{})
+ }
+ if !CanPoint(mustDeref(v.Type())) {
+ panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type()))
+ }
+ c.IndirectQueries[v] = struct{}{}
+}
+
+func (c *Config) prog() *ssa.Program {
+ for _, main := range c.Mains {
+ return main.Prog
+ }
+ panic("empty scope")
+}
+
+type Warning struct {
+ Pos token.Pos
+ Message string
+}
+
+// A Result contains the results of a pointer analysis.
+//
+// See Config for how to request the various Result components.
+//
+type Result struct {
+ CallGraph *callgraph.Graph // discovered call graph
+ Queries map[ssa.Value]Pointer // pts(v) for each v in Config.Queries.
+ IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries.
+ Warnings []Warning // warnings of unsoundness
+}
+
+// A Pointer is an equivalence class of pointer-like values.
+//
+// A Pointer doesn't have a unique type because pointers of distinct
+// types may alias the same object.
+//
+type Pointer struct {
+ a *analysis
+ n nodeid
+}
+
+// A PointsToSet is a set of labels (locations or allocations).
+type PointsToSet struct {
+ a *analysis // may be nil if pts is nil
+ pts *nodeset
+}
+
+func (s PointsToSet) String() string {
+ var buf bytes.Buffer
+ buf.WriteByte('[')
+ if s.pts != nil {
+ var space [50]int
+ for i, l := range s.pts.AppendTo(space[:0]) {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(s.a.labelFor(nodeid(l)).String())
+ }
+ }
+ buf.WriteByte(']')
+ return buf.String()
+}
+
+// PointsTo returns the set of labels that this points-to set
+// contains.
+func (s PointsToSet) Labels() []*Label {
+ var labels []*Label
+ if s.pts != nil {
+ var space [50]int
+ for _, l := range s.pts.AppendTo(space[:0]) {
+ labels = append(labels, s.a.labelFor(nodeid(l)))
+ }
+ }
+ return labels
+}
+
+// If this PointsToSet came from a Pointer of interface kind
+// or a reflect.Value, DynamicTypes returns the set of dynamic
+// types that it may contain. (For an interface, they will
+// always be concrete types.)
+//
+// The result is a mapping whose keys are the dynamic types to which
+// it may point. For each pointer-like key type, the corresponding
+// map value is the PointsToSet for pointers of that type.
+//
+// The result is empty unless CanHaveDynamicTypes(T).
+//
+func (s PointsToSet) DynamicTypes() *typeutil.Map {
+ var tmap typeutil.Map
+ tmap.SetHasher(s.a.hasher)
+ if s.pts != nil {
+ var space [50]int
+ for _, x := range s.pts.AppendTo(space[:0]) {
+ ifaceObjId := nodeid(x)
+ if !s.a.isTaggedObject(ifaceObjId) {
+ continue // !CanHaveDynamicTypes(tDyn)
+ }
+ tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
+ if indirect {
+ panic("indirect tagged object") // implement later
+ }
+ pts, ok := tmap.At(tDyn).(PointsToSet)
+ if !ok {
+ pts = PointsToSet{s.a, new(nodeset)}
+ tmap.Set(tDyn, pts)
+ }
+ pts.pts.addAll(&s.a.nodes[v].solve.pts)
+ }
+ }
+ return &tmap
+}
+
+// Intersects reports whether this points-to set and the
+// argument points-to set contain common members.
+func (x PointsToSet) Intersects(y PointsToSet) bool {
+ if x.pts == nil || y.pts == nil {
+ return false
+ }
+ // This takes Θ(|x|+|y|) time.
+ var z intsets.Sparse
+ z.Intersection(&x.pts.Sparse, &y.pts.Sparse)
+ return !z.IsEmpty()
+}
+
+func (p Pointer) String() string {
+ return fmt.Sprintf("n%d", p.n)
+}
+
+// PointsTo returns the points-to set of this pointer.
+func (p Pointer) PointsTo() PointsToSet {
+ if p.n == 0 {
+ return PointsToSet{}
+ }
+ return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts}
+}
+
+// MayAlias reports whether the receiver pointer may alias
+// the argument pointer.
+func (p Pointer) MayAlias(q Pointer) bool {
+ return p.PointsTo().Intersects(q.PointsTo())
+}
+
+// DynamicTypes returns p.PointsTo().DynamicTypes().
+func (p Pointer) DynamicTypes() *typeutil.Map {
+ return p.PointsTo().DynamicTypes()
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/constraint.go b/go/src/golang.org/x/tools/go/pointer/constraint.go
index e6371cc..ea44287 100644
--- a/go/src/golang.org/x/tools/go/pointer/constraint.go
+++ b/go/src/golang.org/x/tools/go/pointer/constraint.go
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
-import (
- "golang.org/x/tools/go/types"
-)
+import "go/types"
type constraint interface {
// For a complex constraint, returns the nodeid of the pointer
diff --git a/go/src/golang.org/x/tools/go/pointer/constraint14.go b/go/src/golang.org/x/tools/go/pointer/constraint14.go
new file mode 100644
index 0000000..d18064c
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/constraint14.go
@@ -0,0 +1,153 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+import (
+ "golang.org/x/tools/go/types"
+)
+
+type constraint interface {
+ // For a complex constraint, returns the nodeid of the pointer
+ // to which it is attached. For addr and copy, returns dst.
+ ptr() nodeid
+
+ // renumber replaces each nodeid n in the constraint by mapping[n].
+ renumber(mapping []nodeid)
+
+ // presolve is a hook for constraint-specific behaviour during
+ // pre-solver optimization. Typical implementations mark as
+ // indirect the set of nodes to which the solver will add copy
+ // edges or PTS labels.
+ presolve(h *hvn)
+
+ // solve is called for complex constraints when the pts for
+ // the node to which they are attached has changed.
+ solve(a *analysis, delta *nodeset)
+
+ String() string
+}
+
+// dst = &src
+// pts(dst) ⊇ {src}
+// A base constraint used to initialize the solver's pt sets
+type addrConstraint struct {
+ dst nodeid // (ptr)
+ src nodeid
+}
+
+func (c *addrConstraint) ptr() nodeid { return c.dst }
+func (c *addrConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// dst = src
+// A simple constraint represented directly as a copyTo graph edge.
+type copyConstraint struct {
+ dst nodeid // (ptr)
+ src nodeid
+}
+
+func (c *copyConstraint) ptr() nodeid { return c.dst }
+func (c *copyConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// dst = src[offset]
+// A complex constraint attached to src (the pointer)
+type loadConstraint struct {
+ offset uint32
+ dst nodeid
+ src nodeid // (ptr)
+}
+
+func (c *loadConstraint) ptr() nodeid { return c.src }
+func (c *loadConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// dst[offset] = src
+// A complex constraint attached to dst (the pointer)
+type storeConstraint struct {
+ offset uint32
+ dst nodeid // (ptr)
+ src nodeid
+}
+
+func (c *storeConstraint) ptr() nodeid { return c.dst }
+func (c *storeConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// dst = &src.f or dst = &src[0]
+// A complex constraint attached to dst (the pointer)
+type offsetAddrConstraint struct {
+ offset uint32
+ dst nodeid
+ src nodeid // (ptr)
+}
+
+func (c *offsetAddrConstraint) ptr() nodeid { return c.src }
+func (c *offsetAddrConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// dst = src.(typ) where typ is an interface
+// A complex constraint attached to src (the interface).
+// No representation change: pts(dst) and pts(src) contains tagged objects.
+type typeFilterConstraint struct {
+ typ types.Type // an interface type
+ dst nodeid
+ src nodeid // (ptr)
+}
+
+func (c *typeFilterConstraint) ptr() nodeid { return c.src }
+func (c *typeFilterConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// dst = src.(typ) where typ is a concrete type
+// A complex constraint attached to src (the interface).
+//
+// If exact, only tagged objects identical to typ are untagged.
+// If !exact, tagged objects assignable to typ are untagged too.
+// The latter is needed for various reflect operators, e.g. Send.
+//
+// This entails a representation change:
+// pts(src) contains tagged objects,
+// pts(dst) contains their payloads.
+type untagConstraint struct {
+ typ types.Type // a concrete type
+ dst nodeid
+ src nodeid // (ptr)
+ exact bool
+}
+
+func (c *untagConstraint) ptr() nodeid { return c.src }
+func (c *untagConstraint) renumber(mapping []nodeid) {
+ c.dst = mapping[c.dst]
+ c.src = mapping[c.src]
+}
+
+// src.method(params...)
+// A complex constraint attached to iface.
+type invokeConstraint struct {
+ method *types.Func // the abstract method
+ iface nodeid // (ptr) the interface
+ params nodeid // the start of the identity/params/results block
+}
+
+func (c *invokeConstraint) ptr() nodeid { return c.iface }
+func (c *invokeConstraint) renumber(mapping []nodeid) {
+ c.iface = mapping[c.iface]
+ c.params = mapping[c.params]
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/doc.go b/go/src/golang.org/x/tools/go/pointer/doc.go
index 22e569c..17d98cb 100644
--- a/go/src/golang.org/x/tools/go/pointer/doc.go
+++ b/go/src/golang.org/x/tools/go/pointer/doc.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
/*
Package pointer implements Andersen's analysis, an inclusion-based
diff --git a/go/src/golang.org/x/tools/go/pointer/doc14.go b/go/src/golang.org/x/tools/go/pointer/doc14.go
new file mode 100644
index 0000000..fe5ad60
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/doc14.go
@@ -0,0 +1,612 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+/*
+
+Package pointer implements Andersen's analysis, an inclusion-based
+pointer analysis algorithm first described in (Andersen, 1994).
+
+A pointer analysis relates every pointer expression in a whole program
+to the set of memory locations to which it might point. This
+information can be used to construct a call graph of the program that
+precisely represents the destinations of dynamic function and method
+calls. It can also be used to determine, for example, which pairs of
+channel operations operate on the same channel.
+
+The package allows the client to request a set of expressions of
+interest for which the points-to information will be returned once the
+analysis is complete. In addition, the client may request that a
+callgraph is constructed. The example program in example_test.go
+demonstrates both of these features. Clients should not request more
+information than they need since it may increase the cost of the
+analysis significantly.
+
+
+CLASSIFICATION
+
+Our algorithm is INCLUSION-BASED: the points-to sets for x and y will
+be related by pts(y) ⊇ pts(x) if the program contains the statement
+y = x.
+
+It is FLOW-INSENSITIVE: it ignores all control flow constructs and the
+order of statements in a program. It is therefore a "MAY ALIAS"
+analysis: its facts are of the form "P may/may not point to L",
+not "P must point to L".
+
+It is FIELD-SENSITIVE: it builds separate points-to sets for distinct
+fields, such as x and y in struct { x, y *int }.
+
+It is mostly CONTEXT-INSENSITIVE: most functions are analyzed once,
+so values can flow in at one call to the function and return out at
+another. Only some smaller functions are analyzed with consideration
+of their calling context.
+
+It has a CONTEXT-SENSITIVE HEAP: objects are named by both allocation
+site and context, so the objects returned by two distinct calls to f:
+ func f() *T { return new(T) }
+are distinguished up to the limits of the calling context.
+
+It is a WHOLE PROGRAM analysis: it requires SSA-form IR for the
+complete Go program and summaries for native code.
+
+See the (Hind, PASTE'01) survey paper for an explanation of these terms.
+
+
+SOUNDNESS
+
+The analysis is fully sound when invoked on pure Go programs that do not
+use reflection or unsafe.Pointer conversions. In other words, if there
+is any possible execution of the program in which pointer P may point to
+object O, the analysis will report that fact.
+
+
+REFLECTION
+
+By default, the "reflect" library is ignored by the analysis, as if all
+its functions were no-ops, but if the client enables the Reflection flag,
+the analysis will make a reasonable attempt to model the effects of
+calls into this library. However, this comes at a significant
+performance cost, and not all features of that library are yet
+implemented. In addition, some simplifying approximations must be made
+to ensure that the analysis terminates; for example, reflection can be
+used to construct an infinite set of types and values of those types,
+but the analysis arbitrarily bounds the depth of such types.
+
+Most but not all reflection operations are supported.
+In particular, addressable reflect.Values are not yet implemented, so
+operations such as (reflect.Value).Set have no analytic effect.
+
+
+UNSAFE POINTER CONVERSIONS
+
+The pointer analysis makes no attempt to understand aliasing between the
+operand x and result y of an unsafe.Pointer conversion:
+ y = (*T)(unsafe.Pointer(x))
+It is as if the conversion allocated an entirely new object:
+ y = new(T)
+
+
+NATIVE CODE
+
+The analysis cannot model the aliasing effects of functions written in
+languages other than Go, such as runtime intrinsics in C or assembly, or
+code accessed via cgo. The result is as if such functions are no-ops.
+However, various important intrinsics are understood by the analysis,
+along with built-ins such as append.
+
+The analysis currently provides no way for users to specify the aliasing
+effects of native code.
+
+------------------------------------------------------------------------
+
+IMPLEMENTATION
+
+The remaining documentation is intended for package maintainers and
+pointer analysis specialists. Maintainers should have a solid
+understanding of the referenced papers (especially those by H&L and PKH)
+before making making significant changes.
+
+The implementation is similar to that described in (Pearce et al,
+PASTE'04). Unlike many algorithms which interleave constraint
+generation and solving, constructing the callgraph as they go, this
+implementation for the most part observes a phase ordering (generation
+before solving), with only simple (copy) constraints being generated
+during solving. (The exception is reflection, which creates various
+constraints during solving as new types flow to reflect.Value
+operations.) This improves the traction of presolver optimisations,
+but imposes certain restrictions, e.g. potential context sensitivity
+is limited since all variants must be created a priori.
+
+
+TERMINOLOGY
+
+A type is said to be "pointer-like" if it is a reference to an object.
+Pointer-like types include pointers and also interfaces, maps, channels,
+functions and slices.
+
+We occasionally use C's x->f notation to distinguish the case where x
+is a struct pointer from x.f where is a struct value.
+
+Pointer analysis literature (and our comments) often uses the notation
+dst=*src+offset to mean something different than what it means in Go.
+It means: for each node index p in pts(src), the node index p+offset is
+in pts(dst). Similarly *dst+offset=src is used for store constraints
+and dst=src+offset for offset-address constraints.
+
+
+NODES
+
+Nodes are the key datastructure of the analysis, and have a dual role:
+they represent both constraint variables (equivalence classes of
+pointers) and members of points-to sets (things that can be pointed
+at, i.e. "labels").
+
+Nodes are naturally numbered. The numbering enables compact
+representations of sets of nodes such as bitvectors (or BDDs); and the
+ordering enables a very cheap way to group related nodes together. For
+example, passing n parameters consists of generating n parallel
+constraints from caller+i to callee+i for 0<=i<n.
+
+The zero nodeid means "not a pointer". For simplicity, we generate flow
+constraints even for non-pointer types such as int. The pointer
+equivalence (PE) presolver optimization detects which variables cannot
+point to anything; this includes not only all variables of non-pointer
+types (such as int) but also variables of pointer-like types if they are
+always nil, or are parameters to a function that is never called.
+
+Each node represents a scalar part of a value or object.
+Aggregate types (structs, tuples, arrays) are recursively flattened
+out into a sequential list of scalar component types, and all the
+elements of an array are represented by a single node. (The
+flattening of a basic type is a list containing a single node.)
+
+Nodes are connected into a graph with various kinds of labelled edges:
+simple edges (or copy constraints) represent value flow. Complex
+edges (load, store, etc) trigger the creation of new simple edges
+during the solving phase.
+
+
+OBJECTS
+
+Conceptually, an "object" is a contiguous sequence of nodes denoting
+an addressable location: something that a pointer can point to. The
+first node of an object has a non-nil obj field containing information
+about the allocation: its size, context, and ssa.Value.
+
+Objects include:
+ - functions and globals;
+ - variable allocations in the stack frame or heap;
+ - maps, channels and slices created by calls to make();
+ - allocations to construct an interface;
+ - allocations caused by conversions, e.g. []byte(str).
+ - arrays allocated by calls to append();
+
+Many objects have no Go types. For example, the func, map and chan type
+kinds in Go are all varieties of pointers, but their respective objects
+are actual functions (executable code), maps (hash tables), and channels
+(synchronized queues). Given the way we model interfaces, they too are
+pointers to "tagged" objects with no Go type. And an *ssa.Global denotes
+the address of a global variable, but the object for a Global is the
+actual data. So, the types of an ssa.Value that creates an object is
+"off by one indirection": a pointer to the object.
+
+The individual nodes of an object are sometimes referred to as "labels".
+
+For uniformity, all objects have a non-zero number of fields, even those
+of the empty type struct{}. (All arrays are treated as if of length 1,
+so there are no empty arrays. The empty tuple is never address-taken,
+so is never an object.)
+
+
+TAGGED OBJECTS
+
+An tagged object has the following layout:
+
+ T -- obj.flags ⊇ {otTagged}
+ v
+ ...
+
+The T node's typ field is the dynamic type of the "payload": the value
+v which follows, flattened out. The T node's obj has the otTagged
+flag.
+
+Tagged objects are needed when generalizing across types: interfaces,
+reflect.Values, reflect.Types. Each of these three types is modelled
+as a pointer that exclusively points to tagged objects.
+
+Tagged objects may be indirect (obj.flags ⊇ {otIndirect}) meaning that
+the value v is not of type T but *T; this is used only for
+reflect.Values that represent lvalues. (These are not implemented yet.)
+
+
+ANALYSIS ABSTRACTION OF EACH TYPE
+
+Variables of the following "scalar" types may be represented by a
+single node: basic types, pointers, channels, maps, slices, 'func'
+pointers, interfaces.
+
+Pointers
+ Nothing to say here, oddly.
+
+Basic types (bool, string, numbers, unsafe.Pointer)
+ Currently all fields in the flattening of a type, including
+ non-pointer basic types such as int, are represented in objects and
+ values. Though non-pointer nodes within values are uninteresting,
+ non-pointer nodes in objects may be useful (if address-taken)
+ because they permit the analysis to deduce, in this example,
+
+ var s struct{ ...; x int; ... }
+ p := &s.x
+
+ that p points to s.x. If we ignored such object fields, we could only
+ say that p points somewhere within s.
+
+ All other basic types are ignored. Expressions of these types have
+ zero nodeid, and fields of these types within aggregate other types
+ are omitted.
+
+ unsafe.Pointers are not modelled as pointers, so a conversion of an
+ unsafe.Pointer to *T is (unsoundly) treated equivalent to new(T).
+
+Channels
+ An expression of type 'chan T' is a kind of pointer that points
+ exclusively to channel objects, i.e. objects created by MakeChan (or
+ reflection).
+
+ 'chan T' is treated like *T.
+ *ssa.MakeChan is treated as equivalent to new(T).
+ *ssa.Send and receive (*ssa.UnOp(ARROW)) and are equivalent to store
+ and load.
+
+Maps
+ An expression of type 'map[K]V' is a kind of pointer that points
+ exclusively to map objects, i.e. objects created by MakeMap (or
+ reflection).
+
+ map K[V] is treated like *M where M = struct{k K; v V}.
+ *ssa.MakeMap is equivalent to new(M).
+ *ssa.MapUpdate is equivalent to *y=x where *y and x have type M.
+ *ssa.Lookup is equivalent to y=x.v where x has type *M.
+
+Slices
+ A slice []T, which dynamically resembles a struct{array *T, len, cap int},
+ is treated as if it were just a *T pointer; the len and cap fields are
+ ignored.
+
+ *ssa.MakeSlice is treated like new([1]T): an allocation of a
+ singleton array.
+ *ssa.Index on a slice is equivalent to a load.
+ *ssa.IndexAddr on a slice returns the address of the sole element of the
+ slice, i.e. the same address.
+ *ssa.Slice is treated as a simple copy.
+
+Functions
+ An expression of type 'func...' is a kind of pointer that points
+ exclusively to function objects.
+
+ A function object has the following layout:
+
+ identity -- typ:*types.Signature; obj.flags ⊇ {otFunction}
+ params_0 -- (the receiver, if a method)
+ ...
+ params_n-1
+ results_0
+ ...
+ results_m-1
+
+ There may be multiple function objects for the same *ssa.Function
+ due to context-sensitive treatment of some functions.
+
+ The first node is the function's identity node.
+ Associated with every callsite is a special "targets" variable,
+ whose pts() contains the identity node of each function to which
+ the call may dispatch. Identity words are not otherwise used during
+ the analysis, but we construct the call graph from the pts()
+ solution for such nodes.
+
+ The following block of contiguous nodes represents the flattened-out
+ types of the parameters ("P-block") and results ("R-block") of the
+ function object.
+
+ The treatment of free variables of closures (*ssa.FreeVar) is like
+ that of global variables; it is not context-sensitive.
+ *ssa.MakeClosure instructions create copy edges to Captures.
+
+ A Go value of type 'func' (i.e. a pointer to one or more functions)
+ is a pointer whose pts() contains function objects. The valueNode()
+ for an *ssa.Function returns a singleton for that function.
+
+Interfaces
+ An expression of type 'interface{...}' is a kind of pointer that
+ points exclusively to tagged objects. All tagged objects pointed to
+ by an interface are direct (the otIndirect flag is clear) and
+ concrete (the tag type T is not itself an interface type). The
+ associated ssa.Value for an interface's tagged objects may be an
+ *ssa.MakeInterface instruction, or nil if the tagged object was
+ created by an instrinsic (e.g. reflection).
+
+ Constructing an interface value causes generation of constraints for
+ all of the concrete type's methods; we can't tell a priori which
+ ones may be called.
+
+ TypeAssert y = x.(T) is implemented by a dynamic constraint
+ triggered by each tagged object O added to pts(x): a typeFilter
+ constraint if T is an interface type, or an untag constraint if T is
+ a concrete type. A typeFilter tests whether O.typ implements T; if
+ so, O is added to pts(y). An untagFilter tests whether O.typ is
+ assignable to T,and if so, a copy edge O.v -> y is added.
+
+ ChangeInterface is a simple copy because the representation of
+ tagged objects is independent of the interface type (in contrast
+ to the "method tables" approach used by the gc runtime).
+
+ y := Invoke x.m(...) is implemented by allocating contiguous P/R
+ blocks for the callsite and adding a dynamic rule triggered by each
+ tagged object added to pts(x). The rule adds param/results copy
+ edges to/from each discovered concrete method.
+
+ (Q. Why do we model an interface as a pointer to a pair of type and
+ value, rather than as a pair of a pointer to type and a pointer to
+ value?
+ A. Control-flow joins would merge interfaces ({T1}, {V1}) and ({T2},
+ {V2}) to make ({T1,T2}, {V1,V2}), leading to the infeasible and
+ type-unsafe combination (T1,V2). Treating the value and its concrete
+ type as inseparable makes the analysis type-safe.)
+
+reflect.Value
+ A reflect.Value is modelled very similar to an interface{}, i.e. as
+ a pointer exclusively to tagged objects, but with two generalizations.
+
+ 1) a reflect.Value that represents an lvalue points to an indirect
+ (obj.flags ⊇ {otIndirect}) tagged object, which has a similar
+ layout to an tagged object except that the value is a pointer to
+ the dynamic type. Indirect tagged objects preserve the correct
+ aliasing so that mutations made by (reflect.Value).Set can be
+ observed.
+
+ Indirect objects only arise when an lvalue is derived from an
+ rvalue by indirection, e.g. the following code:
+
+ type S struct { X T }
+ var s S
+ var i interface{} = &s // i points to a *S-tagged object (from MakeInterface)
+ v1 := reflect.ValueOf(i) // v1 points to same *S-tagged object as i
+ v2 := v1.Elem() // v2 points to an indirect S-tagged object, pointing to s
+ v3 := v2.FieldByName("X") // v3 points to an indirect int-tagged object, pointing to s.X
+ v3.Set(y) // pts(s.X) ⊇ pts(y)
+
+ Whether indirect or not, the concrete type of the tagged object
+ corresponds to the user-visible dynamic type, and the existence
+ of a pointer is an implementation detail.
+
+ (NB: indirect tagged objects are not yet implemented)
+
+ 2) The dynamic type tag of a tagged object pointed to by a
+ reflect.Value may be an interface type; it need not be concrete.
+
+ This arises in code such as this:
+ tEface := reflect.TypeOf(new(interface{}).Elem() // interface{}
+ eface := reflect.Zero(tEface)
+ pts(eface) is a singleton containing an interface{}-tagged
+ object. That tagged object's payload is an interface{} value,
+ i.e. the pts of the payload contains only concrete-tagged
+ objects, although in this example it's the zero interface{} value,
+ so its pts is empty.
+
+reflect.Type
+ Just as in the real "reflect" library, we represent a reflect.Type
+ as an interface whose sole implementation is the concrete type,
+ *reflect.rtype. (This choice is forced on us by go/types: clients
+ cannot fabricate types with arbitrary method sets.)
+
+ rtype instances are canonical: there is at most one per dynamic
+ type. (rtypes are in fact large structs but since identity is all
+ that matters, we represent them by a single node.)
+
+ The payload of each *rtype-tagged object is an *rtype pointer that
+ points to exactly one such canonical rtype object. We exploit this
+ by setting the node.typ of the payload to the dynamic type, not
+ '*rtype'. This saves us an indirection in each resolution rule. As
+ an optimisation, *rtype-tagged objects are canonicalized too.
+
+
+Aggregate types:
+
+Aggregate types are treated as if all directly contained
+aggregates are recursively flattened out.
+
+Structs
+ *ssa.Field y = x.f creates a simple edge to y from x's node at f's offset.
+
+ *ssa.FieldAddr y = &x->f requires a dynamic closure rule to create
+ simple edges for each struct discovered in pts(x).
+
+ The nodes of a struct consist of a special 'identity' node (whose
+ type is that of the struct itself), followed by the nodes for all
+ the struct's fields, recursively flattened out. A pointer to the
+ struct is a pointer to its identity node. That node allows us to
+ distinguish a pointer to a struct from a pointer to its first field.
+
+ Field offsets are logical field offsets (plus one for the identity
+ node), so the sizes of the fields can be ignored by the analysis.
+
+ (The identity node is non-traditional but enables the distiction
+ described above, which is valuable for code comprehension tools.
+ Typical pointer analyses for C, whose purpose is compiler
+ optimization, must soundly model unsafe.Pointer (void*) conversions,
+ and this requires fidelity to the actual memory layout using physical
+ field offsets.)
+
+ *ssa.Field y = x.f creates a simple edge to y from x's node at f's offset.
+
+ *ssa.FieldAddr y = &x->f requires a dynamic closure rule to create
+ simple edges for each struct discovered in pts(x).
+
+Arrays
+ We model an array by an identity node (whose type is that of the
+ array itself) followed by a node representing all the elements of
+ the array; the analysis does not distinguish elements with different
+ indices. Effectively, an array is treated like struct{elem T}, a
+ load y=x[i] like y=x.elem, and a store x[i]=y like x.elem=y; the
+ index i is ignored.
+
+ A pointer to an array is pointer to its identity node. (A slice is
+ also a pointer to an array's identity node.) The identity node
+ allows us to distinguish a pointer to an array from a pointer to one
+ of its elements, but it is rather costly because it introduces more
+ offset constraints into the system. Furthermore, sound treatment of
+ unsafe.Pointer would require us to dispense with this node.
+
+ Arrays may be allocated by Alloc, by make([]T), by calls to append,
+ and via reflection.
+
+Tuples (T, ...)
+ Tuples are treated like structs with naturally numbered fields.
+ *ssa.Extract is analogous to *ssa.Field.
+
+ However, tuples have no identity field since by construction, they
+ cannot be address-taken.
+
+
+FUNCTION CALLS
+
+ There are three kinds of function call:
+ (1) static "call"-mode calls of functions.
+ (2) dynamic "call"-mode calls of functions.
+ (3) dynamic "invoke"-mode calls of interface methods.
+ Cases 1 and 2 apply equally to methods and standalone functions.
+
+ Static calls.
+ A static call consists three steps:
+ - finding the function object of the callee;
+ - creating copy edges from the actual parameter value nodes to the
+ P-block in the function object (this includes the receiver if
+ the callee is a method);
+ - creating copy edges from the R-block in the function object to
+ the value nodes for the result of the call.
+
+ A static function call is little more than two struct value copies
+ between the P/R blocks of caller and callee:
+
+ callee.P = caller.P
+ caller.R = callee.R
+
+ Context sensitivity
+
+ Static calls (alone) may be treated context sensitively,
+ i.e. each callsite may cause a distinct re-analysis of the
+ callee, improving precision. Our current context-sensitivity
+ policy treats all intrinsics and getter/setter methods in this
+ manner since such functions are small and seem like an obvious
+ source of spurious confluences, though this has not yet been
+ evaluated.
+
+ Dynamic function calls
+
+ Dynamic calls work in a similar manner except that the creation of
+ copy edges occurs dynamically, in a similar fashion to a pair of
+ struct copies in which the callee is indirect:
+
+ callee->P = caller.P
+ caller.R = callee->R
+
+ (Recall that the function object's P- and R-blocks are contiguous.)
+
+ Interface method invocation
+
+ For invoke-mode calls, we create a params/results block for the
+ callsite and attach a dynamic closure rule to the interface. For
+ each new tagged object that flows to the interface, we look up
+ the concrete method, find its function object, and connect its P/R
+ blocks to the callsite's P/R blocks, adding copy edges to the graph
+ during solving.
+
+ Recording call targets
+
+ The analysis notifies its clients of each callsite it encounters,
+ passing a CallSite interface. Among other things, the CallSite
+ contains a synthetic constraint variable ("targets") whose
+ points-to solution includes the set of all function objects to
+ which the call may dispatch.
+
+ It is via this mechanism that the callgraph is made available.
+ Clients may also elect to be notified of callgraph edges directly;
+ internally this just iterates all "targets" variables' pts(·)s.
+
+
+PRESOLVER
+
+We implement Hash-Value Numbering (HVN), a pre-solver constraint
+optimization described in Hardekopf & Lin, SAS'07. This is documented
+in more detail in hvn.go. We intend to add its cousins HR and HU in
+future.
+
+
+SOLVER
+
+The solver is currently a naive Andersen-style implementation; it does
+not perform online cycle detection, though we plan to add solver
+optimisations such as Hybrid- and Lazy- Cycle Detection from (Hardekopf
+& Lin, PLDI'07).
+
+It uses difference propagation (Pearce et al, SQC'04) to avoid
+redundant re-triggering of closure rules for values already seen.
+
+Points-to sets are represented using sparse bit vectors (similar to
+those used in LLVM and gcc), which are more space- and time-efficient
+than sets based on Go's built-in map type or dense bit vectors.
+
+Nodes are permuted prior to solving so that object nodes (which may
+appear in points-to sets) are lower numbered than non-object (var)
+nodes. This improves the density of the set over which the PTSs
+range, and thus the efficiency of the representation.
+
+Partly thanks to avoiding map iteration, the execution of the solver is
+100% deterministic, a great help during debugging.
+
+
+FURTHER READING
+
+Andersen, L. O. 1994. Program analysis and specialization for the C
+programming language. Ph.D. dissertation. DIKU, University of
+Copenhagen.
+
+David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Efficient
+field-sensitive pointer analysis for C. In Proceedings of the 5th ACM
+SIGPLAN-SIGSOFT workshop on Program analysis for software tools and
+engineering (PASTE '04). ACM, New York, NY, USA, 37-42.
+http://doi.acm.org/10.1145/996821.996835
+
+David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Online
+Cycle Detection and Difference Propagation: Applications to Pointer
+Analysis. Software Quality Control 12, 4 (December 2004), 311-337.
+http://dx.doi.org/10.1023/B:SQJO.0000039791.93071.a2
+
+David Grove and Craig Chambers. 2001. A framework for call graph
+construction algorithms. ACM Trans. Program. Lang. Syst. 23, 6
+(November 2001), 685-746.
+http://doi.acm.org/10.1145/506315.506316
+
+Ben Hardekopf and Calvin Lin. 2007. The ant and the grasshopper: fast
+and accurate pointer analysis for millions of lines of code. In
+Proceedings of the 2007 ACM SIGPLAN conference on Programming language
+design and implementation (PLDI '07). ACM, New York, NY, USA, 290-299.
+http://doi.acm.org/10.1145/1250734.1250767
+
+Ben Hardekopf and Calvin Lin. 2007. Exploiting pointer and location
+equivalence to optimize pointer analysis. In Proceedings of the 14th
+international conference on Static Analysis (SAS'07), Hanne Riis
+Nielson and Gilberto Filé (Eds.). Springer-Verlag, Berlin, Heidelberg,
+265-280.
+
+Atanas Rountev and Satish Chandra. 2000. Off-line variable substitution
+for scaling points-to analysis. In Proceedings of the ACM SIGPLAN 2000
+conference on Programming language design and implementation (PLDI '00).
+ACM, New York, NY, USA, 47-56. DOI=10.1145/349299.349310
+http://doi.acm.org/10.1145/349299.349310
+
+*/
+package pointer // import "golang.org/x/tools/go/pointer"
diff --git a/go/src/golang.org/x/tools/go/pointer/example_test.go b/go/src/golang.org/x/tools/go/pointer/example_test.go
index ba70557..673de7a 100644
--- a/go/src/golang.org/x/tools/go/pointer/example_test.go
+++ b/go/src/golang.org/x/tools/go/pointer/example_test.go
@@ -66,7 +66,7 @@
mainPkg := prog.Package(iprog.Created[0].Pkg)
// Build SSA code for bodies of all functions in the whole program.
- prog.BuildAll()
+ prog.Build()
// Configure the pointer analysis to build a call-graph.
config := &pointer.Config{
@@ -76,7 +76,7 @@
// Query points-to set of (C).f's parameter m, a map.
C := mainPkg.Type("C").Type()
- Cfm := prog.LookupMethod(C, mainPkg.Object, "f").Params[1]
+ Cfm := prog.LookupMethod(C, mainPkg.Pkg, "f").Params[1]
config.AddQuery(Cfm)
// Run the pointer analysis.
diff --git a/go/src/golang.org/x/tools/go/pointer/gen.go b/go/src/golang.org/x/tools/go/pointer/gen.go
index 6c256ac..405a63b 100644
--- a/go/src/golang.org/x/tools/go/pointer/gen.go
+++ b/go/src/golang.org/x/tools/go/pointer/gen.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
// This file defines the constraint generation phase.
@@ -13,10 +15,10 @@
import (
"fmt"
"go/token"
+ "go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
var (
@@ -1050,11 +1052,32 @@
// Assumes that Next is always directly applied to a Range result.
theMap := instr.Iter.(*ssa.Range).X
tMap := theMap.Type().Underlying().(*types.Map)
+
ksize := a.sizeof(tMap.Key())
vsize := a.sizeof(tMap.Elem())
+ // The k/v components of the Next tuple may each be invalid.
+ tTuple := instr.Type().(*types.Tuple)
+
// Load from the map's (k,v) into the tuple's (ok, k, v).
- a.genLoad(cgn, a.valueNode(instr)+1, theMap, 0, ksize+vsize)
+ osrc := uint32(0) // offset within map object
+ odst := uint32(1) // offset within tuple (initially just after 'ok bool')
+ sz := uint32(0) // amount to copy
+
+ // Is key valid?
+ if tTuple.At(1).Type() != tInvalid {
+ sz += ksize
+ } else {
+ odst += ksize
+ osrc += ksize
+ }
+
+ // Is value valid?
+ if tTuple.At(2).Type() != tInvalid {
+ sz += vsize
+ }
+
+ a.genLoad(cgn, a.valueNode(instr)+nodeid(odst), theMap, osrc, sz)
}
case *ssa.Lookup:
@@ -1223,7 +1246,7 @@
// I think so, but the answer may depend on reflection.
mset := a.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
- m := a.prog.Method(mset.At(i))
+ m := a.prog.MethodValue(mset.At(i))
a.valueNode(m)
if !itf {
diff --git a/go/src/golang.org/x/tools/go/pointer/gen14.go b/go/src/golang.org/x/tools/go/pointer/gen14.go
new file mode 100644
index 0000000..c1bce7c
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/gen14.go
@@ -0,0 +1,1315 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+// This file defines the constraint generation phase.
+
+// TODO(adonovan): move the constraint definitions and the store() etc
+// functions which add them (and are also used by the solver) into a
+// new file, constraints.go.
+
+import (
+ "fmt"
+ "go/token"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ tEface = types.NewInterface(nil, nil).Complete()
+ tInvalid = types.Typ[types.Invalid]
+ tUnsafePtr = types.Typ[types.UnsafePointer]
+)
+
+// ---------- Node creation ----------
+
+// nextNode returns the index of the next unused node.
+func (a *analysis) nextNode() nodeid {
+ return nodeid(len(a.nodes))
+}
+
+// addNodes creates nodes for all scalar elements in type typ, and
+// returns the id of the first one, or zero if the type was
+// analytically uninteresting.
+//
+// comment explains the origin of the nodes, as a debugging aid.
+//
+func (a *analysis) addNodes(typ types.Type, comment string) nodeid {
+ id := a.nextNode()
+ for _, fi := range a.flatten(typ) {
+ a.addOneNode(fi.typ, comment, fi)
+ }
+ if id == a.nextNode() {
+ return 0 // type contained no pointers
+ }
+ return id
+}
+
+// addOneNode creates a single node with type typ, and returns its id.
+//
+// typ should generally be scalar (except for tagged.T nodes
+// and struct/array identity nodes). Use addNodes for non-scalar types.
+//
+// comment explains the origin of the nodes, as a debugging aid.
+// subelement indicates the subelement, e.g. ".a.b[*].c".
+//
+func (a *analysis) addOneNode(typ types.Type, comment string, subelement *fieldInfo) nodeid {
+ id := a.nextNode()
+ a.nodes = append(a.nodes, &node{typ: typ, subelement: subelement, solve: new(solverState)})
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tcreate n%d %s for %s%s\n",
+ id, typ, comment, subelement.path())
+ }
+ return id
+}
+
+// setValueNode associates node id with the value v.
+// cgn identifies the context iff v is a local variable.
+//
+func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) {
+ if cgn != nil {
+ a.localval[v] = id
+ } else {
+ a.globalval[v] = id
+ }
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tval[%s] = n%d (%T)\n", v.Name(), id, v)
+ }
+
+ // Due to context-sensitivity, we may encounter the same Value
+ // in many contexts. We merge them to a canonical node, since
+ // that's what all clients want.
+
+ // Record the (v, id) relation if the client has queried pts(v).
+ if _, ok := a.config.Queries[v]; ok {
+ t := v.Type()
+ ptr, ok := a.result.Queries[v]
+ if !ok {
+ // First time? Create the canonical query node.
+ ptr = Pointer{a, a.addNodes(t, "query")}
+ a.result.Queries[v] = ptr
+ }
+ a.result.Queries[v] = ptr
+ a.copy(ptr.n, id, a.sizeof(t))
+ }
+
+ // Record the (*v, id) relation if the client has queried pts(*v).
+ if _, ok := a.config.IndirectQueries[v]; ok {
+ t := v.Type()
+ ptr, ok := a.result.IndirectQueries[v]
+ if !ok {
+ // First time? Create the canonical indirect query node.
+ ptr = Pointer{a, a.addNodes(v.Type(), "query.indirect")}
+ a.result.IndirectQueries[v] = ptr
+ }
+ a.genLoad(cgn, ptr.n, v, 0, a.sizeof(t))
+ }
+}
+
+// endObject marks the end of a sequence of calls to addNodes denoting
+// a single object allocation.
+//
+// obj is the start node of the object, from a prior call to nextNode.
+// Its size, flags and optional data will be updated.
+//
+func (a *analysis) endObject(obj nodeid, cgn *cgnode, data interface{}) *object {
+ // Ensure object is non-empty by padding;
+ // the pad will be the object node.
+ size := uint32(a.nextNode() - obj)
+ if size == 0 {
+ a.addOneNode(tInvalid, "padding", nil)
+ }
+ objNode := a.nodes[obj]
+ o := &object{
+ size: size, // excludes padding
+ cgn: cgn,
+ data: data,
+ }
+ objNode.obj = o
+
+ return o
+}
+
+// makeFunctionObject creates and returns a new function object
+// (contour) for fn, and returns the id of its first node. It also
+// enqueues fn for subsequent constraint generation.
+//
+// For a context-sensitive contour, callersite identifies the sole
+// callsite; for shared contours, caller is nil.
+//
+func (a *analysis) makeFunctionObject(fn *ssa.Function, callersite *callsite) nodeid {
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t---- makeFunctionObject %s\n", fn)
+ }
+
+ // obj is the function object (identity, params, results).
+ obj := a.nextNode()
+ cgn := a.makeCGNode(fn, obj, callersite)
+ sig := fn.Signature
+ a.addOneNode(sig, "func.cgnode", nil) // (scalar with Signature type)
+ if recv := sig.Recv(); recv != nil {
+ a.addNodes(recv.Type(), "func.recv")
+ }
+ a.addNodes(sig.Params(), "func.params")
+ a.addNodes(sig.Results(), "func.results")
+ a.endObject(obj, cgn, fn).flags |= otFunction
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t----\n")
+ }
+
+ // Queue it up for constraint processing.
+ a.genq = append(a.genq, cgn)
+
+ return obj
+}
+
+// makeTagged creates a tagged object of type typ.
+func (a *analysis) makeTagged(typ types.Type, cgn *cgnode, data interface{}) nodeid {
+ obj := a.addOneNode(typ, "tagged.T", nil) // NB: type may be non-scalar!
+ a.addNodes(typ, "tagged.v")
+ a.endObject(obj, cgn, data).flags |= otTagged
+ return obj
+}
+
+// makeRtype returns the canonical tagged object of type *rtype whose
+// payload points to the sole rtype object for T.
+//
+// TODO(adonovan): move to reflect.go; it's part of the solver really.
+//
+func (a *analysis) makeRtype(T types.Type) nodeid {
+ if v := a.rtypes.At(T); v != nil {
+ return v.(nodeid)
+ }
+
+ // Create the object for the reflect.rtype itself, which is
+ // ordinarily a large struct but here a single node will do.
+ obj := a.nextNode()
+ a.addOneNode(T, "reflect.rtype", nil)
+ a.endObject(obj, nil, T)
+
+ id := a.makeTagged(a.reflectRtypePtr, nil, T)
+ a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
+ a.addressOf(a.reflectRtypePtr, id+1, obj)
+
+ a.rtypes.Set(T, id)
+ return id
+}
+
+// rtypeValue returns the type of the *reflect.rtype-tagged object obj.
+func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type {
+ tDyn, t, _ := a.taggedValue(obj)
+ if tDyn != a.reflectRtypePtr {
+ panic(fmt.Sprintf("not a *reflect.rtype-tagged object: obj=n%d tag=%v payload=n%d", obj, tDyn, t))
+ }
+ return a.nodes[t].typ
+}
+
+// valueNode returns the id of the value node for v, creating it (and
+// the association) as needed. It may return zero for uninteresting
+// values containing no pointers.
+//
+func (a *analysis) valueNode(v ssa.Value) nodeid {
+ // Value nodes for locals are created en masse by genFunc.
+ if id, ok := a.localval[v]; ok {
+ return id
+ }
+
+ // Value nodes for globals are created on demand.
+ id, ok := a.globalval[v]
+ if !ok {
+ var comment string
+ if a.log != nil {
+ comment = v.String()
+ }
+ id = a.addNodes(v.Type(), comment)
+ if obj := a.objectNode(nil, v); obj != 0 {
+ a.addressOf(v.Type(), id, obj)
+ }
+ a.setValueNode(v, id, nil)
+ }
+ return id
+}
+
+// valueOffsetNode ascertains the node for tuple/struct value v,
+// then returns the node for its subfield #index.
+//
+func (a *analysis) valueOffsetNode(v ssa.Value, index int) nodeid {
+ id := a.valueNode(v)
+ if id == 0 {
+ panic(fmt.Sprintf("cannot offset within n0: %s = %s", v.Name(), v))
+ }
+ return id + nodeid(a.offsetOf(v.Type(), index))
+}
+
+// isTaggedObject reports whether object obj is a tagged object.
+func (a *analysis) isTaggedObject(obj nodeid) bool {
+ return a.nodes[obj].obj.flags&otTagged != 0
+}
+
+// taggedValue returns the dynamic type tag, the (first node of the)
+// payload, and the indirect flag of the tagged object starting at id.
+// Panic ensues if !isTaggedObject(id).
+//
+func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect bool) {
+ n := a.nodes[obj]
+ flags := n.obj.flags
+ if flags&otTagged == 0 {
+ panic(fmt.Sprintf("not a tagged object: n%d", obj))
+ }
+ return n.typ, obj + 1, flags&otIndirect != 0
+}
+
+// funcParams returns the first node of the params (P) block of the
+// function whose object node (obj.flags&otFunction) is id.
+//
+func (a *analysis) funcParams(id nodeid) nodeid {
+ n := a.nodes[id]
+ if n.obj == nil || n.obj.flags&otFunction == 0 {
+ panic(fmt.Sprintf("funcParams(n%d): not a function object block", id))
+ }
+ return id + 1
+}
+
+// funcResults returns the first node of the results (R) block of the
+// function whose object node (obj.flags&otFunction) is id.
+//
+func (a *analysis) funcResults(id nodeid) nodeid {
+ n := a.nodes[id]
+ if n.obj == nil || n.obj.flags&otFunction == 0 {
+ panic(fmt.Sprintf("funcResults(n%d): not a function object block", id))
+ }
+ sig := n.typ.(*types.Signature)
+ id += 1 + nodeid(a.sizeof(sig.Params()))
+ if sig.Recv() != nil {
+ id += nodeid(a.sizeof(sig.Recv().Type()))
+ }
+ return id
+}
+
+// ---------- Constraint creation ----------
+
+// copy creates a constraint of the form dst = src.
+// sizeof is the width (in logical fields) of the copied type.
+//
+func (a *analysis) copy(dst, src nodeid, sizeof uint32) {
+ if src == dst || sizeof == 0 {
+ return // trivial
+ }
+ if src == 0 || dst == 0 {
+ panic(fmt.Sprintf("ill-typed copy dst=n%d src=n%d", dst, src))
+ }
+ for i := uint32(0); i < sizeof; i++ {
+ a.addConstraint(©Constraint{dst, src})
+ src++
+ dst++
+ }
+}
+
+// addressOf creates a constraint of the form id = &obj.
+// T is the type of the address.
+func (a *analysis) addressOf(T types.Type, id, obj nodeid) {
+ if id == 0 {
+ panic("addressOf: zero id")
+ }
+ if obj == 0 {
+ panic("addressOf: zero obj")
+ }
+ if a.shouldTrack(T) {
+ a.addConstraint(&addrConstraint{id, obj})
+ }
+}
+
+// load creates a load constraint of the form dst = src[offset].
+// offset is the pointer offset in logical fields.
+// sizeof is the width (in logical fields) of the loaded type.
+//
+func (a *analysis) load(dst, src nodeid, offset, sizeof uint32) {
+ if dst == 0 {
+ return // load of non-pointerlike value
+ }
+ if src == 0 && dst == 0 {
+ return // non-pointerlike operation
+ }
+ if src == 0 || dst == 0 {
+ panic(fmt.Sprintf("ill-typed load dst=n%d src=n%d", dst, src))
+ }
+ for i := uint32(0); i < sizeof; i++ {
+ a.addConstraint(&loadConstraint{offset, dst, src})
+ offset++
+ dst++
+ }
+}
+
+// store creates a store constraint of the form dst[offset] = src.
+// offset is the pointer offset in logical fields.
+// sizeof is the width (in logical fields) of the stored type.
+//
+func (a *analysis) store(dst, src nodeid, offset uint32, sizeof uint32) {
+ if src == 0 {
+ return // store of non-pointerlike value
+ }
+ if src == 0 && dst == 0 {
+ return // non-pointerlike operation
+ }
+ if src == 0 || dst == 0 {
+ panic(fmt.Sprintf("ill-typed store dst=n%d src=n%d", dst, src))
+ }
+ for i := uint32(0); i < sizeof; i++ {
+ a.addConstraint(&storeConstraint{offset, dst, src})
+ offset++
+ src++
+ }
+}
+
+// offsetAddr creates an offsetAddr constraint of the form dst = &src.#offset.
+// offset is the field offset in logical fields.
+// T is the type of the address.
+//
+func (a *analysis) offsetAddr(T types.Type, dst, src nodeid, offset uint32) {
+ if !a.shouldTrack(T) {
+ return
+ }
+ if offset == 0 {
+ // Simplify dst = &src->f0
+ // to dst = src
+ // (NB: this optimisation is defeated by the identity
+ // field prepended to struct and array objects.)
+ a.copy(dst, src, 1)
+ } else {
+ a.addConstraint(&offsetAddrConstraint{offset, dst, src})
+ }
+}
+
+// typeAssert creates a typeFilter or untag constraint of the form dst = src.(T):
+// typeFilter for an interface, untag for a concrete type.
+// The exact flag is specified as for untagConstraint.
+//
+func (a *analysis) typeAssert(T types.Type, dst, src nodeid, exact bool) {
+ if isInterface(T) {
+ a.addConstraint(&typeFilterConstraint{T, dst, src})
+ } else {
+ a.addConstraint(&untagConstraint{T, dst, src, exact})
+ }
+}
+
+// addConstraint adds c to the constraint set.
+func (a *analysis) addConstraint(c constraint) {
+ a.constraints = append(a.constraints, c)
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t%s\n", c)
+ }
+}
+
+// copyElems generates load/store constraints for *dst = *src,
+// where src and dst are slices or *arrays.
+//
+func (a *analysis) copyElems(cgn *cgnode, typ types.Type, dst, src ssa.Value) {
+ tmp := a.addNodes(typ, "copy")
+ sz := a.sizeof(typ)
+ a.genLoad(cgn, tmp, src, 1, sz)
+ a.genStore(cgn, dst, tmp, 1, sz)
+}
+
+// ---------- Constraint generation ----------
+
+// genConv generates constraints for the conversion operation conv.
+func (a *analysis) genConv(conv *ssa.Convert, cgn *cgnode) {
+ res := a.valueNode(conv)
+ if res == 0 {
+ return // result is non-pointerlike
+ }
+
+ tSrc := conv.X.Type()
+ tDst := conv.Type()
+
+ switch utSrc := tSrc.Underlying().(type) {
+ case *types.Slice:
+ // []byte/[]rune -> string?
+ return
+
+ case *types.Pointer:
+ // *T -> unsafe.Pointer?
+ if tDst.Underlying() == tUnsafePtr {
+ return // we don't model unsafe aliasing (unsound)
+ }
+
+ case *types.Basic:
+ switch tDst.Underlying().(type) {
+ case *types.Pointer:
+ // Treat unsafe.Pointer->*T conversions like
+ // new(T) and create an unaliased object.
+ if utSrc == tUnsafePtr {
+ obj := a.addNodes(mustDeref(tDst), "unsafe.Pointer conversion")
+ a.endObject(obj, cgn, conv)
+ a.addressOf(tDst, res, obj)
+ return
+ }
+
+ case *types.Slice:
+ // string -> []byte/[]rune (or named aliases)?
+ if utSrc.Info()&types.IsString != 0 {
+ obj := a.addNodes(sliceToArray(tDst), "convert")
+ a.endObject(obj, cgn, conv)
+ a.addressOf(tDst, res, obj)
+ return
+ }
+
+ case *types.Basic:
+ // All basic-to-basic type conversions are no-ops.
+ // This includes uintptr<->unsafe.Pointer conversions,
+ // which we (unsoundly) ignore.
+ return
+ }
+ }
+
+ panic(fmt.Sprintf("illegal *ssa.Convert %s -> %s: %s", tSrc, tDst, conv.Parent()))
+}
+
+// genAppend generates constraints for a call to append.
+func (a *analysis) genAppend(instr *ssa.Call, cgn *cgnode) {
+ // Consider z = append(x, y). y is optional.
+ // This may allocate a new [1]T array; call its object w.
+ // We get the following constraints:
+ // z = x
+ // z = &w
+ // *z = *y
+
+ x := instr.Call.Args[0]
+
+ z := instr
+ a.copy(a.valueNode(z), a.valueNode(x), 1) // z = x
+
+ if len(instr.Call.Args) == 1 {
+ return // no allocation for z = append(x) or _ = append(x).
+ }
+
+ // TODO(adonovan): test append([]byte, ...string) []byte.
+
+ y := instr.Call.Args[1]
+ tArray := sliceToArray(instr.Call.Args[0].Type())
+
+ var w nodeid
+ w = a.nextNode()
+ a.addNodes(tArray, "append")
+ a.endObject(w, cgn, instr)
+
+ a.copyElems(cgn, tArray.Elem(), z, y) // *z = *y
+ a.addressOf(instr.Type(), a.valueNode(z), w) // z = &w
+}
+
+// genBuiltinCall generates contraints for a call to a built-in.
+func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) {
+ call := instr.Common()
+ switch call.Value.(*ssa.Builtin).Name() {
+ case "append":
+ // Safe cast: append cannot appear in a go or defer statement.
+ a.genAppend(instr.(*ssa.Call), cgn)
+
+ case "copy":
+ tElem := call.Args[0].Type().Underlying().(*types.Slice).Elem()
+ a.copyElems(cgn, tElem, call.Args[0], call.Args[1])
+
+ case "panic":
+ a.copy(a.panicNode, a.valueNode(call.Args[0]), 1)
+
+ case "recover":
+ if v := instr.Value(); v != nil {
+ a.copy(a.valueNode(v), a.panicNode, 1)
+ }
+
+ case "print":
+ // In the tests, the probe might be the sole reference
+ // to its arg, so make sure we create nodes for it.
+ if len(call.Args) > 0 {
+ a.valueNode(call.Args[0])
+ }
+
+ case "ssa:wrapnilchk":
+ a.copy(a.valueNode(instr.Value()), a.valueNode(call.Args[0]), 1)
+
+ default:
+ // No-ops: close len cap real imag complex print println delete.
+ }
+}
+
+// shouldUseContext defines the context-sensitivity policy. It
+// returns true if we should analyse all static calls to fn anew.
+//
+// Obviously this interface rather limits how much freedom we have to
+// choose a policy. The current policy, rather arbitrarily, is true
+// for intrinsics and accessor methods (actually: short, single-block,
+// call-free functions). This is just a starting point.
+//
+func (a *analysis) shouldUseContext(fn *ssa.Function) bool {
+ if a.findIntrinsic(fn) != nil {
+ return true // treat intrinsics context-sensitively
+ }
+ if len(fn.Blocks) != 1 {
+ return false // too expensive
+ }
+ blk := fn.Blocks[0]
+ if len(blk.Instrs) > 10 {
+ return false // too expensive
+ }
+ if fn.Synthetic != "" && (fn.Pkg == nil || fn != fn.Pkg.Func("init")) {
+ return true // treat synthetic wrappers context-sensitively
+ }
+ for _, instr := range blk.Instrs {
+ switch instr := instr.(type) {
+ case ssa.CallInstruction:
+ // Disallow function calls (except to built-ins)
+ // because of the danger of unbounded recursion.
+ if _, ok := instr.Common().Value.(*ssa.Builtin); !ok {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// genStaticCall generates constraints for a statically dispatched function call.
+func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
+ fn := call.StaticCallee()
+
+ // Special cases for inlined intrinsics.
+ switch fn {
+ case a.runtimeSetFinalizer:
+ // Inline SetFinalizer so the call appears direct.
+ site.targets = a.addOneNode(tInvalid, "SetFinalizer.targets", nil)
+ a.addConstraint(&runtimeSetFinalizerConstraint{
+ targets: site.targets,
+ x: a.valueNode(call.Args[0]),
+ f: a.valueNode(call.Args[1]),
+ })
+ return
+
+ case a.reflectValueCall:
+ // Inline (reflect.Value).Call so the call appears direct.
+ dotdotdot := false
+ ret := reflectCallImpl(a, caller, site, a.valueNode(call.Args[0]), a.valueNode(call.Args[1]), dotdotdot)
+ if result != 0 {
+ a.addressOf(fn.Signature.Results().At(0).Type(), result, ret)
+ }
+ return
+ }
+
+ // Ascertain the context (contour/cgnode) for a particular call.
+ var obj nodeid
+ if a.shouldUseContext(fn) {
+ obj = a.makeFunctionObject(fn, site) // new contour
+ } else {
+ obj = a.objectNode(nil, fn) // shared contour
+ }
+ a.callEdge(caller, site, obj)
+
+ sig := call.Signature()
+
+ // Copy receiver, if any.
+ params := a.funcParams(obj)
+ args := call.Args
+ if sig.Recv() != nil {
+ sz := a.sizeof(sig.Recv().Type())
+ a.copy(params, a.valueNode(args[0]), sz)
+ params += nodeid(sz)
+ args = args[1:]
+ }
+
+ // Copy actual parameters into formal params block.
+ // Must loop, since the actuals aren't contiguous.
+ for i, arg := range args {
+ sz := a.sizeof(sig.Params().At(i).Type())
+ a.copy(params, a.valueNode(arg), sz)
+ params += nodeid(sz)
+ }
+
+ // Copy formal results block to actual result.
+ if result != 0 {
+ a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
+ }
+}
+
+// genDynamicCall generates constraints for a dynamic function call.
+func (a *analysis) genDynamicCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
+ // pts(targets) will be the set of possible call targets.
+ site.targets = a.valueNode(call.Value)
+
+ // We add dynamic closure rules that store the arguments into
+ // the P-block and load the results from the R-block of each
+ // function discovered in pts(targets).
+
+ sig := call.Signature()
+ var offset uint32 = 1 // P/R block starts at offset 1
+ for i, arg := range call.Args {
+ sz := a.sizeof(sig.Params().At(i).Type())
+ a.genStore(caller, call.Value, a.valueNode(arg), offset, sz)
+ offset += sz
+ }
+ if result != 0 {
+ a.genLoad(caller, result, call.Value, offset, a.sizeof(sig.Results()))
+ }
+}
+
+// genInvoke generates constraints for a dynamic method invocation.
+func (a *analysis) genInvoke(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
+ if call.Value.Type() == a.reflectType {
+ a.genInvokeReflectType(caller, site, call, result)
+ return
+ }
+
+ sig := call.Signature()
+
+ // Allocate a contiguous targets/params/results block for this call.
+ block := a.nextNode()
+ // pts(targets) will be the set of possible call targets
+ site.targets = a.addOneNode(sig, "invoke.targets", nil)
+ p := a.addNodes(sig.Params(), "invoke.params")
+ r := a.addNodes(sig.Results(), "invoke.results")
+
+ // Copy the actual parameters into the call's params block.
+ for i, n := 0, sig.Params().Len(); i < n; i++ {
+ sz := a.sizeof(sig.Params().At(i).Type())
+ a.copy(p, a.valueNode(call.Args[i]), sz)
+ p += nodeid(sz)
+ }
+ // Copy the call's results block to the actual results.
+ if result != 0 {
+ a.copy(result, r, a.sizeof(sig.Results()))
+ }
+
+ // We add a dynamic invoke constraint that will connect the
+ // caller's and the callee's P/R blocks for each discovered
+ // call target.
+ a.addConstraint(&invokeConstraint{call.Method, a.valueNode(call.Value), block})
+}
+
+// genInvokeReflectType is a specialization of genInvoke where the
+// receiver type is a reflect.Type, under the assumption that there
+// can be at most one implementation of this interface, *reflect.rtype.
+//
+// (Though this may appear to be an instance of a pattern---method
+// calls on interfaces known to have exactly one implementation---in
+// practice it occurs rarely, so we special case for reflect.Type.)
+//
+// In effect we treat this:
+// var rt reflect.Type = ...
+// rt.F()
+// as this:
+// rt.(*reflect.rtype).F()
+//
+func (a *analysis) genInvokeReflectType(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
+ // Unpack receiver into rtype
+ rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
+ recv := a.valueNode(call.Value)
+ a.typeAssert(a.reflectRtypePtr, rtype, recv, true)
+
+ // Look up the concrete method.
+ fn := a.prog.LookupMethod(a.reflectRtypePtr, call.Method.Pkg(), call.Method.Name())
+
+ obj := a.makeFunctionObject(fn, site) // new contour for this call
+ a.callEdge(caller, site, obj)
+
+ // From now on, it's essentially a static call, but little is
+ // gained by factoring together the code for both cases.
+
+ sig := fn.Signature // concrete method
+ targets := a.addOneNode(sig, "call.targets", nil)
+ a.addressOf(sig, targets, obj) // (a singleton)
+
+ // Copy receiver.
+ params := a.funcParams(obj)
+ a.copy(params, rtype, 1)
+ params++
+
+ // Copy actual parameters into formal P-block.
+ // Must loop, since the actuals aren't contiguous.
+ for i, arg := range call.Args {
+ sz := a.sizeof(sig.Params().At(i).Type())
+ a.copy(params, a.valueNode(arg), sz)
+ params += nodeid(sz)
+ }
+
+ // Copy formal R-block to actual R-block.
+ if result != 0 {
+ a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
+ }
+}
+
+// genCall generates constraints for call instruction instr.
+func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
+ call := instr.Common()
+
+ // Intrinsic implementations of built-in functions.
+ if _, ok := call.Value.(*ssa.Builtin); ok {
+ a.genBuiltinCall(instr, caller)
+ return
+ }
+
+ var result nodeid
+ if v := instr.Value(); v != nil {
+ result = a.valueNode(v)
+ }
+
+ site := &callsite{instr: instr}
+ if call.StaticCallee() != nil {
+ a.genStaticCall(caller, site, call, result)
+ } else if call.IsInvoke() {
+ a.genInvoke(caller, site, call, result)
+ } else {
+ a.genDynamicCall(caller, site, call, result)
+ }
+
+ caller.sites = append(caller.sites, site)
+
+ if a.log != nil {
+ // TODO(adonovan): debug: improve log message.
+ fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller)
+ }
+}
+
+// objectNode returns the object to which v points, if known.
+// In other words, if the points-to set of v is a singleton, it
+// returns the sole label, zero otherwise.
+//
+// We exploit this information to make the generated constraints less
+// dynamic. For example, a complex load constraint can be replaced by
+// a simple copy constraint when the sole destination is known a priori.
+//
+// Some SSA instructions always have singletons points-to sets:
+// Alloc, Function, Global, MakeChan, MakeClosure, MakeInterface, MakeMap, MakeSlice.
+// Others may be singletons depending on their operands:
+// FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice.
+//
+// Idempotent. Objects are created as needed, possibly via recursion
+// down the SSA value graph, e.g IndexAddr(FieldAddr(Alloc))).
+//
+func (a *analysis) objectNode(cgn *cgnode, v ssa.Value) nodeid {
+ switch v.(type) {
+ case *ssa.Global, *ssa.Function, *ssa.Const, *ssa.FreeVar:
+ // Global object.
+ obj, ok := a.globalobj[v]
+ if !ok {
+ switch v := v.(type) {
+ case *ssa.Global:
+ obj = a.nextNode()
+ a.addNodes(mustDeref(v.Type()), "global")
+ a.endObject(obj, nil, v)
+
+ case *ssa.Function:
+ obj = a.makeFunctionObject(v, nil)
+
+ case *ssa.Const:
+ // not addressable
+
+ case *ssa.FreeVar:
+ // not addressable
+ }
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tglobalobj[%s] = n%d\n", v, obj)
+ }
+ a.globalobj[v] = obj
+ }
+ return obj
+ }
+
+ // Local object.
+ obj, ok := a.localobj[v]
+ if !ok {
+ switch v := v.(type) {
+ case *ssa.Alloc:
+ obj = a.nextNode()
+ a.addNodes(mustDeref(v.Type()), "alloc")
+ a.endObject(obj, cgn, v)
+
+ case *ssa.MakeSlice:
+ obj = a.nextNode()
+ a.addNodes(sliceToArray(v.Type()), "makeslice")
+ a.endObject(obj, cgn, v)
+
+ case *ssa.MakeChan:
+ obj = a.nextNode()
+ a.addNodes(v.Type().Underlying().(*types.Chan).Elem(), "makechan")
+ a.endObject(obj, cgn, v)
+
+ case *ssa.MakeMap:
+ obj = a.nextNode()
+ tmap := v.Type().Underlying().(*types.Map)
+ a.addNodes(tmap.Key(), "makemap.key")
+ elem := a.addNodes(tmap.Elem(), "makemap.value")
+
+ // To update the value field, MapUpdate
+ // generates store-with-offset constraints which
+ // the presolver can't model, so we must mark
+ // those nodes indirect.
+ for id, end := elem, elem+nodeid(a.sizeof(tmap.Elem())); id < end; id++ {
+ a.mapValues = append(a.mapValues, id)
+ }
+ a.endObject(obj, cgn, v)
+
+ case *ssa.MakeInterface:
+ tConc := v.X.Type()
+ obj = a.makeTagged(tConc, cgn, v)
+
+ // Copy the value into it, if nontrivial.
+ if x := a.valueNode(v.X); x != 0 {
+ a.copy(obj+1, x, a.sizeof(tConc))
+ }
+
+ case *ssa.FieldAddr:
+ if xobj := a.objectNode(cgn, v.X); xobj != 0 {
+ obj = xobj + nodeid(a.offsetOf(mustDeref(v.X.Type()), v.Field))
+ }
+
+ case *ssa.IndexAddr:
+ if xobj := a.objectNode(cgn, v.X); xobj != 0 {
+ obj = xobj + 1
+ }
+
+ case *ssa.Slice:
+ obj = a.objectNode(cgn, v.X)
+
+ case *ssa.Convert:
+ // TODO(adonovan): opt: handle these cases too:
+ // - unsafe.Pointer->*T conversion acts like Alloc
+ // - string->[]byte/[]rune conversion acts like MakeSlice
+ }
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tlocalobj[%s] = n%d\n", v.Name(), obj)
+ }
+ a.localobj[v] = obj
+ }
+ return obj
+}
+
+// genLoad generates constraints for result = *(ptr + val).
+func (a *analysis) genLoad(cgn *cgnode, result nodeid, ptr ssa.Value, offset, sizeof uint32) {
+ if obj := a.objectNode(cgn, ptr); obj != 0 {
+ // Pre-apply loadConstraint.solve().
+ a.copy(result, obj+nodeid(offset), sizeof)
+ } else {
+ a.load(result, a.valueNode(ptr), offset, sizeof)
+ }
+}
+
+// genOffsetAddr generates constraints for a 'v=ptr.field' (FieldAddr)
+// or 'v=ptr[*]' (IndexAddr) instruction v.
+func (a *analysis) genOffsetAddr(cgn *cgnode, v ssa.Value, ptr nodeid, offset uint32) {
+ dst := a.valueNode(v)
+ if obj := a.objectNode(cgn, v); obj != 0 {
+ // Pre-apply offsetAddrConstraint.solve().
+ a.addressOf(v.Type(), dst, obj)
+ } else {
+ a.offsetAddr(v.Type(), dst, ptr, offset)
+ }
+}
+
+// genStore generates constraints for *(ptr + offset) = val.
+func (a *analysis) genStore(cgn *cgnode, ptr ssa.Value, val nodeid, offset, sizeof uint32) {
+ if obj := a.objectNode(cgn, ptr); obj != 0 {
+ // Pre-apply storeConstraint.solve().
+ a.copy(obj+nodeid(offset), val, sizeof)
+ } else {
+ a.store(a.valueNode(ptr), val, offset, sizeof)
+ }
+}
+
+// genInstr generates constraints for instruction instr in context cgn.
+func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
+ if a.log != nil {
+ var prefix string
+ if val, ok := instr.(ssa.Value); ok {
+ prefix = val.Name() + " = "
+ }
+ fmt.Fprintf(a.log, "; %s%s\n", prefix, instr)
+ }
+
+ switch instr := instr.(type) {
+ case *ssa.DebugRef:
+ // no-op.
+
+ case *ssa.UnOp:
+ switch instr.Op {
+ case token.ARROW: // <-x
+ // We can ignore instr.CommaOk because the node we're
+ // altering is always at zero offset relative to instr
+ tElem := instr.X.Type().Underlying().(*types.Chan).Elem()
+ a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(tElem))
+
+ case token.MUL: // *x
+ a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(instr.Type()))
+
+ default:
+ // NOT, SUB, XOR: no-op.
+ }
+
+ case *ssa.BinOp:
+ // All no-ops.
+
+ case ssa.CallInstruction: // *ssa.Call, *ssa.Go, *ssa.Defer
+ a.genCall(cgn, instr)
+
+ case *ssa.ChangeType:
+ a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
+
+ case *ssa.Convert:
+ a.genConv(instr, cgn)
+
+ case *ssa.Extract:
+ a.copy(a.valueNode(instr),
+ a.valueOffsetNode(instr.Tuple, instr.Index),
+ a.sizeof(instr.Type()))
+
+ case *ssa.FieldAddr:
+ a.genOffsetAddr(cgn, instr, a.valueNode(instr.X),
+ a.offsetOf(mustDeref(instr.X.Type()), instr.Field))
+
+ case *ssa.IndexAddr:
+ a.genOffsetAddr(cgn, instr, a.valueNode(instr.X), 1)
+
+ case *ssa.Field:
+ a.copy(a.valueNode(instr),
+ a.valueOffsetNode(instr.X, instr.Field),
+ a.sizeof(instr.Type()))
+
+ case *ssa.Index:
+ a.copy(a.valueNode(instr), 1+a.valueNode(instr.X), a.sizeof(instr.Type()))
+
+ case *ssa.Select:
+ recv := a.valueOffsetNode(instr, 2) // instr : (index, recvOk, recv0, ... recv_n-1)
+ for _, st := range instr.States {
+ elemSize := a.sizeof(st.Chan.Type().Underlying().(*types.Chan).Elem())
+ switch st.Dir {
+ case types.RecvOnly:
+ a.genLoad(cgn, recv, st.Chan, 0, elemSize)
+ recv += nodeid(elemSize)
+
+ case types.SendOnly:
+ a.genStore(cgn, st.Chan, a.valueNode(st.Send), 0, elemSize)
+ }
+ }
+
+ case *ssa.Return:
+ results := a.funcResults(cgn.obj)
+ for _, r := range instr.Results {
+ sz := a.sizeof(r.Type())
+ a.copy(results, a.valueNode(r), sz)
+ results += nodeid(sz)
+ }
+
+ case *ssa.Send:
+ a.genStore(cgn, instr.Chan, a.valueNode(instr.X), 0, a.sizeof(instr.X.Type()))
+
+ case *ssa.Store:
+ a.genStore(cgn, instr.Addr, a.valueNode(instr.Val), 0, a.sizeof(instr.Val.Type()))
+
+ case *ssa.Alloc, *ssa.MakeSlice, *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeInterface:
+ v := instr.(ssa.Value)
+ a.addressOf(v.Type(), a.valueNode(v), a.objectNode(cgn, v))
+
+ case *ssa.ChangeInterface:
+ a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
+
+ case *ssa.TypeAssert:
+ a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X), true)
+
+ case *ssa.Slice:
+ a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
+
+ case *ssa.If, *ssa.Jump:
+ // no-op.
+
+ case *ssa.Phi:
+ sz := a.sizeof(instr.Type())
+ for _, e := range instr.Edges {
+ a.copy(a.valueNode(instr), a.valueNode(e), sz)
+ }
+
+ case *ssa.MakeClosure:
+ fn := instr.Fn.(*ssa.Function)
+ a.copy(a.valueNode(instr), a.valueNode(fn), 1)
+ // Free variables are treated like global variables.
+ for i, b := range instr.Bindings {
+ a.copy(a.valueNode(fn.FreeVars[i]), a.valueNode(b), a.sizeof(b.Type()))
+ }
+
+ case *ssa.RunDefers:
+ // The analysis is flow insensitive, so we just "call"
+ // defers as we encounter them.
+
+ case *ssa.Range:
+ // Do nothing. Next{Iter: *ssa.Range} handles this case.
+
+ case *ssa.Next:
+ if !instr.IsString { // map
+ // Assumes that Next is always directly applied to a Range result.
+ theMap := instr.Iter.(*ssa.Range).X
+ tMap := theMap.Type().Underlying().(*types.Map)
+
+ ksize := a.sizeof(tMap.Key())
+ vsize := a.sizeof(tMap.Elem())
+
+ // The k/v components of the Next tuple may each be invalid.
+ tTuple := instr.Type().(*types.Tuple)
+
+ // Load from the map's (k,v) into the tuple's (ok, k, v).
+ osrc := uint32(0) // offset within map object
+ odst := uint32(1) // offset within tuple (initially just after 'ok bool')
+ sz := uint32(0) // amount to copy
+
+ // Is key valid?
+ if tTuple.At(1).Type() != tInvalid {
+ sz += ksize
+ } else {
+ odst += ksize
+ osrc += ksize
+ }
+
+ // Is value valid?
+ if tTuple.At(2).Type() != tInvalid {
+ sz += vsize
+ }
+
+ a.genLoad(cgn, a.valueNode(instr)+nodeid(odst), theMap, osrc, sz)
+ }
+
+ case *ssa.Lookup:
+ if tMap, ok := instr.X.Type().Underlying().(*types.Map); ok {
+ // CommaOk can be ignored: field 0 is a no-op.
+ ksize := a.sizeof(tMap.Key())
+ vsize := a.sizeof(tMap.Elem())
+ a.genLoad(cgn, a.valueNode(instr), instr.X, ksize, vsize)
+ }
+
+ case *ssa.MapUpdate:
+ tmap := instr.Map.Type().Underlying().(*types.Map)
+ ksize := a.sizeof(tmap.Key())
+ vsize := a.sizeof(tmap.Elem())
+ a.genStore(cgn, instr.Map, a.valueNode(instr.Key), 0, ksize)
+ a.genStore(cgn, instr.Map, a.valueNode(instr.Value), ksize, vsize)
+
+ case *ssa.Panic:
+ a.copy(a.panicNode, a.valueNode(instr.X), 1)
+
+ default:
+ panic(fmt.Sprintf("unimplemented: %T", instr))
+ }
+}
+
+func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite) *cgnode {
+ cgn := &cgnode{fn: fn, obj: obj, callersite: callersite}
+ a.cgnodes = append(a.cgnodes, cgn)
+ return cgn
+}
+
+// genRootCalls generates the synthetic root of the callgraph and the
+// initial calls from it to the analysis scope, such as main, a test
+// or a library.
+//
+func (a *analysis) genRootCalls() *cgnode {
+ r := a.prog.NewFunction("<root>", new(types.Signature), "root of callgraph")
+ root := a.makeCGNode(r, 0, nil)
+
+ // TODO(adonovan): make an ssa utility to construct an actual
+ // root function so we don't need to special-case site-less
+ // call edges.
+
+ // For each main package, call main.init(), main.main().
+ for _, mainPkg := range a.config.Mains {
+ main := mainPkg.Func("main")
+ if main == nil {
+ panic(fmt.Sprintf("%s has no main function", mainPkg))
+ }
+
+ targets := a.addOneNode(main.Signature, "root.targets", nil)
+ site := &callsite{targets: targets}
+ root.sites = append(root.sites, site)
+ for _, fn := range [2]*ssa.Function{mainPkg.Func("init"), main} {
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\troot call to %s:\n", fn)
+ }
+ a.copy(targets, a.valueNode(fn), 1)
+ }
+ }
+
+ return root
+}
+
+// genFunc generates constraints for function fn.
+func (a *analysis) genFunc(cgn *cgnode) {
+ fn := cgn.fn
+
+ impl := a.findIntrinsic(fn)
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\n\n==== Generating constraints for %s, %s\n", cgn, cgn.contour())
+
+ // Hack: don't display body if intrinsic.
+ if impl != nil {
+ fn2 := *cgn.fn // copy
+ fn2.Locals = nil
+ fn2.Blocks = nil
+ fn2.WriteTo(a.log)
+ } else {
+ cgn.fn.WriteTo(a.log)
+ }
+ }
+
+ if impl != nil {
+ impl(a, cgn)
+ return
+ }
+
+ if fn.Blocks == nil {
+ // External function with no intrinsic treatment.
+ // We'll warn about calls to such functions at the end.
+ return
+ }
+
+ if a.log != nil {
+ fmt.Fprintln(a.log, "; Creating nodes for local values")
+ }
+
+ a.localval = make(map[ssa.Value]nodeid)
+ a.localobj = make(map[ssa.Value]nodeid)
+
+ // The value nodes for the params are in the func object block.
+ params := a.funcParams(cgn.obj)
+ for _, p := range fn.Params {
+ a.setValueNode(p, params, cgn)
+ params += nodeid(a.sizeof(p.Type()))
+ }
+
+ // Free variables have global cardinality:
+ // the outer function sets them with MakeClosure;
+ // the inner function accesses them with FreeVar.
+ //
+ // TODO(adonovan): treat free vars context-sensitively.
+
+ // Create value nodes for all value instructions
+ // since SSA may contain forward references.
+ var space [10]*ssa.Value
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ switch instr := instr.(type) {
+ case *ssa.Range:
+ // do nothing: it has a funky type,
+ // and *ssa.Next does all the work.
+
+ case ssa.Value:
+ var comment string
+ if a.log != nil {
+ comment = instr.Name()
+ }
+ id := a.addNodes(instr.Type(), comment)
+ a.setValueNode(instr, id, cgn)
+ }
+
+ // Record all address-taken functions (for presolver).
+ rands := instr.Operands(space[:0])
+ if call, ok := instr.(ssa.CallInstruction); ok && !call.Common().IsInvoke() {
+ // Skip CallCommon.Value in "call" mode.
+ // TODO(adonovan): fix: relies on unspecified ordering. Specify it.
+ rands = rands[1:]
+ }
+ for _, rand := range rands {
+ if atf, ok := (*rand).(*ssa.Function); ok {
+ a.atFuncs[atf] = true
+ }
+ }
+ }
+ }
+
+ // Generate constraints for instructions.
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ a.genInstr(cgn, instr)
+ }
+ }
+
+ a.localval = nil
+ a.localobj = nil
+}
+
+// genMethodsOf generates nodes and constraints for all methods of type T.
+func (a *analysis) genMethodsOf(T types.Type) {
+ itf := isInterface(T)
+
+ // TODO(adonovan): can we skip this entirely if itf is true?
+ // I think so, but the answer may depend on reflection.
+ mset := a.prog.MethodSets.MethodSet(T)
+ for i, n := 0, mset.Len(); i < n; i++ {
+ m := a.prog.MethodValue(mset.At(i))
+ a.valueNode(m)
+
+ if !itf {
+ // Methods of concrete types are address-taken functions.
+ a.atFuncs[m] = true
+ }
+ }
+}
+
+// generate generates offline constraints for the entire program.
+func (a *analysis) generate() {
+ start("Constraint generation")
+ if a.log != nil {
+ fmt.Fprintln(a.log, "==== Generating constraints")
+ }
+
+ // Create a dummy node since we use the nodeid 0 for
+ // non-pointerlike variables.
+ a.addNodes(tInvalid, "(zero)")
+
+ // Create the global node for panic values.
+ a.panicNode = a.addNodes(tEface, "panic")
+
+ // Create nodes and constraints for all methods of reflect.rtype.
+ // (Shared contours are used by dynamic calls to reflect.Type
+ // methods---typically just String().)
+ if rtype := a.reflectRtypePtr; rtype != nil {
+ a.genMethodsOf(rtype)
+ }
+
+ root := a.genRootCalls()
+
+ if a.config.BuildCallGraph {
+ a.result.CallGraph = callgraph.New(root.fn)
+ }
+
+ // Create nodes and constraints for all methods of all types
+ // that are dynamically accessible via reflection or interfaces.
+ for _, T := range a.prog.RuntimeTypes() {
+ a.genMethodsOf(T)
+ }
+
+ // Generate constraints for entire program.
+ for len(a.genq) > 0 {
+ cgn := a.genq[0]
+ a.genq = a.genq[1:]
+ a.genFunc(cgn)
+ }
+
+ // The runtime magically allocates os.Args; so should we.
+ if os := a.prog.ImportedPackage("os"); os != nil {
+ // In effect: os.Args = new([1]string)[:]
+ T := types.NewSlice(types.Typ[types.String])
+ obj := a.addNodes(sliceToArray(T), "<command-line args>")
+ a.endObject(obj, nil, "<command-line args>")
+ a.addressOf(T, a.objectNode(nil, os.Var("Args")), obj)
+ }
+
+ // Discard generation state, to avoid confusion after node renumbering.
+ a.panicNode = 0
+ a.globalval = nil
+ a.localval = nil
+ a.localobj = nil
+
+ stop("Constraint generation")
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/hvn.go b/go/src/golang.org/x/tools/go/pointer/hvn.go
index a3ef1f3..e550bc9 100644
--- a/go/src/golang.org/x/tools/go/pointer/hvn.go
+++ b/go/src/golang.org/x/tools/go/pointer/hvn.go
@@ -1,3 +1,9 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
package pointer
// This file implements Hash-Value Numbering (HVN), a pre-solver
@@ -159,11 +165,11 @@
import (
"fmt"
+ "go/types"
"io"
"reflect"
"golang.org/x/tools/container/intsets"
- "golang.org/x/tools/go/types"
)
// A peLabel is a pointer-equivalence label: two nodes with the same
diff --git a/go/src/golang.org/x/tools/go/pointer/hvn14.go b/go/src/golang.org/x/tools/go/pointer/hvn14.go
new file mode 100644
index 0000000..bc38745
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/hvn14.go
@@ -0,0 +1,975 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+// This file implements Hash-Value Numbering (HVN), a pre-solver
+// constraint optimization described in Hardekopf & Lin, SAS'07 (see
+// doc.go) that analyses the graph topology to determine which sets of
+// variables are "pointer equivalent" (PE), i.e. must have identical
+// points-to sets in the solution.
+//
+// A separate ("offline") graph is constructed. Its nodes are those of
+// the main-graph, plus an additional node *X for each pointer node X.
+// With this graph we can reason about the unknown points-to set of
+// dereferenced pointers. (We do not generalize this to represent
+// unknown fields x->f, perhaps because such fields would be numerous,
+// though it might be worth an experiment.)
+//
+// Nodes whose points-to relations are not entirely captured by the
+// graph are marked as "indirect": the *X nodes, the parameters of
+// address-taken functions (which includes all functions in method
+// sets), or nodes updated by the solver rules for reflection, etc.
+//
+// All addr (y=&x) nodes are initially assigned a pointer-equivalence
+// (PE) label equal to x's nodeid in the main graph. (These are the
+// only PE labels that are less than len(a.nodes).)
+//
+// All offsetAddr (y=&x.f) constraints are initially assigned a PE
+// label; such labels are memoized, keyed by (x, f), so that equivalent
+// nodes y as assigned the same label.
+//
+// Then we process each strongly connected component (SCC) of the graph
+// in topological order, assigning it a PE label based on the set P of
+// PE labels that flow to it from its immediate dependencies.
+//
+// If any node in P is "indirect", the entire SCC is assigned a fresh PE
+// label. Otherwise:
+//
+// |P|=0 if P is empty, all nodes in the SCC are non-pointers (e.g.
+// uninitialized variables, or formal params of dead functions)
+// and the SCC is assigned the PE label of zero.
+//
+// |P|=1 if P is a singleton, the SCC is assigned the same label as the
+// sole element of P.
+//
+// |P|>1 if P contains multiple labels, a unique label representing P is
+// invented and recorded in an hash table, so that other
+// equivalent SCCs may also be assigned this label, akin to
+// conventional hash-value numbering in a compiler.
+//
+// Finally, a renumbering is computed such that each node is replaced by
+// the lowest-numbered node with the same PE label. All constraints are
+// renumbered, and any resulting duplicates are eliminated.
+//
+// The only nodes that are not renumbered are the objects x in addr
+// (y=&x) constraints, since the ids of these nodes (and fields derived
+// from them via offsetAddr rules) are the elements of all points-to
+// sets, so they must remain as they are if we want the same solution.
+//
+// The solverStates (node.solve) for nodes in the same equivalence class
+// are linked together so that all nodes in the class have the same
+// solution. This avoids the need to renumber nodeids buried in
+// Queries, cgnodes, etc (like (*analysis).renumber() does) since only
+// the solution is needed.
+//
+// The result of HVN is that the number of distinct nodes and
+// constraints is reduced, but the solution is identical (almost---see
+// CROSS-CHECK below). In particular, both linear and cyclic chains of
+// copies are each replaced by a single node.
+//
+// Nodes and constraints created "online" (e.g. while solving reflection
+// constraints) are not subject to this optimization.
+//
+// PERFORMANCE
+//
+// In two benchmarks (oracle and godoc), HVN eliminates about two thirds
+// of nodes, the majority accounted for by non-pointers: nodes of
+// non-pointer type, pointers that remain nil, formal parameters of dead
+// functions, nodes of untracked types, etc. It also reduces the number
+// of constraints, also by about two thirds, and the solving time by
+// 30--42%, although we must pay about 15% for the running time of HVN
+// itself. The benefit is greater for larger applications.
+//
+// There are many possible optimizations to improve the performance:
+// * Use fewer than 1:1 onodes to main graph nodes: many of the onodes
+// we create are not needed.
+// * HU (HVN with Union---see paper): coalesce "union" peLabels when
+// their expanded-out sets are equal.
+// * HR (HVN with deReference---see paper): this will require that we
+// apply HVN until fixed point, which may need more bookkeeping of the
+// correspondance of main nodes to onodes.
+// * Location Equivalence (see paper): have points-to sets contain not
+// locations but location-equivalence class labels, each representing
+// a set of locations.
+// * HVN with field-sensitive ref: model each of the fields of a
+// pointer-to-struct.
+//
+// CROSS-CHECK
+//
+// To verify the soundness of the optimization, when the
+// debugHVNCrossCheck option is enabled, we run the solver twice, once
+// before and once after running HVN, dumping the solution to disk, and
+// then we compare the results. If they are not identical, the analysis
+// panics.
+//
+// The solution dumped to disk includes only the N*N submatrix of the
+// complete solution where N is the number of nodes after generation.
+// In other words, we ignore pointer variables and objects created by
+// the solver itself, since their numbering depends on the solver order,
+// which is affected by the optimization. In any case, that's the only
+// part the client cares about.
+//
+// The cross-check is too strict and may fail spuriously. Although the
+// H&L paper describing HVN states that the solutions obtained should be
+// identical, this is not the case in practice because HVN can collapse
+// cycles involving *p even when pts(p)={}. Consider this example
+// distilled from testdata/hello.go:
+//
+// var x T
+// func f(p **T) {
+// t0 = *p
+// ...
+// t1 = φ(t0, &x)
+// *p = t1
+// }
+//
+// If f is dead code, we get:
+// unoptimized: pts(p)={} pts(t0)={} pts(t1)={&x}
+// optimized: pts(p)={} pts(t0)=pts(t1)=pts(*p)={&x}
+//
+// It's hard to argue that this is a bug: the result is sound and the
+// loss of precision is inconsequential---f is dead code, after all.
+// But unfortunately it limits the usefulness of the cross-check since
+// failures must be carefully analyzed. Ben Hardekopf suggests (in
+// personal correspondence) some approaches to mitigating it:
+//
+// If there is a node with an HVN points-to set that is a superset
+// of the NORM points-to set, then either it's a bug or it's a
+// result of this issue. If it's a result of this issue, then in
+// the offline constraint graph there should be a REF node inside
+// some cycle that reaches this node, and in the NORM solution the
+// pointer being dereferenced by that REF node should be the empty
+// set. If that isn't true then this is a bug. If it is true, then
+// you can further check that in the NORM solution the "extra"
+// points-to info in the HVN solution does in fact come from that
+// purported cycle (if it doesn't, then this is still a bug). If
+// you're doing the further check then you'll need to do it for
+// each "extra" points-to element in the HVN points-to set.
+//
+// There are probably ways to optimize these checks by taking
+// advantage of graph properties. For example, extraneous points-to
+// info will flow through the graph and end up in many
+// nodes. Rather than checking every node with extra info, you
+// could probably work out the "origin point" of the extra info and
+// just check there. Note that the check in the first bullet is
+// looking for soundness bugs, while the check in the second bullet
+// is looking for precision bugs; depending on your needs, you may
+// care more about one than the other.
+//
+// which we should evaluate. The cross-check is nonetheless invaluable
+// for all but one of the programs in the pointer_test suite.
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+
+ "golang.org/x/tools/container/intsets"
+ "golang.org/x/tools/go/types"
+)
+
+// A peLabel is a pointer-equivalence label: two nodes with the same
+// peLabel have identical points-to solutions.
+//
+// The numbers are allocated consecutively like so:
+// 0 not a pointer
+// 1..N-1 addrConstraints (equals the constraint's .src field, hence sparse)
+// ... offsetAddr constraints
+// ... SCCs (with indirect nodes or multiple inputs)
+//
+// Each PE label denotes a set of pointers containing a single addr, a
+// single offsetAddr, or some set of other PE labels.
+//
+type peLabel int
+
+type hvn struct {
+ a *analysis
+ N int // len(a.nodes) immediately after constraint generation
+ log io.Writer // (optional) log of HVN lemmas
+ onodes []*onode // nodes of the offline graph
+ label peLabel // the next available PE label
+ hvnLabel map[string]peLabel // hash-value numbering (PE label) for each set of onodeids
+ stack []onodeid // DFS stack
+ index int32 // next onode.index, from Tarjan's SCC algorithm
+
+ // For each distinct offsetAddrConstraint (src, offset) pair,
+ // offsetAddrLabels records a unique PE label >= N.
+ offsetAddrLabels map[offsetAddr]peLabel
+}
+
+// The index of an node in the offline graph.
+// (Currently the first N align with the main nodes,
+// but this may change with HRU.)
+type onodeid uint32
+
+// An onode is a node in the offline constraint graph.
+// (Where ambiguous, members of analysis.nodes are referred to as
+// "main graph" nodes.)
+//
+// Edges in the offline constraint graph (edges and implicit) point to
+// the source, i.e. against the flow of values: they are dependencies.
+// Implicit edges are used for SCC computation, but not for gathering
+// incoming labels.
+//
+type onode struct {
+ rep onodeid // index of representative of SCC in offline constraint graph
+
+ edges intsets.Sparse // constraint edges X-->Y (this onode is X)
+ implicit intsets.Sparse // implicit edges *X-->*Y (this onode is X)
+ peLabels intsets.Sparse // set of peLabels are pointer-equivalent to this one
+ indirect bool // node has points-to relations not represented in graph
+
+ // Tarjan's SCC algorithm
+ index, lowlink int32 // Tarjan numbering
+ scc int32 // -ve => on stack; 0 => unvisited; +ve => node is root of a found SCC
+}
+
+type offsetAddr struct {
+ ptr nodeid
+ offset uint32
+}
+
+// nextLabel issues the next unused pointer-equivalence label.
+func (h *hvn) nextLabel() peLabel {
+ h.label++
+ return h.label
+}
+
+// ref(X) returns the index of the onode for *X.
+func (h *hvn) ref(id onodeid) onodeid {
+ return id + onodeid(len(h.a.nodes))
+}
+
+// hvn computes pointer-equivalence labels (peLabels) using the Hash-based
+// Value Numbering (HVN) algorithm described in Hardekopf & Lin, SAS'07.
+//
+func (a *analysis) hvn() {
+ start("HVN")
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\n\n==== Pointer equivalence optimization\n\n")
+ }
+
+ h := hvn{
+ a: a,
+ N: len(a.nodes),
+ log: a.log,
+ hvnLabel: make(map[string]peLabel),
+ offsetAddrLabels: make(map[offsetAddr]peLabel),
+ }
+
+ if h.log != nil {
+ fmt.Fprintf(h.log, "\nCreating offline graph nodes...\n")
+ }
+
+ // Create offline nodes. The first N nodes correspond to main
+ // graph nodes; the next N are their corresponding ref() nodes.
+ h.onodes = make([]*onode, 2*h.N)
+ for id := range a.nodes {
+ id := onodeid(id)
+ h.onodes[id] = &onode{}
+ h.onodes[h.ref(id)] = &onode{indirect: true}
+ }
+
+ // Each node initially represents just itself.
+ for id, o := range h.onodes {
+ o.rep = onodeid(id)
+ }
+
+ h.markIndirectNodes()
+
+ // Reserve the first N PE labels for addrConstraints.
+ h.label = peLabel(h.N)
+
+ // Add offline constraint edges.
+ if h.log != nil {
+ fmt.Fprintf(h.log, "\nAdding offline graph edges...\n")
+ }
+ for _, c := range a.constraints {
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "; %s\n", c)
+ }
+ c.presolve(&h)
+ }
+
+ // Find and collapse SCCs.
+ if h.log != nil {
+ fmt.Fprintf(h.log, "\nFinding SCCs...\n")
+ }
+ h.index = 1
+ for id, o := range h.onodes {
+ if id > 0 && o.index == 0 {
+ // Start depth-first search at each unvisited node.
+ h.visit(onodeid(id))
+ }
+ }
+
+ // Dump the solution
+ // (NB: somewhat redundant with logging from simplify().)
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\nPointer equivalences:\n")
+ for id, o := range h.onodes {
+ if id == 0 {
+ continue
+ }
+ if id == int(h.N) {
+ fmt.Fprintf(h.log, "---\n")
+ }
+ fmt.Fprintf(h.log, "o%d\t", id)
+ if o.rep != onodeid(id) {
+ fmt.Fprintf(h.log, "rep=o%d", o.rep)
+ } else {
+ fmt.Fprintf(h.log, "p%d", o.peLabels.Min())
+ if o.indirect {
+ fmt.Fprint(h.log, " indirect")
+ }
+ }
+ fmt.Fprintln(h.log)
+ }
+ }
+
+ // Simplify the main constraint graph
+ h.simplify()
+
+ a.showCounts()
+
+ stop("HVN")
+}
+
+// ---- constraint-specific rules ----
+
+// dst := &src
+func (c *addrConstraint) presolve(h *hvn) {
+ // Each object (src) is an initial PE label.
+ label := peLabel(c.src) // label < N
+ if debugHVNVerbose && h.log != nil {
+ // duplicate log messages are possible
+ fmt.Fprintf(h.log, "\tcreate p%d: {&n%d}\n", label, c.src)
+ }
+ odst := onodeid(c.dst)
+ osrc := onodeid(c.src)
+
+ // Assign dst this label.
+ h.onodes[odst].peLabels.Insert(int(label))
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d has p%d\n", odst, label)
+ }
+
+ h.addImplicitEdge(h.ref(odst), osrc) // *dst ~~> src.
+}
+
+// dst = src
+func (c *copyConstraint) presolve(h *hvn) {
+ odst := onodeid(c.dst)
+ osrc := onodeid(c.src)
+ h.addEdge(odst, osrc) // dst --> src
+ h.addImplicitEdge(h.ref(odst), h.ref(osrc)) // *dst ~~> *src
+}
+
+// dst = *src + offset
+func (c *loadConstraint) presolve(h *hvn) {
+ odst := onodeid(c.dst)
+ osrc := onodeid(c.src)
+ if c.offset == 0 {
+ h.addEdge(odst, h.ref(osrc)) // dst --> *src
+ } else {
+ // We don't interpret load-with-offset, e.g. results
+ // of map value lookup, R-block of dynamic call, slice
+ // copy/append, reflection.
+ h.markIndirect(odst, "load with offset")
+ }
+}
+
+// *dst + offset = src
+func (c *storeConstraint) presolve(h *hvn) {
+ odst := onodeid(c.dst)
+ osrc := onodeid(c.src)
+ if c.offset == 0 {
+ h.onodes[h.ref(odst)].edges.Insert(int(osrc)) // *dst --> src
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d --> o%d\n", h.ref(odst), osrc)
+ }
+ } else {
+ // We don't interpret store-with-offset.
+ // See discussion of soundness at markIndirectNodes.
+ }
+}
+
+// dst = &src.offset
+func (c *offsetAddrConstraint) presolve(h *hvn) {
+ // Give each distinct (addr, offset) pair a fresh PE label.
+ // The cache performs CSE, effectively.
+ key := offsetAddr{c.src, c.offset}
+ label, ok := h.offsetAddrLabels[key]
+ if !ok {
+ label = h.nextLabel()
+ h.offsetAddrLabels[key] = label
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\tcreate p%d: {&n%d.#%d}\n",
+ label, c.src, c.offset)
+ }
+ }
+
+ // Assign dst this label.
+ h.onodes[c.dst].peLabels.Insert(int(label))
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d has p%d\n", c.dst, label)
+ }
+}
+
+// dst = src.(typ) where typ is an interface
+func (c *typeFilterConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.dst), "typeFilter result")
+}
+
+// dst = src.(typ) where typ is concrete
+func (c *untagConstraint) presolve(h *hvn) {
+ odst := onodeid(c.dst)
+ for end := odst + onodeid(h.a.sizeof(c.typ)); odst < end; odst++ {
+ h.markIndirect(odst, "untag result")
+ }
+}
+
+// dst = src.method(c.params...)
+func (c *invokeConstraint) presolve(h *hvn) {
+ // All methods are address-taken functions, so
+ // their formal P-blocks were already marked indirect.
+
+ // Mark the caller's targets node as indirect.
+ sig := c.method.Type().(*types.Signature)
+ id := c.params
+ h.markIndirect(onodeid(c.params), "invoke targets node")
+ id++
+
+ id += nodeid(h.a.sizeof(sig.Params()))
+
+ // Mark the caller's R-block as indirect.
+ end := id + nodeid(h.a.sizeof(sig.Results()))
+ for id < end {
+ h.markIndirect(onodeid(id), "invoke R-block")
+ id++
+ }
+}
+
+// markIndirectNodes marks as indirect nodes whose points-to relations
+// are not entirely captured by the offline graph, including:
+//
+// (a) All address-taken nodes (including the following nodes within
+// the same object). This is described in the paper.
+//
+// The most subtle cause of indirect nodes is the generation of
+// store-with-offset constraints since the offline graph doesn't
+// represent them. A global audit of constraint generation reveals the
+// following uses of store-with-offset:
+//
+// (b) genDynamicCall, for P-blocks of dynamically called functions,
+// to which dynamic copy edges will be added to them during
+// solving: from storeConstraint for standalone functions,
+// and from invokeConstraint for methods.
+// All such P-blocks must be marked indirect.
+// (c) MakeUpdate, to update the value part of a map object.
+// All MakeMap objects's value parts must be marked indirect.
+// (d) copyElems, to update the destination array.
+// All array elements must be marked indirect.
+//
+// Not all indirect marking happens here. ref() nodes are marked
+// indirect at construction, and each constraint's presolve() method may
+// mark additional nodes.
+//
+func (h *hvn) markIndirectNodes() {
+ // (a) all address-taken nodes, plus all nodes following them
+ // within the same object, since these may be indirectly
+ // stored or address-taken.
+ for _, c := range h.a.constraints {
+ if c, ok := c.(*addrConstraint); ok {
+ start := h.a.enclosingObj(c.src)
+ end := start + nodeid(h.a.nodes[start].obj.size)
+ for id := c.src; id < end; id++ {
+ h.markIndirect(onodeid(id), "A-T object")
+ }
+ }
+ }
+
+ // (b) P-blocks of all address-taken functions.
+ for id := 0; id < h.N; id++ {
+ obj := h.a.nodes[id].obj
+
+ // TODO(adonovan): opt: if obj.cgn.fn is a method and
+ // obj.cgn is not its shared contour, this is an
+ // "inlined" static method call. We needn't consider it
+ // address-taken since no invokeConstraint will affect it.
+
+ if obj != nil && obj.flags&otFunction != 0 && h.a.atFuncs[obj.cgn.fn] {
+ // address-taken function
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "n%d is address-taken: %s\n", id, obj.cgn.fn)
+ }
+ h.markIndirect(onodeid(id), "A-T func identity")
+ id++
+ sig := obj.cgn.fn.Signature
+ psize := h.a.sizeof(sig.Params())
+ if sig.Recv() != nil {
+ psize += h.a.sizeof(sig.Recv().Type())
+ }
+ for end := id + int(psize); id < end; id++ {
+ h.markIndirect(onodeid(id), "A-T func P-block")
+ }
+ id--
+ continue
+ }
+ }
+
+ // (c) all map objects' value fields.
+ for _, id := range h.a.mapValues {
+ h.markIndirect(onodeid(id), "makemap.value")
+ }
+
+ // (d) all array element objects.
+ // TODO(adonovan): opt: can we do better?
+ for id := 0; id < h.N; id++ {
+ // Identity node for an object of array type?
+ if tArray, ok := h.a.nodes[id].typ.(*types.Array); ok {
+ // Mark the array element nodes indirect.
+ // (Skip past the identity field.)
+ for _ = range h.a.flatten(tArray.Elem()) {
+ id++
+ h.markIndirect(onodeid(id), "array elem")
+ }
+ }
+ }
+}
+
+func (h *hvn) markIndirect(oid onodeid, comment string) {
+ h.onodes[oid].indirect = true
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d is indirect: %s\n", oid, comment)
+ }
+}
+
+// Adds an edge dst-->src.
+// Note the unusual convention: edges are dependency (contraflow) edges.
+func (h *hvn) addEdge(odst, osrc onodeid) {
+ h.onodes[odst].edges.Insert(int(osrc))
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d --> o%d\n", odst, osrc)
+ }
+}
+
+func (h *hvn) addImplicitEdge(odst, osrc onodeid) {
+ h.onodes[odst].implicit.Insert(int(osrc))
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d ~~> o%d\n", odst, osrc)
+ }
+}
+
+// visit implements the depth-first search of Tarjan's SCC algorithm.
+// Precondition: x is canonical.
+func (h *hvn) visit(x onodeid) {
+ h.checkCanonical(x)
+ xo := h.onodes[x]
+ xo.index = h.index
+ xo.lowlink = h.index
+ h.index++
+
+ h.stack = append(h.stack, x) // push
+ assert(xo.scc == 0, "node revisited")
+ xo.scc = -1
+
+ var deps []int
+ deps = xo.edges.AppendTo(deps)
+ deps = xo.implicit.AppendTo(deps)
+
+ for _, y := range deps {
+ // Loop invariant: x is canonical.
+
+ y := h.find(onodeid(y))
+
+ if x == y {
+ continue // nodes already coalesced
+ }
+
+ xo := h.onodes[x]
+ yo := h.onodes[y]
+
+ switch {
+ case yo.scc > 0:
+ // y is already a collapsed SCC
+
+ case yo.scc < 0:
+ // y is on the stack, and thus in the current SCC.
+ if yo.index < xo.lowlink {
+ xo.lowlink = yo.index
+ }
+
+ default:
+ // y is unvisited; visit it now.
+ h.visit(y)
+ // Note: x and y are now non-canonical.
+
+ x = h.find(onodeid(x))
+
+ if yo.lowlink < xo.lowlink {
+ xo.lowlink = yo.lowlink
+ }
+ }
+ }
+ h.checkCanonical(x)
+
+ // Is x the root of an SCC?
+ if xo.lowlink == xo.index {
+ // Coalesce all nodes in the SCC.
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "scc o%d\n", x)
+ }
+ for {
+ // Pop y from stack.
+ i := len(h.stack) - 1
+ y := h.stack[i]
+ h.stack = h.stack[:i]
+
+ h.checkCanonical(x)
+ xo := h.onodes[x]
+ h.checkCanonical(y)
+ yo := h.onodes[y]
+
+ if xo == yo {
+ // SCC is complete.
+ xo.scc = 1
+ h.labelSCC(x)
+ break
+ }
+ h.coalesce(x, y)
+ }
+ }
+}
+
+// Precondition: x is canonical.
+func (h *hvn) labelSCC(x onodeid) {
+ h.checkCanonical(x)
+ xo := h.onodes[x]
+ xpe := &xo.peLabels
+
+ // All indirect nodes get new labels.
+ if xo.indirect {
+ label := h.nextLabel()
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\tcreate p%d: indirect SCC\n", label)
+ fmt.Fprintf(h.log, "\to%d has p%d\n", x, label)
+ }
+
+ // Remove pre-labeling, in case a direct pre-labeled node was
+ // merged with an indirect one.
+ xpe.Clear()
+ xpe.Insert(int(label))
+
+ return
+ }
+
+ // Invariant: all peLabels sets are non-empty.
+ // Those that are logically empty contain zero as their sole element.
+ // No other sets contains zero.
+
+ // Find all labels coming in to the coalesced SCC node.
+ for _, y := range xo.edges.AppendTo(nil) {
+ y := h.find(onodeid(y))
+ if y == x {
+ continue // already coalesced
+ }
+ ype := &h.onodes[y].peLabels
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\tedge from o%d = %s\n", y, ype)
+ }
+
+ if ype.IsEmpty() {
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\tnode has no PE label\n")
+ }
+ }
+ assert(!ype.IsEmpty(), "incoming node has no PE label")
+
+ if ype.Has(0) {
+ // {0} represents a non-pointer.
+ assert(ype.Len() == 1, "PE set contains {0, ...}")
+ } else {
+ xpe.UnionWith(ype)
+ }
+ }
+
+ switch xpe.Len() {
+ case 0:
+ // SCC has no incoming non-zero PE labels: it is a non-pointer.
+ xpe.Insert(0)
+
+ case 1:
+ // already a singleton
+
+ default:
+ // SCC has multiple incoming non-zero PE labels.
+ // Find the canonical label representing this set.
+ // We use String() as a fingerprint consistent with Equals().
+ key := xpe.String()
+ label, ok := h.hvnLabel[key]
+ if !ok {
+ label = h.nextLabel()
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\tcreate p%d: union %s\n", label, xpe.String())
+ }
+ h.hvnLabel[key] = label
+ }
+ xpe.Clear()
+ xpe.Insert(int(label))
+ }
+
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\to%d has p%d\n", x, xpe.Min())
+ }
+}
+
+// coalesce combines two nodes in the offline constraint graph.
+// Precondition: x and y are canonical.
+func (h *hvn) coalesce(x, y onodeid) {
+ xo := h.onodes[x]
+ yo := h.onodes[y]
+
+ // x becomes y's canonical representative.
+ yo.rep = x
+
+ if debugHVNVerbose && h.log != nil {
+ fmt.Fprintf(h.log, "\tcoalesce o%d into o%d\n", y, x)
+ }
+
+ // x accumulates y's edges.
+ xo.edges.UnionWith(&yo.edges)
+ yo.edges.Clear()
+
+ // x accumulates y's implicit edges.
+ xo.implicit.UnionWith(&yo.implicit)
+ yo.implicit.Clear()
+
+ // x accumulates y's pointer-equivalence labels.
+ xo.peLabels.UnionWith(&yo.peLabels)
+ yo.peLabels.Clear()
+
+ // x accumulates y's indirect flag.
+ if yo.indirect {
+ xo.indirect = true
+ }
+}
+
+// simplify computes a degenerate renumbering of nodeids from the PE
+// labels assigned by the hvn, and uses it to simplify the main
+// constraint graph, eliminating non-pointer nodes and duplicate
+// constraints.
+//
+func (h *hvn) simplify() {
+ // canon maps each peLabel to its canonical main node.
+ canon := make([]nodeid, h.label)
+ for i := range canon {
+ canon[i] = nodeid(h.N) // indicates "unset"
+ }
+
+ // mapping maps each main node index to the index of the canonical node.
+ mapping := make([]nodeid, len(h.a.nodes))
+
+ for id := range h.a.nodes {
+ id := nodeid(id)
+ if id == 0 {
+ canon[0] = 0
+ mapping[0] = 0
+ continue
+ }
+ oid := h.find(onodeid(id))
+ peLabels := &h.onodes[oid].peLabels
+ assert(peLabels.Len() == 1, "PE class is not a singleton")
+ label := peLabel(peLabels.Min())
+
+ canonId := canon[label]
+ if canonId == nodeid(h.N) {
+ // id becomes the representative of the PE label.
+ canonId = id
+ canon[label] = canonId
+
+ if h.a.log != nil {
+ fmt.Fprintf(h.a.log, "\tpts(n%d) is canonical : \t(%s)\n",
+ id, h.a.nodes[id].typ)
+ }
+
+ } else {
+ // Link the solver states for the two nodes.
+ assert(h.a.nodes[canonId].solve != nil, "missing solver state")
+ h.a.nodes[id].solve = h.a.nodes[canonId].solve
+
+ if h.a.log != nil {
+ // TODO(adonovan): debug: reorganize the log so it prints
+ // one line:
+ // pe y = x1, ..., xn
+ // for each canonical y. Requires allocation.
+ fmt.Fprintf(h.a.log, "\tpts(n%d) = pts(n%d) : %s\n",
+ id, canonId, h.a.nodes[id].typ)
+ }
+ }
+
+ mapping[id] = canonId
+ }
+
+ // Renumber the constraints, eliminate duplicates, and eliminate
+ // any containing non-pointers (n0).
+ addrs := make(map[addrConstraint]bool)
+ copys := make(map[copyConstraint]bool)
+ loads := make(map[loadConstraint]bool)
+ stores := make(map[storeConstraint]bool)
+ offsetAddrs := make(map[offsetAddrConstraint]bool)
+ untags := make(map[untagConstraint]bool)
+ typeFilters := make(map[typeFilterConstraint]bool)
+ invokes := make(map[invokeConstraint]bool)
+
+ nbefore := len(h.a.constraints)
+ cc := h.a.constraints[:0] // in-situ compaction
+ for _, c := range h.a.constraints {
+ // Renumber.
+ switch c := c.(type) {
+ case *addrConstraint:
+ // Don't renumber c.src since it is the label of
+ // an addressable object and will appear in PT sets.
+ c.dst = mapping[c.dst]
+ default:
+ c.renumber(mapping)
+ }
+
+ if c.ptr() == 0 {
+ continue // skip: constraint attached to non-pointer
+ }
+
+ var dup bool
+ switch c := c.(type) {
+ case *addrConstraint:
+ _, dup = addrs[*c]
+ addrs[*c] = true
+
+ case *copyConstraint:
+ if c.src == c.dst {
+ continue // skip degenerate copies
+ }
+ if c.src == 0 {
+ continue // skip copy from non-pointer
+ }
+ _, dup = copys[*c]
+ copys[*c] = true
+
+ case *loadConstraint:
+ if c.src == 0 {
+ continue // skip load from non-pointer
+ }
+ _, dup = loads[*c]
+ loads[*c] = true
+
+ case *storeConstraint:
+ if c.src == 0 {
+ continue // skip store from non-pointer
+ }
+ _, dup = stores[*c]
+ stores[*c] = true
+
+ case *offsetAddrConstraint:
+ if c.src == 0 {
+ continue // skip offset from non-pointer
+ }
+ _, dup = offsetAddrs[*c]
+ offsetAddrs[*c] = true
+
+ case *untagConstraint:
+ if c.src == 0 {
+ continue // skip untag of non-pointer
+ }
+ _, dup = untags[*c]
+ untags[*c] = true
+
+ case *typeFilterConstraint:
+ if c.src == 0 {
+ continue // skip filter of non-pointer
+ }
+ _, dup = typeFilters[*c]
+ typeFilters[*c] = true
+
+ case *invokeConstraint:
+ if c.params == 0 {
+ panic("non-pointer invoke.params")
+ }
+ if c.iface == 0 {
+ continue // skip invoke on non-pointer
+ }
+ _, dup = invokes[*c]
+ invokes[*c] = true
+
+ default:
+ // We don't bother de-duping advanced constraints
+ // (e.g. reflection) since they are uncommon.
+
+ // Eliminate constraints containing non-pointer nodeids.
+ //
+ // We use reflection to find the fields to avoid
+ // adding yet another method to constraint.
+ //
+ // TODO(adonovan): experiment with a constraint
+ // method that returns a slice of pointers to
+ // nodeids fields to enable uniform iteration;
+ // the renumber() method could be removed and
+ // implemented using the new one.
+ //
+ // TODO(adonovan): opt: this is unsound since
+ // some constraints still have an effect if one
+ // of the operands is zero: rVCall, rVMapIndex,
+ // rvSetMapIndex. Handle them specially.
+ rtNodeid := reflect.TypeOf(nodeid(0))
+ x := reflect.ValueOf(c).Elem()
+ for i, nf := 0, x.NumField(); i < nf; i++ {
+ f := x.Field(i)
+ if f.Type() == rtNodeid {
+ if f.Uint() == 0 {
+ dup = true // skip it
+ break
+ }
+ }
+ }
+ }
+ if dup {
+ continue // skip duplicates
+ }
+
+ cc = append(cc, c)
+ }
+ h.a.constraints = cc
+
+ if h.log != nil {
+ fmt.Fprintf(h.log, "#constraints: was %d, now %d\n", nbefore, len(h.a.constraints))
+ }
+}
+
+// find returns the canonical onodeid for x.
+// (The onodes form a disjoint set forest.)
+func (h *hvn) find(x onodeid) onodeid {
+ // TODO(adonovan): opt: this is a CPU hotspot. Try "union by rank".
+ xo := h.onodes[x]
+ rep := xo.rep
+ if rep != x {
+ rep = h.find(rep) // simple path compression
+ xo.rep = rep
+ }
+ return rep
+}
+
+func (h *hvn) checkCanonical(x onodeid) {
+ if debugHVN {
+ assert(x == h.find(x), "not canonical")
+ }
+}
+
+func assert(p bool, msg string) {
+ if debugHVN && !p {
+ panic("assertion failed: " + msg)
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/intrinsics.go b/go/src/golang.org/x/tools/go/pointer/intrinsics.go
index 251c0e2..fbfb36d 100644
--- a/go/src/golang.org/x/tools/go/pointer/intrinsics.go
+++ b/go/src/golang.org/x/tools/go/pointer/intrinsics.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
// This package defines the treatment of intrinsics, i.e. library
@@ -17,9 +19,9 @@
import (
"fmt"
+ "go/types"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// Instances of 'intrinsic' generate analysis constraints for calls to
@@ -211,7 +213,7 @@
return false // "reflect" package not loaded
}
reflectPackage := a.reflectValueObj.Pkg()
- if fn.Pkg != nil && fn.Pkg.Object == reflectPackage {
+ if fn.Pkg != nil && fn.Pkg.Pkg == reflectPackage {
return true
}
// Synthetic wrappers have a nil Pkg, so they slip through the
diff --git a/go/src/golang.org/x/tools/go/pointer/intrinsics14.go b/go/src/golang.org/x/tools/go/pointer/intrinsics14.go
new file mode 100644
index 0000000..5e108d2
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/intrinsics14.go
@@ -0,0 +1,382 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+// This package defines the treatment of intrinsics, i.e. library
+// functions requiring special analytical treatment.
+//
+// Most of these are C or assembly functions, but even some Go
+// functions require may special treatment if the analysis completely
+// replaces the implementation of an API such as reflection.
+
+// TODO(adonovan): support a means of writing analytic summaries in
+// the target code, so that users can summarise the effects of their
+// own C functions using a snippet of Go.
+
+import (
+ "fmt"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+// Instances of 'intrinsic' generate analysis constraints for calls to
+// intrinsic functions.
+// Implementations may exploit information from the calling site
+// via cgn.callersite; for shared contours this is nil.
+type intrinsic func(a *analysis, cgn *cgnode)
+
+// Initialized in explicit init() to defeat (spurious) initialization
+// cycle error.
+var intrinsicsByName = make(map[string]intrinsic)
+
+func init() {
+ // Key strings are from Function.String().
+ // That little dot Û° is an Arabic zero numeral (U+06F0),
+ // categories [Nd].
+ for name, fn := range map[string]intrinsic{
+ // Other packages.
+ "bytes.Equal": extÛ°NoEffect,
+ "bytes.IndexByte": extÛ°NoEffect,
+ "crypto/aes.decryptBlockAsm": extÛ°NoEffect,
+ "crypto/aes.encryptBlockAsm": extÛ°NoEffect,
+ "crypto/aes.expandKeyAsm": extÛ°NoEffect,
+ "crypto/aes.hasAsm": extÛ°NoEffect,
+ "crypto/md5.block": extÛ°NoEffect,
+ "crypto/rc4.xorKeyStream": extÛ°NoEffect,
+ "crypto/sha1.block": extÛ°NoEffect,
+ "crypto/sha256.block": extÛ°NoEffect,
+ "hash/crc32.castagnoliSSE42": extÛ°NoEffect,
+ "hash/crc32.haveSSE42": extÛ°NoEffect,
+ "math.Abs": extÛ°NoEffect,
+ "math.Acos": extÛ°NoEffect,
+ "math.Asin": extÛ°NoEffect,
+ "math.Atan": extÛ°NoEffect,
+ "math.Atan2": extÛ°NoEffect,
+ "math.Ceil": extÛ°NoEffect,
+ "math.Cos": extÛ°NoEffect,
+ "math.Dim": extÛ°NoEffect,
+ "math.Exp": extÛ°NoEffect,
+ "math.Exp2": extÛ°NoEffect,
+ "math.Expm1": extÛ°NoEffect,
+ "math.Float32bits": extÛ°NoEffect,
+ "math.Float32frombits": extÛ°NoEffect,
+ "math.Float64bits": extÛ°NoEffect,
+ "math.Float64frombits": extÛ°NoEffect,
+ "math.Floor": extÛ°NoEffect,
+ "math.Frexp": extÛ°NoEffect,
+ "math.Hypot": extÛ°NoEffect,
+ "math.Ldexp": extÛ°NoEffect,
+ "math.Log": extÛ°NoEffect,
+ "math.Log10": extÛ°NoEffect,
+ "math.Log1p": extÛ°NoEffect,
+ "math.Log2": extÛ°NoEffect,
+ "math.Max": extÛ°NoEffect,
+ "math.Min": extÛ°NoEffect,
+ "math.Mod": extÛ°NoEffect,
+ "math.Modf": extÛ°NoEffect,
+ "math.Remainder": extÛ°NoEffect,
+ "math.Sin": extÛ°NoEffect,
+ "math.Sincos": extÛ°NoEffect,
+ "math.Sqrt": extÛ°NoEffect,
+ "math.Tan": extÛ°NoEffect,
+ "math.Trunc": extÛ°NoEffect,
+ "math/big.addMulVVW": extÛ°NoEffect,
+ "math/big.addVV": extÛ°NoEffect,
+ "math/big.addVW": extÛ°NoEffect,
+ "math/big.bitLen": extÛ°NoEffect,
+ "math/big.divWVW": extÛ°NoEffect,
+ "math/big.divWW": extÛ°NoEffect,
+ "math/big.mulAddVWW": extÛ°NoEffect,
+ "math/big.mulWW": extÛ°NoEffect,
+ "math/big.shlVU": extÛ°NoEffect,
+ "math/big.shrVU": extÛ°NoEffect,
+ "math/big.subVV": extÛ°NoEffect,
+ "math/big.subVW": extÛ°NoEffect,
+ "net.runtime_Semacquire": extÛ°NoEffect,
+ "net.runtime_Semrelease": extÛ°NoEffect,
+ "net.runtime_pollClose": extÛ°NoEffect,
+ "net.runtime_pollOpen": extÛ°NoEffect,
+ "net.runtime_pollReset": extÛ°NoEffect,
+ "net.runtime_pollServerInit": extÛ°NoEffect,
+ "net.runtime_pollSetDeadline": extÛ°NoEffect,
+ "net.runtime_pollUnblock": extÛ°NoEffect,
+ "net.runtime_pollWait": extÛ°NoEffect,
+ "net.runtime_pollWaitCanceled": extÛ°NoEffect,
+ "os.epipecheck": extÛ°NoEffect,
+ "runtime.BlockProfile": extÛ°NoEffect,
+ "runtime.Breakpoint": extÛ°NoEffect,
+ "runtime.CPUProfile": extÛ°NoEffect, // good enough
+ "runtime.Caller": extÛ°NoEffect,
+ "runtime.Callers": extÛ°NoEffect, // good enough
+ "runtime.FuncForPC": extÛ°NoEffect,
+ "runtime.GC": extÛ°NoEffect,
+ "runtime.GOMAXPROCS": extÛ°NoEffect,
+ "runtime.Goexit": extÛ°NoEffect,
+ "runtime.GoroutineProfile": extÛ°NoEffect,
+ "runtime.Gosched": extÛ°NoEffect,
+ "runtime.MemProfile": extÛ°NoEffect,
+ "runtime.NumCPU": extÛ°NoEffect,
+ "runtime.NumGoroutine": extÛ°NoEffect,
+ "runtime.ReadMemStats": extÛ°NoEffect,
+ "runtime.SetBlockProfileRate": extÛ°NoEffect,
+ "runtime.SetCPUProfileRate": extÛ°NoEffect,
+ "runtime.SetFinalizer": extÛ°runtimeÛ°SetFinalizer,
+ "runtime.Stack": extÛ°NoEffect,
+ "runtime.ThreadCreateProfile": extÛ°NoEffect,
+ "runtime.cstringToGo": extÛ°NoEffect,
+ "runtime.funcentry_go": extÛ°NoEffect,
+ "runtime.funcline_go": extÛ°NoEffect,
+ "runtime.funcname_go": extÛ°NoEffect,
+ "runtime.getgoroot": extÛ°NoEffect,
+ "runtime/pprof.runtime_cyclesPerSecond": extÛ°NoEffect,
+ "strings.IndexByte": extÛ°NoEffect,
+ "sync.runtime_Semacquire": extÛ°NoEffect,
+ "sync.runtime_Semrelease": extÛ°NoEffect,
+ "sync.runtime_Syncsemacquire": extÛ°NoEffect,
+ "sync.runtime_Syncsemcheck": extÛ°NoEffect,
+ "sync.runtime_Syncsemrelease": extÛ°NoEffect,
+ "sync.runtime_procPin": extÛ°NoEffect,
+ "sync.runtime_procUnpin": extÛ°NoEffect,
+ "sync.runtime_registerPool": extÛ°NoEffect,
+ "sync/atomic.AddInt32": extÛ°NoEffect,
+ "sync/atomic.AddInt64": extÛ°NoEffect,
+ "sync/atomic.AddUint32": extÛ°NoEffect,
+ "sync/atomic.AddUint64": extÛ°NoEffect,
+ "sync/atomic.AddUintptr": extÛ°NoEffect,
+ "sync/atomic.CompareAndSwapInt32": extÛ°NoEffect,
+ "sync/atomic.CompareAndSwapUint32": extÛ°NoEffect,
+ "sync/atomic.CompareAndSwapUint64": extÛ°NoEffect,
+ "sync/atomic.CompareAndSwapUintptr": extÛ°NoEffect,
+ "sync/atomic.LoadInt32": extÛ°NoEffect,
+ "sync/atomic.LoadInt64": extÛ°NoEffect,
+ "sync/atomic.LoadPointer": extÛ°NoEffect, // ignore unsafe.Pointers
+ "sync/atomic.LoadUint32": extÛ°NoEffect,
+ "sync/atomic.LoadUint64": extÛ°NoEffect,
+ "sync/atomic.LoadUintptr": extÛ°NoEffect,
+ "sync/atomic.StoreInt32": extÛ°NoEffect,
+ "sync/atomic.StorePointer": extÛ°NoEffect, // ignore unsafe.Pointers
+ "sync/atomic.StoreUint32": extÛ°NoEffect,
+ "sync/atomic.StoreUintptr": extÛ°NoEffect,
+ "syscall.Close": extÛ°NoEffect,
+ "syscall.Exit": extÛ°NoEffect,
+ "syscall.Getpid": extÛ°NoEffect,
+ "syscall.Getwd": extÛ°NoEffect,
+ "syscall.Kill": extÛ°NoEffect,
+ "syscall.RawSyscall": extÛ°NoEffect,
+ "syscall.RawSyscall6": extÛ°NoEffect,
+ "syscall.Syscall": extÛ°NoEffect,
+ "syscall.Syscall6": extÛ°NoEffect,
+ "syscall.runtime_AfterFork": extÛ°NoEffect,
+ "syscall.runtime_BeforeFork": extÛ°NoEffect,
+ "syscall.setenv_c": extÛ°NoEffect,
+ "time.Sleep": extÛ°NoEffect,
+ "time.now": extÛ°NoEffect,
+ "time.startTimer": extÛ°timeÛ°startTimer,
+ "time.stopTimer": extÛ°NoEffect,
+ } {
+ intrinsicsByName[name] = fn
+ }
+}
+
+// findIntrinsic returns the constraint generation function for an
+// intrinsic function fn, or nil if the function should be handled normally.
+//
+func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
+ // Consult the *Function-keyed cache.
+ // A cached nil indicates a normal non-intrinsic function.
+ impl, ok := a.intrinsics[fn]
+ if !ok {
+ impl = intrinsicsByName[fn.String()] // may be nil
+
+ if a.isReflect(fn) {
+ if !a.config.Reflection {
+ impl = extÛ°NoEffect // reflection disabled
+ } else if impl == nil {
+ // Ensure all "reflect" code is treated intrinsically.
+ impl = extÛ°NotYetImplemented
+ }
+ }
+
+ a.intrinsics[fn] = impl
+ }
+ return impl
+}
+
+// isReflect reports whether fn belongs to the "reflect" package.
+func (a *analysis) isReflect(fn *ssa.Function) bool {
+ if a.reflectValueObj == nil {
+ return false // "reflect" package not loaded
+ }
+ reflectPackage := a.reflectValueObj.Pkg()
+ if fn.Pkg != nil && fn.Pkg.Pkg == reflectPackage {
+ return true
+ }
+ // Synthetic wrappers have a nil Pkg, so they slip through the
+ // previous check. Check the receiver package.
+ // TODO(adonovan): should synthetic wrappers have a non-nil Pkg?
+ if recv := fn.Signature.Recv(); recv != nil {
+ if named, ok := deref(recv.Type()).(*types.Named); ok {
+ if named.Obj().Pkg() == reflectPackage {
+ return true // e.g. wrapper of (reflect.Value).f
+ }
+ }
+ }
+ return false
+}
+
+// A trivial intrinsic suitable for any function that does not:
+// 1) induce aliases between its arguments or any global variables;
+// 2) call any functions; or
+// 3) create any labels.
+//
+// Many intrinsics (such as CompareAndSwapInt32) have a fourth kind of
+// effect: loading or storing through a pointer. Though these could
+// be significant, we deliberately ignore them because they are
+// generally not worth the effort.
+//
+// We sometimes violate condition #3 if the function creates only
+// non-function labels, as the control-flow graph is still sound.
+//
+func extÛ°NoEffect(a *analysis, cgn *cgnode) {}
+
+func extÛ°NotYetImplemented(a *analysis, cgn *cgnode) {
+ fn := cgn.fn
+ a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn)
+}
+
+// ---------- func runtime.SetFinalizer(x, f interface{}) ----------
+
+// runtime.SetFinalizer(x, f)
+type runtimeSetFinalizerConstraint struct {
+ targets nodeid // (indirect)
+ f nodeid // (ptr)
+ x nodeid
+}
+
+func (c *runtimeSetFinalizerConstraint) ptr() nodeid { return c.f }
+func (c *runtimeSetFinalizerConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.targets), "SetFinalizer.targets")
+}
+func (c *runtimeSetFinalizerConstraint) renumber(mapping []nodeid) {
+ c.targets = mapping[c.targets]
+ c.f = mapping[c.f]
+ c.x = mapping[c.x]
+}
+
+func (c *runtimeSetFinalizerConstraint) String() string {
+ return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
+}
+
+func (c *runtimeSetFinalizerConstraint) solve(a *analysis, delta *nodeset) {
+ for _, fObj := range delta.AppendTo(a.deltaSpace) {
+ tDyn, f, indirect := a.taggedValue(nodeid(fObj))
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ tSig, ok := tDyn.Underlying().(*types.Signature)
+ if !ok {
+ continue // not a function
+ }
+ if tSig.Recv() != nil {
+ panic(tSig)
+ }
+ if tSig.Params().Len() != 1 {
+ continue // not a unary function
+ }
+
+ // Extract x to tmp.
+ tx := tSig.Params().At(0).Type()
+ tmp := a.addNodes(tx, "SetFinalizer.tmp")
+ a.typeAssert(tx, tmp, c.x, false)
+
+ // Call f(tmp).
+ a.store(f, tmp, 1, a.sizeof(tx))
+
+ // Add dynamic call target.
+ if a.onlineCopy(c.targets, f) {
+ a.addWork(c.targets)
+ }
+ }
+}
+
+func extÛ°runtimeÛ°SetFinalizer(a *analysis, cgn *cgnode) {
+ // This is the shared contour, used for dynamic calls.
+ targets := a.addOneNode(tInvalid, "SetFinalizer.targets", nil)
+ cgn.sites = append(cgn.sites, &callsite{targets: targets})
+ params := a.funcParams(cgn.obj)
+ a.addConstraint(&runtimeSetFinalizerConstraint{
+ targets: targets,
+ x: params,
+ f: params + 1,
+ })
+}
+
+// ---------- func time.startTimer(t *runtimeTimer) ----------
+
+// time.StartTimer(t)
+type timeStartTimerConstraint struct {
+ targets nodeid // (indirect)
+ t nodeid // (ptr)
+}
+
+func (c *timeStartTimerConstraint) ptr() nodeid { return c.t }
+func (c *timeStartTimerConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.targets), "StartTimer.targets")
+}
+func (c *timeStartTimerConstraint) renumber(mapping []nodeid) {
+ c.targets = mapping[c.targets]
+ c.t = mapping[c.t]
+}
+
+func (c *timeStartTimerConstraint) String() string {
+ return fmt.Sprintf("time.startTimer(n%d)", c.t)
+}
+
+func (c *timeStartTimerConstraint) solve(a *analysis, delta *nodeset) {
+ for _, tObj := range delta.AppendTo(a.deltaSpace) {
+ t := nodeid(tObj)
+
+ // We model startTimer as if it was defined thus:
+ // func startTimer(t *runtimeTimer) { t.f(t.arg) }
+
+ // We hard-code the field offsets of time.runtimeTimer:
+ // type runtimeTimer struct {
+ // 0 __identity__
+ // 1 i int32
+ // 2 when int64
+ // 3 period int64
+ // 4 f func(int64, interface{})
+ // 5 arg interface{}
+ // }
+ f := t + 4
+ arg := t + 5
+
+ // store t.arg to t.f.params[0]
+ // (offset 1 => skip identity)
+ a.store(f, arg, 1, 1)
+
+ // Add dynamic call target.
+ if a.onlineCopy(c.targets, f) {
+ a.addWork(c.targets)
+ }
+ }
+}
+
+func extÛ°timeÛ°startTimer(a *analysis, cgn *cgnode) {
+ // This is the shared contour, used for dynamic calls.
+ targets := a.addOneNode(tInvalid, "startTimer.targets", nil)
+ cgn.sites = append(cgn.sites, &callsite{targets: targets})
+ params := a.funcParams(cgn.obj)
+ a.addConstraint(&timeStartTimerConstraint{
+ targets: targets,
+ t: params,
+ })
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/labels.go b/go/src/golang.org/x/tools/go/pointer/labels.go
index cf6ef20..bfe60d2 100644
--- a/go/src/golang.org/x/tools/go/pointer/labels.go
+++ b/go/src/golang.org/x/tools/go/pointer/labels.go
@@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
import (
"fmt"
"go/token"
+ "go/types"
"strings"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// A Label is an entity that may be pointed to by a pointer, map,
diff --git a/go/src/golang.org/x/tools/go/pointer/labels14.go b/go/src/golang.org/x/tools/go/pointer/labels14.go
new file mode 100644
index 0000000..c9ca6a3
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/labels14.go
@@ -0,0 +1,154 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+import (
+ "fmt"
+ "go/token"
+ "strings"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+// A Label is an entity that may be pointed to by a pointer, map,
+// channel, 'func', slice or interface.
+//
+// Labels include:
+// - functions
+// - globals
+// - tagged objects, representing interfaces and reflect.Values
+// - arrays created by conversions (e.g. []byte("foo"), []byte(s))
+// - stack- and heap-allocated variables (including composite literals)
+// - channels, maps and arrays created by make()
+// - intrinsic or reflective operations that allocate (e.g. append, reflect.New)
+// - intrinsic objects, e.g. the initial array behind os.Args.
+// - and their subelements, e.g. "alloc.y[*].z"
+//
+// Labels are so varied that they defy good generalizations;
+// some have no value, no callgraph node, or no position.
+// Many objects have types that are inexpressible in Go:
+// maps, channels, functions, tagged objects.
+//
+// At most one of Value() or ReflectType() may return non-nil.
+//
+type Label struct {
+ obj *object // the addressable memory location containing this label
+ subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c"
+}
+
+// Value returns the ssa.Value that allocated this label's object, if any.
+func (l Label) Value() ssa.Value {
+ val, _ := l.obj.data.(ssa.Value)
+ return val
+}
+
+// ReflectType returns the type represented by this label if it is an
+// reflect.rtype instance object or *reflect.rtype-tagged object.
+//
+func (l Label) ReflectType() types.Type {
+ rtype, _ := l.obj.data.(types.Type)
+ return rtype
+}
+
+// Path returns the path to the subelement of the object containing
+// this label. For example, ".x[*].y".
+//
+func (l Label) Path() string {
+ return l.subelement.path()
+}
+
+// Pos returns the position of this label, if known, zero otherwise.
+func (l Label) Pos() token.Pos {
+ switch data := l.obj.data.(type) {
+ case ssa.Value:
+ return data.Pos()
+ case types.Type:
+ if nt, ok := deref(data).(*types.Named); ok {
+ return nt.Obj().Pos()
+ }
+ }
+ if cgn := l.obj.cgn; cgn != nil {
+ return cgn.fn.Pos()
+ }
+ return token.NoPos
+}
+
+// String returns the printed form of this label.
+//
+// Examples: Object type:
+// x (a variable)
+// (sync.Mutex).Lock (a function)
+// convert (array created by conversion)
+// makemap (map allocated via make)
+// makechan (channel allocated via make)
+// makeinterface (tagged object allocated by makeinterface)
+// <alloc in reflect.Zero> (allocation in instrinsic)
+// sync.Mutex (a reflect.rtype instance)
+// <command-line arguments> (an intrinsic object)
+//
+// Labels within compound objects have subelement paths:
+// x.y[*].z (a struct variable, x)
+// append.y[*].z (array allocated by append)
+// makeslice.y[*].z (array allocated via make)
+//
+// TODO(adonovan): expose func LabelString(*types.Package, Label).
+//
+func (l Label) String() string {
+ var s string
+ switch v := l.obj.data.(type) {
+ case types.Type:
+ return v.String()
+
+ case string:
+ s = v // an intrinsic object (e.g. os.Args[*])
+
+ case nil:
+ if l.obj.cgn != nil {
+ // allocation by intrinsic or reflective operation
+ s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn)
+ } else {
+ s = "<unknown>" // should be unreachable
+ }
+
+ case *ssa.Function:
+ s = v.String()
+
+ case *ssa.Global:
+ s = v.String()
+
+ case *ssa.Const:
+ s = v.Name()
+
+ case *ssa.Alloc:
+ s = v.Comment
+ if s == "" {
+ s = "alloc"
+ }
+
+ case *ssa.Call:
+ // Currently only calls to append can allocate objects.
+ if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" {
+ panic("unhandled *ssa.Call label: " + v.Name())
+ }
+ s = "append"
+
+ case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert:
+ s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa."))
+
+ case *ssa.MakeInterface:
+ // MakeInterface is usually implicit in Go source (so
+ // Pos()==0), and tagged objects may be allocated
+ // synthetically (so no *MakeInterface data).
+ s = "makeinterface:" + v.X.Type().String()
+
+ default:
+ panic(fmt.Sprintf("unhandled object data type: %T", v))
+ }
+
+ return s + l.subelement.path()
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/pointer14_test.go b/go/src/golang.org/x/tools/go/pointer/pointer14_test.go
new file mode 100644
index 0000000..2bcdd56
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/pointer14_test.go
@@ -0,0 +1,578 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// No testdata on Android.
+
+// +build !android
+
+package pointer_test
+
+// This test uses 'expectation' comments embedded within testdata/*.go
+// files to specify the expected pointer analysis behaviour.
+// See below for grammar.
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+var inputs = []string{
+ "testdata/a_test.go",
+ "testdata/another.go",
+ "testdata/arrayreflect.go",
+ "testdata/arrays.go",
+ "testdata/channels.go",
+ "testdata/chanreflect.go",
+ "testdata/context.go",
+ "testdata/conv.go",
+ "testdata/finalizer.go",
+ "testdata/flow.go",
+ "testdata/fmtexcerpt.go",
+ "testdata/func.go",
+ "testdata/funcreflect.go",
+ "testdata/hello.go", // NB: causes spurious failure of HVN cross-check
+ "testdata/interfaces.go",
+ "testdata/issue9002.go",
+ "testdata/mapreflect.go",
+ "testdata/maps.go",
+ "testdata/panic.go",
+ "testdata/recur.go",
+ "testdata/reflect.go",
+ "testdata/rtti.go",
+ "testdata/structreflect.go",
+ "testdata/structs.go",
+ "testdata/timer.go",
+}
+
+// Expectation grammar:
+//
+// @calls f -> g
+//
+// A 'calls' expectation asserts that edge (f, g) appears in the
+// callgraph. f and g are notated as per Function.String(), which
+// may contain spaces (e.g. promoted method in anon struct).
+//
+// @pointsto a | b | c
+//
+// A 'pointsto' expectation asserts that the points-to set of its
+// operand contains exactly the set of labels {a,b,c} notated as per
+// labelString.
+//
+// A 'pointsto' expectation must appear on the same line as a
+// print(x) statement; the expectation's operand is x.
+//
+// If one of the strings is "...", the expectation asserts that the
+// points-to set at least the other labels.
+//
+// We use '|' because label names may contain spaces, e.g. methods
+// of anonymous structs.
+//
+// From a theoretical perspective, concrete types in interfaces are
+// labels too, but they are represented differently and so have a
+// different expectation, @types, below.
+//
+// @types t | u | v
+//
+// A 'types' expectation asserts that the set of possible dynamic
+// types of its interface operand is exactly {t,u,v}, notated per
+// go/types.Type.String(). In other words, it asserts that the type
+// component of the interface may point to that set of concrete type
+// literals. It also works for reflect.Value, though the types
+// needn't be concrete in that case.
+//
+// A 'types' expectation must appear on the same line as a
+// print(x) statement; the expectation's operand is x.
+//
+// If one of the strings is "...", the expectation asserts that the
+// interface's type may point to at least the other types.
+//
+// We use '|' because type names may contain spaces.
+//
+// @warning "regexp"
+//
+// A 'warning' expectation asserts that the analysis issues a
+// warning that matches the regular expression within the string
+// literal.
+//
+// @line id
+//
+// A line directive associates the name "id" with the current
+// file:line. The string form of labels will use this id instead of
+// a file:line, making @pointsto expectations more robust against
+// perturbations in the source file.
+// (NB, anon functions still include line numbers.)
+//
+type expectation struct {
+ kind string // "pointsto" | "types" | "calls" | "warning"
+ filename string
+ linenum int // source line number, 1-based
+ args []string
+ types []types.Type // for types
+}
+
+func (e *expectation) String() string {
+ return fmt.Sprintf("@%s[%s]", e.kind, strings.Join(e.args, " | "))
+}
+
+func (e *expectation) errorf(format string, args ...interface{}) {
+ fmt.Printf("%s:%d: ", e.filename, e.linenum)
+ fmt.Printf(format, args...)
+ fmt.Println()
+}
+
+func (e *expectation) needsProbe() bool {
+ return e.kind == "pointsto" || e.kind == "types"
+}
+
+// Find probe (call to print(x)) of same source file/line as expectation.
+func findProbe(prog *ssa.Program, probes map[*ssa.CallCommon]bool, queries map[ssa.Value]pointer.Pointer, e *expectation) (site *ssa.CallCommon, pts pointer.PointsToSet) {
+ for call := range probes {
+ pos := prog.Fset.Position(call.Pos())
+ if pos.Line == e.linenum && pos.Filename == e.filename {
+ // TODO(adonovan): send this to test log (display only on failure).
+ // fmt.Printf("%s:%d: info: found probe for %s: %s\n",
+ // e.filename, e.linenum, e, p.arg0) // debugging
+ return call, queries[call.Args[0]].PointsTo()
+ }
+ }
+ return // e.g. analysis didn't reach this call
+}
+
+func doOneInput(input, filename string) bool {
+ var conf loader.Config
+
+ // Parsing.
+ f, err := conf.ParseFile(filename, input)
+ if err != nil {
+ fmt.Println(err)
+ return false
+ }
+
+ // Create single-file main package and import its dependencies.
+ conf.CreateFromFiles("main", f)
+ iprog, err := conf.Load()
+ if err != nil {
+ fmt.Println(err)
+ return false
+ }
+ mainPkgInfo := iprog.Created[0].Pkg
+
+ // SSA creation + building.
+ prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
+ prog.Build()
+
+ mainpkg := prog.Package(mainPkgInfo)
+ ptrmain := mainpkg // main package for the pointer analysis
+ if mainpkg.Func("main") == nil {
+ // No main function; assume it's a test.
+ ptrmain = prog.CreateTestMainPackage(mainpkg)
+ }
+
+ // Find all calls to the built-in print(x). Analytically,
+ // print is a no-op, but it's a convenient hook for testing
+ // the PTS of an expression, so our tests use it.
+ probes := make(map[*ssa.CallCommon]bool)
+ for fn := range ssautil.AllFunctions(prog) {
+ if fn.Pkg == mainpkg {
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ if instr, ok := instr.(ssa.CallInstruction); ok {
+ call := instr.Common()
+ if b, ok := call.Value.(*ssa.Builtin); ok && b.Name() == "print" && len(call.Args) == 1 {
+ probes[instr.Common()] = true
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ok := true
+
+ lineMapping := make(map[string]string) // maps "file:line" to @line tag
+
+ // Parse expectations in this input.
+ var exps []*expectation
+ re := regexp.MustCompile("// *@([a-z]*) *(.*)$")
+ lines := strings.Split(input, "\n")
+ for linenum, line := range lines {
+ linenum++ // make it 1-based
+ if matches := re.FindAllStringSubmatch(line, -1); matches != nil {
+ match := matches[0]
+ kind, rest := match[1], match[2]
+ e := &expectation{kind: kind, filename: filename, linenum: linenum}
+
+ if kind == "line" {
+ if rest == "" {
+ ok = false
+ e.errorf("@%s expectation requires identifier", kind)
+ } else {
+ lineMapping[fmt.Sprintf("%s:%d", filename, linenum)] = rest
+ }
+ continue
+ }
+
+ if e.needsProbe() && !strings.Contains(line, "print(") {
+ ok = false
+ e.errorf("@%s expectation must follow call to print(x)", kind)
+ continue
+ }
+
+ switch kind {
+ case "pointsto":
+ e.args = split(rest, "|")
+
+ case "types":
+ for _, typstr := range split(rest, "|") {
+ var t types.Type = types.Typ[types.Invalid] // means "..."
+ if typstr != "..." {
+ tv, err := types.Eval(prog.Fset, mainpkg.Pkg, f.Pos(), typstr)
+ if err != nil {
+ ok = false
+ // Don't print err since its location is bad.
+ e.errorf("'%s' is not a valid type: %s", typstr, err)
+ continue
+ }
+ t = tv.Type
+ }
+ e.types = append(e.types, t)
+ }
+
+ case "calls":
+ e.args = split(rest, "->")
+ // TODO(adonovan): eagerly reject the
+ // expectation if fn doesn't denote
+ // existing function, rather than fail
+ // the expectation after analysis.
+ if len(e.args) != 2 {
+ ok = false
+ e.errorf("@calls expectation wants 'caller -> callee' arguments")
+ continue
+ }
+
+ case "warning":
+ lit, err := strconv.Unquote(strings.TrimSpace(rest))
+ if err != nil {
+ ok = false
+ e.errorf("couldn't parse @warning operand: %s", err.Error())
+ continue
+ }
+ e.args = append(e.args, lit)
+
+ default:
+ ok = false
+ e.errorf("unknown expectation kind: %s", e)
+ continue
+ }
+ exps = append(exps, e)
+ }
+ }
+
+ var log bytes.Buffer
+ fmt.Fprintf(&log, "Input: %s\n", filename)
+
+ // Run the analysis.
+ config := &pointer.Config{
+ Reflection: true,
+ BuildCallGraph: true,
+ Mains: []*ssa.Package{ptrmain},
+ Log: &log,
+ }
+ for probe := range probes {
+ v := probe.Args[0]
+ if pointer.CanPoint(v.Type()) {
+ config.AddQuery(v)
+ }
+ }
+
+ // Print the log is there was an error or a panic.
+ complete := false
+ defer func() {
+ if !complete || !ok {
+ log.WriteTo(os.Stderr)
+ }
+ }()
+
+ result, err := pointer.Analyze(config)
+ if err != nil {
+ panic(err) // internal error in pointer analysis
+ }
+
+ // Check the expectations.
+ for _, e := range exps {
+ var call *ssa.CallCommon
+ var pts pointer.PointsToSet
+ var tProbe types.Type
+ if e.needsProbe() {
+ if call, pts = findProbe(prog, probes, result.Queries, e); call == nil {
+ ok = false
+ e.errorf("unreachable print() statement has expectation %s", e)
+ continue
+ }
+ tProbe = call.Args[0].Type()
+ if !pointer.CanPoint(tProbe) {
+ ok = false
+ e.errorf("expectation on non-pointerlike operand: %s", tProbe)
+ continue
+ }
+ }
+
+ switch e.kind {
+ case "pointsto":
+ if !checkPointsToExpectation(e, pts, lineMapping, prog) {
+ ok = false
+ }
+
+ case "types":
+ if !checkTypesExpectation(e, pts, tProbe) {
+ ok = false
+ }
+
+ case "calls":
+ if !checkCallsExpectation(prog, e, result.CallGraph) {
+ ok = false
+ }
+
+ case "warning":
+ if !checkWarningExpectation(prog, e, result.Warnings) {
+ ok = false
+ }
+ }
+ }
+
+ complete = true
+
+ // ok = false // debugging: uncomment to always see log
+
+ return ok
+}
+
+func labelString(l *pointer.Label, lineMapping map[string]string, prog *ssa.Program) string {
+ // Functions and Globals need no pos suffix,
+ // nor do allocations in intrinsic operations
+ // (for which we'll print the function name).
+ switch l.Value().(type) {
+ case nil, *ssa.Function, *ssa.Global:
+ return l.String()
+ }
+
+ str := l.String()
+ if pos := l.Pos(); pos != token.NoPos {
+ // Append the position, using a @line tag instead of a line number, if defined.
+ posn := prog.Fset.Position(pos)
+ s := fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
+ if tag, ok := lineMapping[s]; ok {
+ return fmt.Sprintf("%s@%s:%d", str, tag, posn.Column)
+ }
+ str = fmt.Sprintf("%s@%s", str, posn)
+ }
+ return str
+}
+
+func checkPointsToExpectation(e *expectation, pts pointer.PointsToSet, lineMapping map[string]string, prog *ssa.Program) bool {
+ expected := make(map[string]int)
+ surplus := make(map[string]int)
+ exact := true
+ for _, g := range e.args {
+ if g == "..." {
+ exact = false
+ continue
+ }
+ expected[g]++
+ }
+ // Find the set of labels that the probe's
+ // argument (x in print(x)) may point to.
+ for _, label := range pts.Labels() {
+ name := labelString(label, lineMapping, prog)
+ if expected[name] > 0 {
+ expected[name]--
+ } else if exact {
+ surplus[name]++
+ }
+ }
+ // Report multiset difference:
+ ok := true
+ for _, count := range expected {
+ if count > 0 {
+ ok = false
+ e.errorf("value does not alias these expected labels: %s", join(expected))
+ break
+ }
+ }
+ for _, count := range surplus {
+ if count > 0 {
+ ok = false
+ e.errorf("value may additionally alias these labels: %s", join(surplus))
+ break
+ }
+ }
+ return ok
+}
+
+func checkTypesExpectation(e *expectation, pts pointer.PointsToSet, typ types.Type) bool {
+ var expected typeutil.Map
+ var surplus typeutil.Map
+ exact := true
+ for _, g := range e.types {
+ if g == types.Typ[types.Invalid] {
+ exact = false
+ continue
+ }
+ expected.Set(g, struct{}{})
+ }
+
+ if !pointer.CanHaveDynamicTypes(typ) {
+ e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", typ)
+ return false
+ }
+
+ // Find the set of types that the probe's
+ // argument (x in print(x)) may contain.
+ for _, T := range pts.DynamicTypes().Keys() {
+ if expected.At(T) != nil {
+ expected.Delete(T)
+ } else if exact {
+ surplus.Set(T, struct{}{})
+ }
+ }
+ // Report set difference:
+ ok := true
+ if expected.Len() > 0 {
+ ok = false
+ e.errorf("interface cannot contain these types: %s", expected.KeysString())
+ }
+ if surplus.Len() > 0 {
+ ok = false
+ e.errorf("interface may additionally contain these types: %s", surplus.KeysString())
+ }
+ return ok
+}
+
+var errOK = errors.New("OK")
+
+func checkCallsExpectation(prog *ssa.Program, e *expectation, cg *callgraph.Graph) bool {
+ found := make(map[string]int)
+ err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error {
+ // Name-based matching is inefficient but it allows us to
+ // match functions whose names that would not appear in an
+ // index ("<root>") or which are not unique ("func@1.2").
+ if edge.Caller.Func.String() == e.args[0] {
+ calleeStr := edge.Callee.Func.String()
+ if calleeStr == e.args[1] {
+ return errOK // expectation satisified; stop the search
+ }
+ found[calleeStr]++
+ }
+ return nil
+ })
+ if err == errOK {
+ return true
+ }
+ if len(found) == 0 {
+ e.errorf("didn't find any calls from %s", e.args[0])
+ }
+ e.errorf("found no call from %s to %s, but only to %s",
+ e.args[0], e.args[1], join(found))
+ return false
+}
+
+func checkWarningExpectation(prog *ssa.Program, e *expectation, warnings []pointer.Warning) bool {
+ // TODO(adonovan): check the position part of the warning too?
+ re, err := regexp.Compile(e.args[0])
+ if err != nil {
+ e.errorf("invalid regular expression in @warning expectation: %s", err.Error())
+ return false
+ }
+
+ if len(warnings) == 0 {
+ e.errorf("@warning %s expectation, but no warnings", strconv.Quote(e.args[0]))
+ return false
+ }
+
+ for _, w := range warnings {
+ if re.MatchString(w.Message) {
+ return true
+ }
+ }
+
+ e.errorf("@warning %s expectation not satised; found these warnings though:", strconv.Quote(e.args[0]))
+ for _, w := range warnings {
+ fmt.Printf("%s: warning: %s\n", prog.Fset.Position(w.Pos), w.Message)
+ }
+ return false
+}
+
+func TestInput(t *testing.T) {
+ ok := true
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Errorf("os.Getwd: %s", err)
+ return
+ }
+
+ // 'go test' does a chdir so that relative paths in
+ // diagnostics no longer make sense relative to the invoking
+ // shell's cwd. We print a special marker so that Emacs can
+ // make sense of them.
+ fmt.Fprintf(os.Stderr, "Entering directory `%s'\n", wd)
+
+ for _, filename := range inputs {
+ content, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Errorf("couldn't read file '%s': %s", filename, err)
+ continue
+ }
+
+ if !doOneInput(string(content), filename) {
+ ok = false
+ }
+ }
+ if !ok {
+ t.Fail()
+ }
+}
+
+// join joins the elements of multiset with " | "s.
+func join(set map[string]int) string {
+ var buf bytes.Buffer
+ sep := ""
+ for name, count := range set {
+ for i := 0; i < count; i++ {
+ buf.WriteString(sep)
+ sep = " | "
+ buf.WriteString(name)
+ }
+ }
+ return buf.String()
+}
+
+// split returns the list of sep-delimited non-empty strings in s.
+func split(s, sep string) (r []string) {
+ for _, elem := range strings.Split(s, sep) {
+ elem = strings.TrimSpace(elem)
+ if elem != "" {
+ r = append(r, elem)
+ }
+ }
+ return
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/pointer_test.go b/go/src/golang.org/x/tools/go/pointer/pointer_test.go
index 2744d4f..7cb16d7 100644
--- a/go/src/golang.org/x/tools/go/pointer/pointer_test.go
+++ b/go/src/golang.org/x/tools/go/pointer/pointer_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// No testdata on Android.
// +build !android
@@ -17,6 +19,7 @@
"errors"
"fmt"
"go/token"
+ "go/types"
"io/ioutil"
"os"
"regexp"
@@ -29,7 +32,6 @@
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -176,7 +178,7 @@
// SSA creation + building.
prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
- prog.BuildAll()
+ prog.Build()
mainpkg := prog.Package(mainPkgInfo)
ptrmain := mainpkg // main package for the pointer analysis
@@ -243,7 +245,7 @@
for _, typstr := range split(rest, "|") {
var t types.Type = types.Typ[types.Invalid] // means "..."
if typstr != "..." {
- tv, err := types.Eval(prog.Fset, mainpkg.Object, f.Pos(), typstr)
+ tv, err := types.Eval(prog.Fset, mainpkg.Pkg, f.Pos(), typstr)
if err != nil {
ok = false
// Don't print err since its location is bad.
@@ -520,6 +522,9 @@
}
func TestInput(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode; this test requires tons of memory; golang.org/issue/14113")
+ }
ok := true
wd, err := os.Getwd()
diff --git a/go/src/golang.org/x/tools/go/pointer/reflect.go b/go/src/golang.org/x/tools/go/pointer/reflect.go
index 466995c..bdb22cf 100644
--- a/go/src/golang.org/x/tools/go/pointer/reflect.go
+++ b/go/src/golang.org/x/tools/go/pointer/reflect.go
@@ -1,3 +1,9 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
package pointer
// This file implements the generation and resolution rules for
@@ -26,11 +32,11 @@
import (
"fmt"
+ exact "go/constant"
+ "go/types"
"reflect"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
func init() {
@@ -1879,7 +1885,7 @@
if isIface {
sig = sel.Type().(*types.Signature)
} else {
- fn = a.prog.Method(sel)
+ fn = a.prog.MethodValue(sel)
// move receiver to params[0]
sig = changeRecv(fn.Signature)
}
diff --git a/go/src/golang.org/x/tools/go/pointer/reflect14.go b/go/src/golang.org/x/tools/go/pointer/reflect14.go
new file mode 100644
index 0000000..c55f69e
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/reflect14.go
@@ -0,0 +1,1977 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+// This file implements the generation and resolution rules for
+// constraints arising from the use of reflection in the target
+// program. See doc.go for explanation of the representation.
+//
+// For consistency, the names of all parameters match those of the
+// actual functions in the "reflect" package.
+//
+// To avoid proliferation of equivalent labels, intrinsics should
+// memoize as much as possible, like TypeOf and Zero do for their
+// tagged objects.
+//
+// TODO(adonovan): this file is rather subtle. Explain how we derive
+// the implementation of each reflect operator from its spec,
+// including the subtleties of reflect.flag{Addr,RO,Indir}.
+// [Hint: our implementation is as if reflect.flagIndir was always
+// true, i.e. reflect.Values are pointers to tagged objects, there is
+// no inline allocation optimization; and indirect tagged objects (not
+// yet implemented) correspond to reflect.Values with
+// reflect.flagAddr.]
+// A picture would help too.
+//
+// TODO(adonovan): try factoring up the common parts of the majority of
+// these constraints that are single input, single output.
+
+import (
+ "fmt"
+ "reflect"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+func init() {
+ for name, fn := range map[string]intrinsic{
+ // reflect.Value methods.
+ "(reflect.Value).Addr": extÛ°reflectÛ°ValueÛ°Addr,
+ "(reflect.Value).Bool": extÛ°NoEffect,
+ "(reflect.Value).Bytes": extÛ°reflectÛ°ValueÛ°Bytes,
+ "(reflect.Value).Call": extÛ°reflectÛ°ValueÛ°Call,
+ "(reflect.Value).CallSlice": extÛ°reflectÛ°ValueÛ°CallSlice,
+ "(reflect.Value).CanAddr": extÛ°NoEffect,
+ "(reflect.Value).CanInterface": extÛ°NoEffect,
+ "(reflect.Value).CanSet": extÛ°NoEffect,
+ "(reflect.Value).Cap": extÛ°NoEffect,
+ "(reflect.Value).Close": extÛ°NoEffect,
+ "(reflect.Value).Complex": extÛ°NoEffect,
+ "(reflect.Value).Convert": extÛ°reflectÛ°ValueÛ°Convert,
+ "(reflect.Value).Elem": extÛ°reflectÛ°ValueÛ°Elem,
+ "(reflect.Value).Field": extÛ°reflectÛ°ValueÛ°Field,
+ "(reflect.Value).FieldByIndex": extÛ°reflectÛ°ValueÛ°FieldByIndex,
+ "(reflect.Value).FieldByName": extÛ°reflectÛ°ValueÛ°FieldByName,
+ "(reflect.Value).FieldByNameFunc": extÛ°reflectÛ°ValueÛ°FieldByNameFunc,
+ "(reflect.Value).Float": extÛ°NoEffect,
+ "(reflect.Value).Index": extÛ°reflectÛ°ValueÛ°Index,
+ "(reflect.Value).Int": extÛ°NoEffect,
+ "(reflect.Value).Interface": extÛ°reflectÛ°ValueÛ°Interface,
+ "(reflect.Value).InterfaceData": extÛ°NoEffect,
+ "(reflect.Value).IsNil": extÛ°NoEffect,
+ "(reflect.Value).IsValid": extÛ°NoEffect,
+ "(reflect.Value).Kind": extÛ°NoEffect,
+ "(reflect.Value).Len": extÛ°NoEffect,
+ "(reflect.Value).MapIndex": extÛ°reflectÛ°ValueÛ°MapIndex,
+ "(reflect.Value).MapKeys": extÛ°reflectÛ°ValueÛ°MapKeys,
+ "(reflect.Value).Method": extÛ°reflectÛ°ValueÛ°Method,
+ "(reflect.Value).MethodByName": extÛ°reflectÛ°ValueÛ°MethodByName,
+ "(reflect.Value).NumField": extÛ°NoEffect,
+ "(reflect.Value).NumMethod": extÛ°NoEffect,
+ "(reflect.Value).OverflowComplex": extÛ°NoEffect,
+ "(reflect.Value).OverflowFloat": extÛ°NoEffect,
+ "(reflect.Value).OverflowInt": extÛ°NoEffect,
+ "(reflect.Value).OverflowUint": extÛ°NoEffect,
+ "(reflect.Value).Pointer": extÛ°NoEffect,
+ "(reflect.Value).Recv": extÛ°reflectÛ°ValueÛ°Recv,
+ "(reflect.Value).Send": extÛ°reflectÛ°ValueÛ°Send,
+ "(reflect.Value).Set": extÛ°reflectÛ°ValueÛ°Set,
+ "(reflect.Value).SetBool": extÛ°NoEffect,
+ "(reflect.Value).SetBytes": extÛ°reflectÛ°ValueÛ°SetBytes,
+ "(reflect.Value).SetComplex": extÛ°NoEffect,
+ "(reflect.Value).SetFloat": extÛ°NoEffect,
+ "(reflect.Value).SetInt": extÛ°NoEffect,
+ "(reflect.Value).SetLen": extÛ°NoEffect,
+ "(reflect.Value).SetMapIndex": extÛ°reflectÛ°ValueÛ°SetMapIndex,
+ "(reflect.Value).SetPointer": extÛ°reflectÛ°ValueÛ°SetPointer,
+ "(reflect.Value).SetString": extÛ°NoEffect,
+ "(reflect.Value).SetUint": extÛ°NoEffect,
+ "(reflect.Value).Slice": extÛ°reflectÛ°ValueÛ°Slice,
+ "(reflect.Value).String": extÛ°NoEffect,
+ "(reflect.Value).TryRecv": extÛ°reflectÛ°ValueÛ°Recv,
+ "(reflect.Value).TrySend": extÛ°reflectÛ°ValueÛ°Send,
+ "(reflect.Value).Type": extÛ°NoEffect,
+ "(reflect.Value).Uint": extÛ°NoEffect,
+ "(reflect.Value).UnsafeAddr": extÛ°NoEffect,
+
+ // Standalone reflect.* functions.
+ "reflect.Append": extÛ°reflectÛ°Append,
+ "reflect.AppendSlice": extÛ°reflectÛ°AppendSlice,
+ "reflect.Copy": extÛ°reflectÛ°Copy,
+ "reflect.ChanOf": extÛ°reflectÛ°ChanOf,
+ "reflect.DeepEqual": extÛ°NoEffect,
+ "reflect.Indirect": extÛ°reflectÛ°Indirect,
+ "reflect.MakeChan": extÛ°reflectÛ°MakeChan,
+ "reflect.MakeFunc": extÛ°reflectÛ°MakeFunc,
+ "reflect.MakeMap": extÛ°reflectÛ°MakeMap,
+ "reflect.MakeSlice": extÛ°reflectÛ°MakeSlice,
+ "reflect.MapOf": extÛ°reflectÛ°MapOf,
+ "reflect.New": extÛ°reflectÛ°New,
+ "reflect.NewAt": extÛ°reflectÛ°NewAt,
+ "reflect.PtrTo": extÛ°reflectÛ°PtrTo,
+ "reflect.Select": extÛ°reflectÛ°Select,
+ "reflect.SliceOf": extÛ°reflectÛ°SliceOf,
+ "reflect.TypeOf": extÛ°reflectÛ°TypeOf,
+ "reflect.ValueOf": extÛ°reflectÛ°ValueOf,
+ "reflect.Zero": extÛ°reflectÛ°Zero,
+ "reflect.init": extÛ°NoEffect,
+
+ // *reflect.rtype methods
+ "(*reflect.rtype).Align": extÛ°NoEffect,
+ "(*reflect.rtype).AssignableTo": extÛ°NoEffect,
+ "(*reflect.rtype).Bits": extÛ°NoEffect,
+ "(*reflect.rtype).ChanDir": extÛ°NoEffect,
+ "(*reflect.rtype).ConvertibleTo": extÛ°NoEffect,
+ "(*reflect.rtype).Elem": extÛ°reflectÛ°rtypeÛ°Elem,
+ "(*reflect.rtype).Field": extÛ°reflectÛ°rtypeÛ°Field,
+ "(*reflect.rtype).FieldAlign": extÛ°NoEffect,
+ "(*reflect.rtype).FieldByIndex": extÛ°reflectÛ°rtypeÛ°FieldByIndex,
+ "(*reflect.rtype).FieldByName": extÛ°reflectÛ°rtypeÛ°FieldByName,
+ "(*reflect.rtype).FieldByNameFunc": extÛ°reflectÛ°rtypeÛ°FieldByNameFunc,
+ "(*reflect.rtype).Implements": extÛ°NoEffect,
+ "(*reflect.rtype).In": extÛ°reflectÛ°rtypeÛ°In,
+ "(*reflect.rtype).IsVariadic": extÛ°NoEffect,
+ "(*reflect.rtype).Key": extÛ°reflectÛ°rtypeÛ°Key,
+ "(*reflect.rtype).Kind": extÛ°NoEffect,
+ "(*reflect.rtype).Len": extÛ°NoEffect,
+ "(*reflect.rtype).Method": extÛ°reflectÛ°rtypeÛ°Method,
+ "(*reflect.rtype).MethodByName": extÛ°reflectÛ°rtypeÛ°MethodByName,
+ "(*reflect.rtype).Name": extÛ°NoEffect,
+ "(*reflect.rtype).NumField": extÛ°NoEffect,
+ "(*reflect.rtype).NumIn": extÛ°NoEffect,
+ "(*reflect.rtype).NumMethod": extÛ°NoEffect,
+ "(*reflect.rtype).NumOut": extÛ°NoEffect,
+ "(*reflect.rtype).Out": extÛ°reflectÛ°rtypeÛ°Out,
+ "(*reflect.rtype).PkgPath": extÛ°NoEffect,
+ "(*reflect.rtype).Size": extÛ°NoEffect,
+ "(*reflect.rtype).String": extÛ°NoEffect,
+ } {
+ intrinsicsByName[name] = fn
+ }
+}
+
+// -------------------- (reflect.Value) --------------------
+
+func extÛ°reflectÛ°ValueÛ°Addr(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Bytes() Value ----------
+
+// result = v.Bytes()
+type rVBytesConstraint struct {
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVBytesConstraint) ptr() nodeid { return c.v }
+func (c *rVBytesConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVBytes.result")
+}
+func (c *rVBytesConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVBytesConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v)
+}
+
+func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, slice, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ tSlice, ok := tDyn.Underlying().(*types.Slice)
+ if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
+ if a.onlineCopy(c.result, slice) {
+ changed = true
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Bytes(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVBytesConstraint{
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (Value).Call(in []Value) []Value ----------
+
+// result = v.Call(in)
+type rVCallConstraint struct {
+ cgn *cgnode
+ targets nodeid // (indirect)
+ v nodeid // (ptr)
+ arg nodeid // = in[*]
+ result nodeid // (indirect)
+ dotdotdot bool // interpret last arg as a "..." slice
+}
+
+func (c *rVCallConstraint) ptr() nodeid { return c.v }
+func (c *rVCallConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.targets), "rVCall.targets")
+ h.markIndirect(onodeid(c.result), "rVCall.result")
+}
+func (c *rVCallConstraint) renumber(mapping []nodeid) {
+ c.targets = mapping[c.targets]
+ c.v = mapping[c.v]
+ c.arg = mapping[c.arg]
+ c.result = mapping[c.result]
+}
+
+func (c *rVCallConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg)
+}
+
+func (c *rVCallConstraint) solve(a *analysis, delta *nodeset) {
+ if c.targets == 0 {
+ panic("no targets")
+ }
+
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, fn, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ tSig, ok := tDyn.Underlying().(*types.Signature)
+ if !ok {
+ continue // not a function
+ }
+ if tSig.Recv() != nil {
+ panic(tSig) // TODO(adonovan): rethink when we implement Method()
+ }
+
+ // Add dynamic call target.
+ if a.onlineCopy(c.targets, fn) {
+ a.addWork(c.targets)
+ // TODO(adonovan): is 'else continue' a sound optimisation here?
+ }
+
+ // Allocate a P/R block.
+ tParams := tSig.Params()
+ tResults := tSig.Results()
+ params := a.addNodes(tParams, "rVCall.params")
+ results := a.addNodes(tResults, "rVCall.results")
+
+ // Make a dynamic call to 'fn'.
+ a.store(fn, params, 1, a.sizeof(tParams))
+ a.load(results, fn, 1+a.sizeof(tParams), a.sizeof(tResults))
+
+ // Populate P by type-asserting each actual arg (all merged in c.arg).
+ for i, n := 0, tParams.Len(); i < n; i++ {
+ T := tParams.At(i).Type()
+ a.typeAssert(T, params, c.arg, false)
+ params += nodeid(a.sizeof(T))
+ }
+
+ // Use R by tagging and copying each actual result to c.result.
+ for i, n := 0, tResults.Len(); i < n; i++ {
+ T := tResults.At(i).Type()
+ // Convert from an arbitrary type to a reflect.Value
+ // (like MakeInterface followed by reflect.ValueOf).
+ if isInterface(T) {
+ // (don't tag)
+ if a.onlineCopy(c.result, results) {
+ changed = true
+ }
+ } else {
+ obj := a.makeTagged(T, c.cgn, nil)
+ a.onlineCopyN(obj+1, results, a.sizeof(T))
+ if a.addLabel(c.result, obj) { // (true)
+ changed = true
+ }
+ }
+ results += nodeid(a.sizeof(T))
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+// Common code for direct (inlined) and indirect calls to (reflect.Value).Call.
+func reflectCallImpl(a *analysis, cgn *cgnode, site *callsite, recv, arg nodeid, dotdotdot bool) nodeid {
+ // Allocate []reflect.Value array for the result.
+ ret := a.nextNode()
+ a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "rVCall.ret")
+ a.endObject(ret, cgn, nil)
+
+ // pts(targets) will be the set of possible call targets.
+ site.targets = a.addOneNode(tInvalid, "rvCall.targets", nil)
+
+ // All arguments are merged since they arrive in a slice.
+ argelts := a.addOneNode(a.reflectValueObj.Type(), "rVCall.args", nil)
+ a.load(argelts, arg, 1, 1) // slice elements
+
+ a.addConstraint(&rVCallConstraint{
+ cgn: cgn,
+ targets: site.targets,
+ v: recv,
+ arg: argelts,
+ result: ret + 1, // results go into elements of ret
+ dotdotdot: dotdotdot,
+ })
+ return ret
+}
+
+func reflectCall(a *analysis, cgn *cgnode, dotdotdot bool) {
+ // This is the shared contour implementation of (reflect.Value).Call
+ // and CallSlice, as used by indirect calls (rare).
+ // Direct calls are inlined in gen.go, eliding the
+ // intermediate cgnode for Call.
+ site := new(callsite)
+ cgn.sites = append(cgn.sites, site)
+ recv := a.funcParams(cgn.obj)
+ arg := recv + 1
+ ret := reflectCallImpl(a, cgn, site, recv, arg, dotdotdot)
+ a.addressOf(cgn.fn.Signature.Results().At(0).Type(), a.funcResults(cgn.obj), ret)
+}
+
+func extÛ°reflectÛ°ValueÛ°Call(a *analysis, cgn *cgnode) {
+ reflectCall(a, cgn, false)
+}
+
+func extÛ°reflectÛ°ValueÛ°CallSlice(a *analysis, cgn *cgnode) {
+ // TODO(adonovan): implement. Also, inline direct calls in gen.go too.
+ if false {
+ reflectCall(a, cgn, true)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Convert(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Elem() Value ----------
+
+// result = v.Elem()
+type rVElemConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVElemConstraint) ptr() nodeid { return c.v }
+func (c *rVElemConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVElem.result")
+}
+func (c *rVElemConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVElemConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v)
+}
+
+func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, payload, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ switch t := tDyn.Underlying().(type) {
+ case *types.Interface:
+ if a.onlineCopy(c.result, payload) {
+ changed = true
+ }
+
+ case *types.Pointer:
+ obj := a.makeTagged(t.Elem(), c.cgn, nil)
+ a.load(obj+1, payload, 0, a.sizeof(t.Elem()))
+ if a.addLabel(c.result, obj) {
+ changed = true
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Elem(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVElemConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°ValueÛ°Field(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°ValueÛ°FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°ValueÛ°FieldByName(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°ValueÛ°FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Index() Value ----------
+
+// result = v.Index()
+type rVIndexConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVIndexConstraint) ptr() nodeid { return c.v }
+func (c *rVIndexConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVIndex.result")
+}
+func (c *rVIndexConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVIndexConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v)
+}
+
+func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, payload, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ var res nodeid
+ switch t := tDyn.Underlying().(type) {
+ case *types.Array:
+ res = a.makeTagged(t.Elem(), c.cgn, nil)
+ a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem()))
+
+ case *types.Slice:
+ res = a.makeTagged(t.Elem(), c.cgn, nil)
+ a.load(res+1, payload, 1, a.sizeof(t.Elem()))
+
+ case *types.Basic:
+ if t.Kind() == types.String {
+ res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil)
+ }
+ }
+ if res != 0 && a.addLabel(c.result, res) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Index(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVIndexConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (Value).Interface() Value ----------
+
+// result = v.Interface()
+type rVInterfaceConstraint struct {
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVInterfaceConstraint) ptr() nodeid { return c.v }
+func (c *rVInterfaceConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVInterface.result")
+}
+func (c *rVInterfaceConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVInterfaceConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
+}
+
+func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, payload, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ if isInterface(tDyn) {
+ if a.onlineCopy(c.result, payload) {
+ a.addWork(c.result)
+ }
+ } else {
+ if a.addLabel(c.result, vObj) {
+ changed = true
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Interface(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVInterfaceConstraint{
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (Value).MapIndex(Value) Value ----------
+
+// result = v.MapIndex(_)
+type rVMapIndexConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVMapIndexConstraint) ptr() nodeid { return c.v }
+func (c *rVMapIndexConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVMapIndex.result")
+}
+func (c *rVMapIndexConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVMapIndexConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
+}
+
+func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, m, indirect := a.taggedValue(vObj)
+ tMap, _ := tDyn.Underlying().(*types.Map)
+ if tMap == nil {
+ continue // not a map
+ }
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ obj := a.makeTagged(tMap.Elem(), c.cgn, nil)
+ a.load(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
+ if a.addLabel(c.result, obj) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°MapIndex(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVMapIndexConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (Value).MapKeys() []Value ----------
+
+// result = v.MapKeys()
+type rVMapKeysConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVMapKeysConstraint) ptr() nodeid { return c.v }
+func (c *rVMapKeysConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVMapKeys.result")
+}
+func (c *rVMapKeysConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVMapKeysConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
+}
+
+func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, m, indirect := a.taggedValue(vObj)
+ tMap, _ := tDyn.Underlying().(*types.Map)
+ if tMap == nil {
+ continue // not a map
+ }
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
+ a.load(kObj+1, m, 0, a.sizeof(tMap.Key()))
+ if a.addLabel(c.result, kObj) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°MapKeys(a *analysis, cgn *cgnode) {
+ // Allocate an array for the result.
+ obj := a.nextNode()
+ T := types.NewSlice(a.reflectValueObj.Type())
+ a.addNodes(sliceToArray(T), "reflect.MapKeys result")
+ a.endObject(obj, cgn, nil)
+ a.addressOf(T, a.funcResults(cgn.obj), obj)
+
+ a.addConstraint(&rVMapKeysConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: obj + 1, // result is stored in array elems
+ })
+}
+
+func extÛ°reflectÛ°ValueÛ°Method(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°ValueÛ°MethodByName(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Recv(Value) Value ----------
+
+// result, _ = v.Recv()
+type rVRecvConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVRecvConstraint) ptr() nodeid { return c.v }
+func (c *rVRecvConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVRecv.result")
+}
+func (c *rVRecvConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVRecvConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
+}
+
+func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, ch, indirect := a.taggedValue(vObj)
+ tChan, _ := tDyn.Underlying().(*types.Chan)
+ if tChan == nil {
+ continue // not a channel
+ }
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ tElem := tChan.Elem()
+ elemObj := a.makeTagged(tElem, c.cgn, nil)
+ a.load(elemObj+1, ch, 0, a.sizeof(tElem))
+ if a.addLabel(c.result, elemObj) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Recv(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVRecvConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (Value).Send(Value) ----------
+
+// v.Send(x)
+type rVSendConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ x nodeid
+}
+
+func (c *rVSendConstraint) ptr() nodeid { return c.v }
+func (c *rVSendConstraint) presolve(*hvn) {}
+func (c *rVSendConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.x = mapping[c.x]
+}
+
+func (c *rVSendConstraint) String() string {
+ return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
+}
+
+func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, ch, indirect := a.taggedValue(vObj)
+ tChan, _ := tDyn.Underlying().(*types.Chan)
+ if tChan == nil {
+ continue // not a channel
+ }
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ // Extract x's payload to xtmp, then store to channel.
+ tElem := tChan.Elem()
+ xtmp := a.addNodes(tElem, "Send.xtmp")
+ a.typeAssert(tElem, xtmp, c.x, false)
+ a.store(ch, xtmp, 0, a.sizeof(tElem))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Send(a *analysis, cgn *cgnode) {
+ params := a.funcParams(cgn.obj)
+ a.addConstraint(&rVSendConstraint{
+ cgn: cgn,
+ v: params,
+ x: params + 1,
+ })
+}
+
+func extÛ°reflectÛ°ValueÛ°Set(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).SetBytes(x []byte) ----------
+
+// v.SetBytes(x)
+type rVSetBytesConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ x nodeid
+}
+
+func (c *rVSetBytesConstraint) ptr() nodeid { return c.v }
+func (c *rVSetBytesConstraint) presolve(*hvn) {}
+func (c *rVSetBytesConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.x = mapping[c.x]
+}
+
+func (c *rVSetBytesConstraint) String() string {
+ return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x)
+}
+
+func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, slice, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ tSlice, ok := tDyn.Underlying().(*types.Slice)
+ if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
+ if a.onlineCopy(slice, c.x) {
+ a.addWork(slice)
+ }
+ }
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°SetBytes(a *analysis, cgn *cgnode) {
+ params := a.funcParams(cgn.obj)
+ a.addConstraint(&rVSetBytesConstraint{
+ cgn: cgn,
+ v: params,
+ x: params + 1,
+ })
+}
+
+// ---------- func (Value).SetMapIndex(k Value, v Value) ----------
+
+// v.SetMapIndex(key, val)
+type rVSetMapIndexConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ key nodeid
+ val nodeid
+}
+
+func (c *rVSetMapIndexConstraint) ptr() nodeid { return c.v }
+func (c *rVSetMapIndexConstraint) presolve(*hvn) {}
+func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.key = mapping[c.key]
+ c.val = mapping[c.val]
+}
+
+func (c *rVSetMapIndexConstraint) String() string {
+ return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val)
+}
+
+func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, m, indirect := a.taggedValue(vObj)
+ tMap, _ := tDyn.Underlying().(*types.Map)
+ if tMap == nil {
+ continue // not a map
+ }
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ keysize := a.sizeof(tMap.Key())
+
+ // Extract key's payload to keytmp, then store to map key.
+ keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
+ a.typeAssert(tMap.Key(), keytmp, c.key, false)
+ a.store(m, keytmp, 0, keysize)
+
+ // Extract val's payload to vtmp, then store to map value.
+ valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp")
+ a.typeAssert(tMap.Elem(), valtmp, c.val, false)
+ a.store(m, valtmp, keysize, a.sizeof(tMap.Elem()))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°SetMapIndex(a *analysis, cgn *cgnode) {
+ params := a.funcParams(cgn.obj)
+ a.addConstraint(&rVSetMapIndexConstraint{
+ cgn: cgn,
+ v: params,
+ key: params + 1,
+ val: params + 2,
+ })
+}
+
+func extÛ°reflectÛ°ValueÛ°SetPointer(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Slice(v Value, i, j int) Value ----------
+
+// result = v.Slice(_, _)
+type rVSliceConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rVSliceConstraint) ptr() nodeid { return c.v }
+func (c *rVSliceConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rVSlice.result")
+}
+func (c *rVSliceConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *rVSliceConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v)
+}
+
+func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, payload, indirect := a.taggedValue(vObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ var res nodeid
+ switch t := tDyn.Underlying().(type) {
+ case *types.Pointer:
+ if tArr, ok := t.Elem().Underlying().(*types.Array); ok {
+ // pointer to array
+ res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil)
+ if a.onlineCopy(res+1, payload) {
+ a.addWork(res + 1)
+ }
+ }
+
+ case *types.Array:
+ // TODO(adonovan): implement addressable
+ // arrays when we do indirect tagged objects.
+
+ case *types.Slice:
+ res = vObj
+
+ case *types.Basic:
+ if t == types.Typ[types.String] {
+ res = vObj
+ }
+ }
+
+ if res != 0 && a.addLabel(c.result, res) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Slice(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rVSliceConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// -------------------- Standalone reflect functions --------------------
+
+func extÛ°reflectÛ°Append(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°AppendSlice(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°Copy(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func ChanOf(ChanDir, Type) Type ----------
+
+// result = ChanOf(dir, t)
+type reflectChanOfConstraint struct {
+ cgn *cgnode
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+ dirs []types.ChanDir
+}
+
+func (c *reflectChanOfConstraint) ptr() nodeid { return c.t }
+func (c *reflectChanOfConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectChanOf.result")
+}
+func (c *reflectChanOfConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectChanOfConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
+}
+
+func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.rtypeTaggedValue(tObj)
+
+ if typeTooHigh(T) {
+ continue
+ }
+
+ for _, dir := range c.dirs {
+ if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) {
+ changed = true
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+// dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf.
+var dirMap = [...][]types.ChanDir{
+ 0: {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown
+ reflect.RecvDir: {types.RecvOnly},
+ reflect.SendDir: {types.SendOnly},
+ reflect.BothDir: {types.SendRecv},
+}
+
+func extÛ°reflectÛ°ChanOf(a *analysis, cgn *cgnode) {
+ // If we have access to the callsite,
+ // and the channel argument is a constant (as is usual),
+ // only generate the requested direction.
+ var dir reflect.ChanDir // unknown
+ if site := cgn.callersite; site != nil {
+ if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+ v, _ := exact.Int64Val(c.Value)
+ if 0 <= v && v <= int64(reflect.BothDir) {
+ dir = reflect.ChanDir(v)
+ }
+ }
+ }
+
+ params := a.funcParams(cgn.obj)
+ a.addConstraint(&reflectChanOfConstraint{
+ cgn: cgn,
+ t: params + 1,
+ result: a.funcResults(cgn.obj),
+ dirs: dirMap[dir],
+ })
+}
+
+// ---------- func Indirect(v Value) Value ----------
+
+// result = Indirect(v)
+type reflectIndirectConstraint struct {
+ cgn *cgnode
+ v nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectIndirectConstraint) ptr() nodeid { return c.v }
+func (c *reflectIndirectConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectIndirect.result")
+}
+func (c *reflectIndirectConstraint) renumber(mapping []nodeid) {
+ c.v = mapping[c.v]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectIndirectConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
+}
+
+func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ vObj := nodeid(x)
+ tDyn, _, _ := a.taggedValue(vObj)
+ var res nodeid
+ if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok {
+ // load the payload of the pointer's tagged object
+ // into a new tagged object
+ res = a.makeTagged(tPtr.Elem(), c.cgn, nil)
+ a.load(res+1, vObj+1, 0, a.sizeof(tPtr.Elem()))
+ } else {
+ res = vObj
+ }
+
+ if a.addLabel(c.result, res) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°Indirect(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectIndirectConstraint{
+ cgn: cgn,
+ v: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func MakeChan(Type) Value ----------
+
+// result = MakeChan(typ)
+type reflectMakeChanConstraint struct {
+ cgn *cgnode
+ typ nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ }
+func (c *reflectMakeChanConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectMakeChan.result")
+}
+func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) {
+ c.typ = mapping[c.typ]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectMakeChanConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
+}
+
+func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ typObj := nodeid(x)
+ T := a.rtypeTaggedValue(typObj)
+ tChan, ok := T.Underlying().(*types.Chan)
+ if !ok || tChan.Dir() != types.SendRecv {
+ continue // not a bidirectional channel type
+ }
+
+ obj := a.nextNode()
+ a.addNodes(tChan.Elem(), "reflect.MakeChan.value")
+ a.endObject(obj, c.cgn, nil)
+
+ // put its address in a new T-tagged object
+ id := a.makeTagged(T, c.cgn, nil)
+ a.addLabel(id+1, obj)
+
+ // flow the T-tagged object to the result
+ if a.addLabel(c.result, id) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°MakeChan(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectMakeChanConstraint{
+ cgn: cgn,
+ typ: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°MakeFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func MakeMap(Type) Value ----------
+
+// result = MakeMap(typ)
+type reflectMakeMapConstraint struct {
+ cgn *cgnode
+ typ nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ }
+func (c *reflectMakeMapConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectMakeMap.result")
+}
+func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) {
+ c.typ = mapping[c.typ]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectMakeMapConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
+}
+
+func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ typObj := nodeid(x)
+ T := a.rtypeTaggedValue(typObj)
+ tMap, ok := T.Underlying().(*types.Map)
+ if !ok {
+ continue // not a map type
+ }
+
+ mapObj := a.nextNode()
+ a.addNodes(tMap.Key(), "reflect.MakeMap.key")
+ a.addNodes(tMap.Elem(), "reflect.MakeMap.value")
+ a.endObject(mapObj, c.cgn, nil)
+
+ // put its address in a new T-tagged object
+ id := a.makeTagged(T, c.cgn, nil)
+ a.addLabel(id+1, mapObj)
+
+ // flow the T-tagged object to the result
+ if a.addLabel(c.result, id) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°MakeMap(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectMakeMapConstraint{
+ cgn: cgn,
+ typ: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func MakeSlice(Type) Value ----------
+
+// result = MakeSlice(typ)
+type reflectMakeSliceConstraint struct {
+ cgn *cgnode
+ typ nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ }
+func (c *reflectMakeSliceConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectMakeSlice.result")
+}
+func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) {
+ c.typ = mapping[c.typ]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectMakeSliceConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ)
+}
+
+func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ typObj := nodeid(x)
+ T := a.rtypeTaggedValue(typObj)
+ if _, ok := T.Underlying().(*types.Slice); !ok {
+ continue // not a slice type
+ }
+
+ obj := a.nextNode()
+ a.addNodes(sliceToArray(T), "reflect.MakeSlice")
+ a.endObject(obj, c.cgn, nil)
+
+ // put its address in a new T-tagged object
+ id := a.makeTagged(T, c.cgn, nil)
+ a.addLabel(id+1, obj)
+
+ // flow the T-tagged object to the result
+ if a.addLabel(c.result, id) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°MakeSlice(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectMakeSliceConstraint{
+ cgn: cgn,
+ typ: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°MapOf(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func New(Type) Value ----------
+
+// result = New(typ)
+type reflectNewConstraint struct {
+ cgn *cgnode
+ typ nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectNewConstraint) ptr() nodeid { return c.typ }
+func (c *reflectNewConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectNew.result")
+}
+func (c *reflectNewConstraint) renumber(mapping []nodeid) {
+ c.typ = mapping[c.typ]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectNewConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
+}
+
+func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ typObj := nodeid(x)
+ T := a.rtypeTaggedValue(typObj)
+
+ // allocate new T object
+ newObj := a.nextNode()
+ a.addNodes(T, "reflect.New")
+ a.endObject(newObj, c.cgn, nil)
+
+ // put its address in a new *T-tagged object
+ id := a.makeTagged(types.NewPointer(T), c.cgn, nil)
+ a.addLabel(id+1, newObj)
+
+ // flow the pointer to the result
+ if a.addLabel(c.result, id) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°New(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectNewConstraint{
+ cgn: cgn,
+ typ: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°NewAt(a *analysis, cgn *cgnode) {
+ extÛ°reflectÛ°New(a, cgn)
+
+ // TODO(adonovan): also report dynamic calls to unsound intrinsics.
+ if site := cgn.callersite; site != nil {
+ a.warnf(site.pos(), "unsound: %s contains a reflect.NewAt() call", site.instr.Parent())
+ }
+}
+
+// ---------- func PtrTo(Type) Type ----------
+
+// result = PtrTo(t)
+type reflectPtrToConstraint struct {
+ cgn *cgnode
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectPtrToConstraint) ptr() nodeid { return c.t }
+func (c *reflectPtrToConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectPtrTo.result")
+}
+func (c *reflectPtrToConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectPtrToConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t)
+}
+
+func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.rtypeTaggedValue(tObj)
+
+ if typeTooHigh(T) {
+ continue
+ }
+
+ if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°PtrTo(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectPtrToConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°Select(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func SliceOf(Type) Type ----------
+
+// result = SliceOf(t)
+type reflectSliceOfConstraint struct {
+ cgn *cgnode
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t }
+func (c *reflectSliceOfConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectSliceOf.result")
+}
+func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectSliceOfConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t)
+}
+
+func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.rtypeTaggedValue(tObj)
+
+ if typeTooHigh(T) {
+ continue
+ }
+
+ if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°SliceOf(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectSliceOfConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func TypeOf(v Value) Type ----------
+
+// result = TypeOf(i)
+type reflectTypeOfConstraint struct {
+ cgn *cgnode
+ i nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i }
+func (c *reflectTypeOfConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectTypeOf.result")
+}
+func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) {
+ c.i = mapping[c.i]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectTypeOfConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
+}
+
+func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ iObj := nodeid(x)
+ tDyn, _, _ := a.taggedValue(iObj)
+ if a.addLabel(c.result, a.makeRtype(tDyn)) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°TypeOf(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectTypeOfConstraint{
+ cgn: cgn,
+ i: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func ValueOf(interface{}) Value ----------
+
+func extÛ°reflectÛ°ValueOf(a *analysis, cgn *cgnode) {
+ // TODO(adonovan): when we start creating indirect tagged
+ // objects, we'll need to handle them specially here since
+ // they must never appear in the PTS of an interface{}.
+ a.copy(a.funcResults(cgn.obj), a.funcParams(cgn.obj), 1)
+}
+
+// ---------- func Zero(Type) Value ----------
+
+// result = Zero(typ)
+type reflectZeroConstraint struct {
+ cgn *cgnode
+ typ nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *reflectZeroConstraint) ptr() nodeid { return c.typ }
+func (c *reflectZeroConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "reflectZero.result")
+}
+func (c *reflectZeroConstraint) renumber(mapping []nodeid) {
+ c.typ = mapping[c.typ]
+ c.result = mapping[c.result]
+}
+
+func (c *reflectZeroConstraint) String() string {
+ return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
+}
+
+func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ typObj := nodeid(x)
+ T := a.rtypeTaggedValue(typObj)
+
+ // TODO(adonovan): if T is an interface type, we need
+ // to create an indirect tagged object containing
+ // new(T). To avoid updates of such shared values,
+ // we'll need another flag on indirect tagged objects
+ // that marks whether they are addressable or
+ // readonly, just like the reflect package does.
+
+ // memoize using a.reflectZeros[T]
+ var id nodeid
+ if z := a.reflectZeros.At(T); false && z != nil {
+ id = z.(nodeid)
+ } else {
+ id = a.makeTagged(T, c.cgn, nil)
+ a.reflectZeros.Set(T, id)
+ }
+ if a.addLabel(c.result, id) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°Zero(a *analysis, cgn *cgnode) {
+ a.addConstraint(&reflectZeroConstraint{
+ cgn: cgn,
+ typ: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// -------------------- (*reflect.rtype) methods --------------------
+
+// ---------- func (*rtype) Elem() Type ----------
+
+// result = Elem(t)
+type rtypeElemConstraint struct {
+ cgn *cgnode
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rtypeElemConstraint) ptr() nodeid { return c.t }
+func (c *rtypeElemConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rtypeElem.result")
+}
+func (c *rtypeElemConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *rtypeElemConstraint) String() string {
+ return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t)
+}
+
+func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) {
+ // Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
+ type hasElem interface {
+ Elem() types.Type
+ }
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.nodes[tObj].obj.data.(types.Type)
+ if tHasElem, ok := T.Underlying().(hasElem); ok {
+ if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
+ changed = true
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°rtypeÛ°Elem(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rtypeElemConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (*rtype) Field(int) StructField ----------
+// ---------- func (*rtype) FieldByName(string) (StructField, bool) ----------
+
+// result = FieldByName(t, name)
+// result = Field(t, _)
+type rtypeFieldByNameConstraint struct {
+ cgn *cgnode
+ name string // name of field; "" for unknown
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rtypeFieldByNameConstraint) ptr() nodeid { return c.t }
+func (c *rtypeFieldByNameConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result+3), "rtypeFieldByName.result.Type")
+}
+func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *rtypeFieldByNameConstraint) String() string {
+ return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name)
+}
+
+func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) {
+ // type StructField struct {
+ // 0 __identity__
+ // 1 Name string
+ // 2 PkgPath string
+ // 3 Type Type
+ // 4 Tag StructTag
+ // 5 Offset uintptr
+ // 6 Index []int
+ // 7 Anonymous bool
+ // }
+
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.nodes[tObj].obj.data.(types.Type)
+ tStruct, ok := T.Underlying().(*types.Struct)
+ if !ok {
+ continue // not a struct type
+ }
+
+ n := tStruct.NumFields()
+ for i := 0; i < n; i++ {
+ f := tStruct.Field(i)
+ if c.name == "" || c.name == f.Name() {
+
+ // a.offsetOf(Type) is 3.
+ if id := c.result + 3; a.addLabel(id, a.makeRtype(f.Type())) {
+ a.addWork(id)
+ }
+ // TODO(adonovan): StructField.Index should be non-nil.
+ }
+ }
+ }
+}
+
+func extÛ°reflectÛ°rtypeÛ°FieldByName(a *analysis, cgn *cgnode) {
+ // If we have access to the callsite,
+ // and the argument is a string constant,
+ // return only that field.
+ var name string
+ if site := cgn.callersite; site != nil {
+ if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+ name = exact.StringVal(c.Value)
+ }
+ }
+
+ a.addConstraint(&rtypeFieldByNameConstraint{
+ cgn: cgn,
+ name: name,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°rtypeÛ°Field(a *analysis, cgn *cgnode) {
+ // No-one ever calls Field with a constant argument,
+ // so we don't specialize that case.
+ a.addConstraint(&rtypeFieldByNameConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°rtypeÛ°FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func extÛ°reflectÛ°rtypeÛ°FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (*rtype) In/Out(i int) Type ----------
+
+// result = In/Out(t, i)
+type rtypeInOutConstraint struct {
+ cgn *cgnode
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+ out bool
+ i int // -ve if not a constant
+}
+
+func (c *rtypeInOutConstraint) ptr() nodeid { return c.t }
+func (c *rtypeInOutConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rtypeInOut.result")
+}
+func (c *rtypeInOutConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *rtypeInOutConstraint) String() string {
+ return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i)
+}
+
+func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.nodes[tObj].obj.data.(types.Type)
+ sig, ok := T.Underlying().(*types.Signature)
+ if !ok {
+ continue // not a func type
+ }
+
+ tuple := sig.Params()
+ if c.out {
+ tuple = sig.Results()
+ }
+ for i, n := 0, tuple.Len(); i < n; i++ {
+ if c.i < 0 || c.i == i {
+ if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) {
+ changed = true
+ }
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°rtypeÛ°InOut(a *analysis, cgn *cgnode, out bool) {
+ // If we have access to the callsite,
+ // and the argument is an int constant,
+ // return only that parameter.
+ index := -1
+ if site := cgn.callersite; site != nil {
+ if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+ v, _ := exact.Int64Val(c.Value)
+ index = int(v)
+ }
+ }
+ a.addConstraint(&rtypeInOutConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ out: out,
+ i: index,
+ })
+}
+
+func extÛ°reflectÛ°rtypeÛ°In(a *analysis, cgn *cgnode) {
+ extÛ°reflectÛ°rtypeÛ°InOut(a, cgn, false)
+}
+
+func extÛ°reflectÛ°rtypeÛ°Out(a *analysis, cgn *cgnode) {
+ extÛ°reflectÛ°rtypeÛ°InOut(a, cgn, true)
+}
+
+// ---------- func (*rtype) Key() Type ----------
+
+// result = Key(t)
+type rtypeKeyConstraint struct {
+ cgn *cgnode
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rtypeKeyConstraint) ptr() nodeid { return c.t }
+func (c *rtypeKeyConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result), "rtypeKey.result")
+}
+func (c *rtypeKeyConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *rtypeKeyConstraint) String() string {
+ return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t)
+}
+
+func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) {
+ changed := false
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.nodes[tObj].obj.data.(types.Type)
+ if tMap, ok := T.Underlying().(*types.Map); ok {
+ if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
+ changed = true
+ }
+ }
+ }
+ if changed {
+ a.addWork(c.result)
+ }
+}
+
+func extÛ°reflectÛ°rtypeÛ°Key(a *analysis, cgn *cgnode) {
+ a.addConstraint(&rtypeKeyConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// ---------- func (*rtype) Method(int) (Method, bool) ----------
+// ---------- func (*rtype) MethodByName(string) (Method, bool) ----------
+
+// result = MethodByName(t, name)
+// result = Method(t, _)
+type rtypeMethodByNameConstraint struct {
+ cgn *cgnode
+ name string // name of method; "" for unknown
+ t nodeid // (ptr)
+ result nodeid // (indirect)
+}
+
+func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t }
+func (c *rtypeMethodByNameConstraint) presolve(h *hvn) {
+ h.markIndirect(onodeid(c.result+3), "rtypeMethodByName.result.Type")
+ h.markIndirect(onodeid(c.result+4), "rtypeMethodByName.result.Func")
+}
+func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) {
+ c.t = mapping[c.t]
+ c.result = mapping[c.result]
+}
+
+func (c *rtypeMethodByNameConstraint) String() string {
+ return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name)
+}
+
+// changeRecv returns sig with Recv prepended to Params().
+func changeRecv(sig *types.Signature) *types.Signature {
+ params := sig.Params()
+ n := params.Len()
+ p2 := make([]*types.Var, n+1)
+ p2[0] = sig.Recv()
+ for i := 0; i < n; i++ {
+ p2[i+1] = params.At(i)
+ }
+ return types.NewSignature(nil, types.NewTuple(p2...), sig.Results(), sig.Variadic())
+}
+
+func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ tObj := nodeid(x)
+ T := a.nodes[tObj].obj.data.(types.Type)
+
+ isIface := isInterface(T)
+
+ // We don't use Lookup(c.name) when c.name != "" to avoid
+ // ambiguity: >1 unexported methods could match.
+ mset := a.prog.MethodSets.MethodSet(T)
+ for i, n := 0, mset.Len(); i < n; i++ {
+ sel := mset.At(i)
+ if c.name == "" || c.name == sel.Obj().Name() {
+ // type Method struct {
+ // 0 __identity__
+ // 1 Name string
+ // 2 PkgPath string
+ // 3 Type Type
+ // 4 Func Value
+ // 5 Index int
+ // }
+
+ var sig *types.Signature
+ var fn *ssa.Function
+ if isIface {
+ sig = sel.Type().(*types.Signature)
+ } else {
+ fn = a.prog.MethodValue(sel)
+ // move receiver to params[0]
+ sig = changeRecv(fn.Signature)
+ }
+
+ // a.offsetOf(Type) is 3.
+ if id := c.result + 3; a.addLabel(id, a.makeRtype(sig)) {
+ a.addWork(id)
+ }
+ if fn != nil {
+ // a.offsetOf(Func) is 4.
+ if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) {
+ a.addWork(id)
+ }
+ }
+ }
+ }
+ }
+}
+
+func extÛ°reflectÛ°rtypeÛ°MethodByName(a *analysis, cgn *cgnode) {
+ // If we have access to the callsite,
+ // and the argument is a string constant,
+ // return only that method.
+ var name string
+ if site := cgn.callersite; site != nil {
+ if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+ name = exact.StringVal(c.Value)
+ }
+ }
+
+ a.addConstraint(&rtypeMethodByNameConstraint{
+ cgn: cgn,
+ name: name,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+func extÛ°reflectÛ°rtypeÛ°Method(a *analysis, cgn *cgnode) {
+ // No-one ever calls Method with a constant argument,
+ // so we don't specialize that case.
+ a.addConstraint(&rtypeMethodByNameConstraint{
+ cgn: cgn,
+ t: a.funcParams(cgn.obj),
+ result: a.funcResults(cgn.obj),
+ })
+}
+
+// typeHeight returns the "height" of the type, which is roughly
+// speaking the number of chan, map, pointer and slice type constructors
+// at the root of T; these are the four type kinds that can be created
+// via reflection. Chan and map constructors are counted as double the
+// height of slice and pointer constructors since they are less often
+// deeply nested.
+//
+// The solver rules for type constructors must somehow bound the set of
+// types they create to ensure termination of the algorithm in cases
+// where the output of a type constructor flows to its input, e.g.
+//
+// func f(t reflect.Type) {
+// f(reflect.PtrTo(t))
+// }
+//
+// It does this by limiting the type height to k, but this still leaves
+// a potentially exponential (4^k) number of of types that may be
+// enumerated in pathological cases.
+//
+func typeHeight(T types.Type) int {
+ switch T := T.(type) {
+ case *types.Chan:
+ return 2 + typeHeight(T.Elem())
+ case *types.Map:
+ k := typeHeight(T.Key())
+ v := typeHeight(T.Elem())
+ if v > k {
+ k = v // max(k, v)
+ }
+ return 2 + k
+ case *types.Slice:
+ return 1 + typeHeight(T.Elem())
+ case *types.Pointer:
+ return 1 + typeHeight(T.Elem())
+ }
+ return 0
+}
+
+func typeTooHigh(T types.Type) bool {
+ return typeHeight(T) > 3
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/solve.go b/go/src/golang.org/x/tools/go/pointer/solve.go
index ad1a65f..3c60685 100644
--- a/go/src/golang.org/x/tools/go/pointer/solve.go
+++ b/go/src/golang.org/x/tools/go/pointer/solve.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
// This file defines a naive Andersen-style solver for the inclusion
@@ -9,8 +11,7 @@
import (
"fmt"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
type solverState struct {
diff --git a/go/src/golang.org/x/tools/go/pointer/solve14.go b/go/src/golang.org/x/tools/go/pointer/solve14.go
new file mode 100644
index 0000000..25b52db
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/solve14.go
@@ -0,0 +1,373 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+// This file defines a naive Andersen-style solver for the inclusion
+// constraint system.
+
+import (
+ "fmt"
+
+ "golang.org/x/tools/go/types"
+)
+
+type solverState struct {
+ complex []constraint // complex constraints attached to this node
+ copyTo nodeset // simple copy constraint edges
+ pts nodeset // points-to set of this node
+ prevPTS nodeset // pts(n) in previous iteration (for difference propagation)
+}
+
+func (a *analysis) solve() {
+ start("Solving")
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\n\n==== Solving constraints\n\n")
+ }
+
+ // Solver main loop.
+ var delta nodeset
+ for {
+ // Add new constraints to the graph:
+ // static constraints from SSA on round 1,
+ // dynamic constraints from reflection thereafter.
+ a.processNewConstraints()
+
+ var x int
+ if !a.work.TakeMin(&x) {
+ break // empty
+ }
+ id := nodeid(x)
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\tnode n%d\n", id)
+ }
+
+ n := a.nodes[id]
+
+ // Difference propagation.
+ delta.Difference(&n.solve.pts.Sparse, &n.solve.prevPTS.Sparse)
+ if delta.IsEmpty() {
+ continue
+ }
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t\tpts(n%d : %s) = %s + %s\n",
+ id, n.typ, &delta, &n.solve.prevPTS)
+ }
+ n.solve.prevPTS.Copy(&n.solve.pts.Sparse)
+
+ // Apply all resolution rules attached to n.
+ a.solveConstraints(n, &delta)
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, &n.solve.pts)
+ }
+ }
+
+ if !a.nodes[0].solve.pts.IsEmpty() {
+ panic(fmt.Sprintf("pts(0) is nonempty: %s", &a.nodes[0].solve.pts))
+ }
+
+ // Release working state (but keep final PTS).
+ for _, n := range a.nodes {
+ n.solve.complex = nil
+ n.solve.copyTo.Clear()
+ n.solve.prevPTS.Clear()
+ }
+
+ if a.log != nil {
+ fmt.Fprintf(a.log, "Solver done\n")
+
+ // Dump solution.
+ for i, n := range a.nodes {
+ if !n.solve.pts.IsEmpty() {
+ fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, &n.solve.pts, n.typ)
+ }
+ }
+ }
+ stop("Solving")
+}
+
+// processNewConstraints takes the new constraints from a.constraints
+// and adds them to the graph, ensuring
+// that new constraints are applied to pre-existing labels and
+// that pre-existing constraints are applied to new labels.
+//
+func (a *analysis) processNewConstraints() {
+ // Take the slice of new constraints.
+ // (May grow during call to solveConstraints.)
+ constraints := a.constraints
+ a.constraints = nil
+
+ // Initialize points-to sets from addr-of (base) constraints.
+ for _, c := range constraints {
+ if c, ok := c.(*addrConstraint); ok {
+ dst := a.nodes[c.dst]
+ dst.solve.pts.add(c.src)
+
+ // Populate the worklist with nodes that point to
+ // something initially (due to addrConstraints) and
+ // have other constraints attached.
+ // (A no-op in round 1.)
+ if !dst.solve.copyTo.IsEmpty() || len(dst.solve.complex) > 0 {
+ a.addWork(c.dst)
+ }
+ }
+ }
+
+ // Attach simple (copy) and complex constraints to nodes.
+ var stale nodeset
+ for _, c := range constraints {
+ var id nodeid
+ switch c := c.(type) {
+ case *addrConstraint:
+ // base constraints handled in previous loop
+ continue
+ case *copyConstraint:
+ // simple (copy) constraint
+ id = c.src
+ a.nodes[id].solve.copyTo.add(c.dst)
+ default:
+ // complex constraint
+ id = c.ptr()
+ solve := a.nodes[id].solve
+ solve.complex = append(solve.complex, c)
+ }
+
+ if n := a.nodes[id]; !n.solve.pts.IsEmpty() {
+ if !n.solve.prevPTS.IsEmpty() {
+ stale.add(id)
+ }
+ a.addWork(id)
+ }
+ }
+ // Apply new constraints to pre-existing PTS labels.
+ var space [50]int
+ for _, id := range stale.AppendTo(space[:0]) {
+ n := a.nodes[nodeid(id)]
+ a.solveConstraints(n, &n.solve.prevPTS)
+ }
+}
+
+// solveConstraints applies each resolution rule attached to node n to
+// the set of labels delta. It may generate new constraints in
+// a.constraints.
+//
+func (a *analysis) solveConstraints(n *node, delta *nodeset) {
+ if delta.IsEmpty() {
+ return
+ }
+
+ // Process complex constraints dependent on n.
+ for _, c := range n.solve.complex {
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t\tconstraint %s\n", c)
+ }
+ c.solve(a, delta)
+ }
+
+ // Process copy constraints.
+ var copySeen nodeset
+ for _, x := range n.solve.copyTo.AppendTo(a.deltaSpace) {
+ mid := nodeid(x)
+ if copySeen.add(mid) {
+ if a.nodes[mid].solve.pts.addAll(delta) {
+ a.addWork(mid)
+ }
+ }
+ }
+}
+
+// addLabel adds label to the points-to set of ptr and reports whether the set grew.
+func (a *analysis) addLabel(ptr, label nodeid) bool {
+ b := a.nodes[ptr].solve.pts.add(label)
+ if b && a.log != nil {
+ fmt.Fprintf(a.log, "\t\tpts(n%d) += n%d\n", ptr, label)
+ }
+ return b
+}
+
+func (a *analysis) addWork(id nodeid) {
+ a.work.Insert(int(id))
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t\twork: n%d\n", id)
+ }
+}
+
+// onlineCopy adds a copy edge. It is called online, i.e. during
+// solving, so it adds edges and pts members directly rather than by
+// instantiating a 'constraint'.
+//
+// The size of the copy is implicitly 1.
+// It returns true if pts(dst) changed.
+//
+func (a *analysis) onlineCopy(dst, src nodeid) bool {
+ if dst != src {
+ if nsrc := a.nodes[src]; nsrc.solve.copyTo.add(dst) {
+ if a.log != nil {
+ fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src)
+ }
+ // TODO(adonovan): most calls to onlineCopy
+ // are followed by addWork, possibly batched
+ // via a 'changed' flag; see if there's a
+ // noticeable penalty to calling addWork here.
+ return a.nodes[dst].solve.pts.addAll(&nsrc.solve.pts)
+ }
+ }
+ return false
+}
+
+// Returns sizeof.
+// Implicitly adds nodes to worklist.
+//
+// TODO(adonovan): now that we support a.copy() during solving, we
+// could eliminate onlineCopyN, but it's much slower. Investigate.
+//
+func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
+ for i := uint32(0); i < sizeof; i++ {
+ if a.onlineCopy(dst, src) {
+ a.addWork(dst)
+ }
+ src++
+ dst++
+ }
+ return sizeof
+}
+
+func (c *loadConstraint) solve(a *analysis, delta *nodeset) {
+ var changed bool
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ k := nodeid(x)
+ koff := k + nodeid(c.offset)
+ if a.onlineCopy(c.dst, koff) {
+ changed = true
+ }
+ }
+ if changed {
+ a.addWork(c.dst)
+ }
+}
+
+func (c *storeConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ k := nodeid(x)
+ koff := k + nodeid(c.offset)
+ if a.onlineCopy(koff, c.src) {
+ a.addWork(koff)
+ }
+ }
+}
+
+func (c *offsetAddrConstraint) solve(a *analysis, delta *nodeset) {
+ dst := a.nodes[c.dst]
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ k := nodeid(x)
+ if dst.solve.pts.add(k + nodeid(c.offset)) {
+ a.addWork(c.dst)
+ }
+ }
+}
+
+func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ ifaceObj := nodeid(x)
+ tDyn, _, indirect := a.taggedValue(ifaceObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ if types.AssignableTo(tDyn, c.typ) {
+ if a.addLabel(c.dst, ifaceObj) {
+ a.addWork(c.dst)
+ }
+ }
+ }
+}
+
+func (c *untagConstraint) solve(a *analysis, delta *nodeset) {
+ predicate := types.AssignableTo
+ if c.exact {
+ predicate = types.Identical
+ }
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ ifaceObj := nodeid(x)
+ tDyn, v, indirect := a.taggedValue(ifaceObj)
+ if indirect {
+ // TODO(adonovan): we'll need to implement this
+ // when we start creating indirect tagged objects.
+ panic("indirect tagged object")
+ }
+
+ if predicate(tDyn, c.typ) {
+ // Copy payload sans tag to dst.
+ //
+ // TODO(adonovan): opt: if tDyn is
+ // nonpointerlike we can skip this entire
+ // constraint, perhaps. We only care about
+ // pointers among the fields.
+ a.onlineCopyN(c.dst, v, a.sizeof(tDyn))
+ }
+ }
+}
+
+func (c *invokeConstraint) solve(a *analysis, delta *nodeset) {
+ for _, x := range delta.AppendTo(a.deltaSpace) {
+ ifaceObj := nodeid(x)
+ tDyn, v, indirect := a.taggedValue(ifaceObj)
+ if indirect {
+ // TODO(adonovan): we may need to implement this if
+ // we ever apply invokeConstraints to reflect.Value PTSs,
+ // e.g. for (reflect.Value).Call.
+ panic("indirect tagged object")
+ }
+
+ // Look up the concrete method.
+ fn := a.prog.LookupMethod(tDyn, c.method.Pkg(), c.method.Name())
+ if fn == nil {
+ panic(fmt.Sprintf("n%d: no ssa.Function for %s", c.iface, c.method))
+ }
+ sig := fn.Signature
+
+ fnObj := a.globalobj[fn] // dynamic calls use shared contour
+ if fnObj == 0 {
+ // a.objectNode(fn) was not called during gen phase.
+ panic(fmt.Sprintf("a.globalobj[%s]==nil", fn))
+ }
+
+ // Make callsite's fn variable point to identity of
+ // concrete method. (There's no need to add it to
+ // worklist since it never has attached constraints.)
+ a.addLabel(c.params, fnObj)
+
+ // Extract value and connect to method's receiver.
+ // Copy payload to method's receiver param (arg0).
+ arg0 := a.funcParams(fnObj)
+ recvSize := a.sizeof(sig.Recv().Type())
+ a.onlineCopyN(arg0, v, recvSize)
+
+ src := c.params + 1 // skip past identity
+ dst := arg0 + nodeid(recvSize)
+
+ // Copy caller's argument block to method formal parameters.
+ paramsSize := a.sizeof(sig.Params())
+ a.onlineCopyN(dst, src, paramsSize)
+ src += nodeid(paramsSize)
+ dst += nodeid(paramsSize)
+
+ // Copy method results to caller's result block.
+ resultsSize := a.sizeof(sig.Results())
+ a.onlineCopyN(src, dst, resultsSize)
+ }
+}
+
+func (c *addrConstraint) solve(a *analysis, delta *nodeset) {
+ panic("addr is not a complex constraint")
+}
+
+func (c *copyConstraint) solve(a *analysis, delta *nodeset) {
+ panic("copy is not a complex constraint")
+}
diff --git a/go/src/golang.org/x/tools/go/pointer/stdlib_test.go b/go/src/golang.org/x/tools/go/pointer/stdlib_test.go
index ef7c652..d3d14ea 100644
--- a/go/src/golang.org/x/tools/go/pointer/stdlib_test.go
+++ b/go/src/golang.org/x/tools/go/pointer/stdlib_test.go
@@ -52,7 +52,7 @@
// Create SSA packages.
prog := ssautil.CreateProgram(iprog, 0)
- prog.BuildAll()
+ prog.Build()
numPkgs := len(prog.AllPackages())
if want := 240; numPkgs < want {
diff --git a/go/src/golang.org/x/tools/go/pointer/testdata/maps.go b/go/src/golang.org/x/tools/go/pointer/testdata/maps.go
index 6f3751d..6729304 100644
--- a/go/src/golang.org/x/tools/go/pointer/testdata/maps.go
+++ b/go/src/golang.org/x/tools/go/pointer/testdata/maps.go
@@ -45,7 +45,30 @@
print(m2[nil]) // @pointsto main.c
}
+var g int
+
+func maps3() {
+ // Regression test for a constraint generation bug for map range
+ // loops in which the key is unused: the (ok, k, v) tuple
+ // returned by ssa.Next may have type 'invalid' for the k and/or
+ // v components, so copying the map key or value may cause
+ // miswiring if the key has >1 components. In the worst case,
+ // this causes a crash. The test below used to report that
+ // pts(v) includes not just main.g but new(float64) too, which
+ // is ill-typed.
+
+ // sizeof(K) > 1, abstractly
+ type K struct{ a, b *float64 }
+ k := K{new(float64), nil}
+ m := map[K]*int{k: &g}
+
+ for _, v := range m {
+ print(v) // @pointsto main.g
+ }
+}
+
func main() {
maps1()
maps2()
+ maps3()
}
diff --git a/go/src/golang.org/x/tools/go/pointer/util.go b/go/src/golang.org/x/tools/go/pointer/util.go
index d4ccbb5..4d2fa74 100644
--- a/go/src/golang.org/x/tools/go/pointer/util.go
+++ b/go/src/golang.org/x/tools/go/pointer/util.go
@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package pointer
import (
"bytes"
"fmt"
+ "go/types"
"log"
"os"
"os/exec"
@@ -14,7 +17,6 @@
"time"
"golang.org/x/tools/container/intsets"
- "golang.org/x/tools/go/types"
)
// CanPoint reports whether the type T is pointerlike,
diff --git a/go/src/golang.org/x/tools/go/pointer/util14.go b/go/src/golang.org/x/tools/go/pointer/util14.go
new file mode 100644
index 0000000..d04deeb
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/pointer/util14.go
@@ -0,0 +1,316 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package pointer
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+ "runtime"
+ "time"
+
+ "golang.org/x/tools/container/intsets"
+ "golang.org/x/tools/go/types"
+)
+
+// CanPoint reports whether the type T is pointerlike,
+// for the purposes of this analysis.
+func CanPoint(T types.Type) bool {
+ switch T := T.(type) {
+ case *types.Named:
+ if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
+ return true // treat reflect.Value like interface{}
+ }
+ return CanPoint(T.Underlying())
+
+ case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice:
+ return true
+ }
+
+ return false // array struct tuple builtin basic
+}
+
+// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types,
+// i.e. is an interface (incl. reflect.Type) or a reflect.Value.
+//
+func CanHaveDynamicTypes(T types.Type) bool {
+ switch T := T.(type) {
+ case *types.Named:
+ if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
+ return true // reflect.Value
+ }
+ return CanHaveDynamicTypes(T.Underlying())
+ case *types.Interface:
+ return true
+ }
+ return false
+}
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }
+
+// mustDeref returns the element type of its argument, which must be a
+// pointer; panic ensues otherwise.
+func mustDeref(typ types.Type) types.Type {
+ return typ.Underlying().(*types.Pointer).Elem()
+}
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
+
+// A fieldInfo describes one subelement (node) of the flattening-out
+// of a type T: the subelement's type and its path from the root of T.
+//
+// For example, for this type:
+// type line struct{ points []struct{x, y int} }
+// flatten() of the inner struct yields the following []fieldInfo:
+// struct{ x, y int } ""
+// int ".x"
+// int ".y"
+// and flatten(line) yields:
+// struct{ points []struct{x, y int} } ""
+// struct{ x, y int } ".points[*]"
+// int ".points[*].x
+// int ".points[*].y"
+//
+type fieldInfo struct {
+ typ types.Type
+
+ // op and tail describe the path to the element (e.g. ".a#2.b[*].c").
+ op interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil
+ tail *fieldInfo
+}
+
+// path returns a user-friendly string describing the subelement path.
+//
+func (fi *fieldInfo) path() string {
+ var buf bytes.Buffer
+ for p := fi; p != nil; p = p.tail {
+ switch op := p.op.(type) {
+ case bool:
+ fmt.Fprintf(&buf, "[*]")
+ case int:
+ fmt.Fprintf(&buf, "#%d", op)
+ case *types.Var:
+ fmt.Fprintf(&buf, ".%s", op.Name())
+ }
+ }
+ return buf.String()
+}
+
+// flatten returns a list of directly contained fields in the preorder
+// traversal of the type tree of t. The resulting elements are all
+// scalars (basic types or pointerlike types), except for struct/array
+// "identity" nodes, whose type is that of the aggregate.
+//
+// reflect.Value is considered pointerlike, similar to interface{}.
+//
+// Callers must not mutate the result.
+//
+func (a *analysis) flatten(t types.Type) []*fieldInfo {
+ fl, ok := a.flattenMemo[t]
+ if !ok {
+ switch t := t.(type) {
+ case *types.Named:
+ u := t.Underlying()
+ if isInterface(u) {
+ // Debuggability hack: don't remove
+ // the named type from interfaces as
+ // they're very verbose.
+ fl = append(fl, &fieldInfo{typ: t})
+ } else {
+ fl = a.flatten(u)
+ }
+
+ case *types.Basic,
+ *types.Signature,
+ *types.Chan,
+ *types.Map,
+ *types.Interface,
+ *types.Slice,
+ *types.Pointer:
+ fl = append(fl, &fieldInfo{typ: t})
+
+ case *types.Array:
+ fl = append(fl, &fieldInfo{typ: t}) // identity node
+ for _, fi := range a.flatten(t.Elem()) {
+ fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi})
+ }
+
+ case *types.Struct:
+ fl = append(fl, &fieldInfo{typ: t}) // identity node
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ f := t.Field(i)
+ for _, fi := range a.flatten(f.Type()) {
+ fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi})
+ }
+ }
+
+ case *types.Tuple:
+ // No identity node: tuples are never address-taken.
+ n := t.Len()
+ if n == 1 {
+ // Don't add a fieldInfo link for singletons,
+ // e.g. in params/results.
+ fl = append(fl, a.flatten(t.At(0).Type())...)
+ } else {
+ for i := 0; i < n; i++ {
+ f := t.At(i)
+ for _, fi := range a.flatten(f.Type()) {
+ fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
+ }
+ }
+ }
+
+ default:
+ panic(t)
+ }
+
+ a.flattenMemo[t] = fl
+ }
+
+ return fl
+}
+
+// sizeof returns the number of pointerlike abstractions (nodes) in the type t.
+func (a *analysis) sizeof(t types.Type) uint32 {
+ return uint32(len(a.flatten(t)))
+}
+
+// shouldTrack reports whether object type T contains (recursively)
+// any fields whose addresses should be tracked.
+func (a *analysis) shouldTrack(T types.Type) bool {
+ if a.track == trackAll {
+ return true // fast path
+ }
+ track, ok := a.trackTypes[T]
+ if !ok {
+ a.trackTypes[T] = true // break cycles conservatively
+ // NB: reflect.Value, reflect.Type are pre-populated to true.
+ for _, fi := range a.flatten(T) {
+ switch ft := fi.typ.Underlying().(type) {
+ case *types.Interface, *types.Signature:
+ track = true // needed for callgraph
+ case *types.Basic:
+ // no-op
+ case *types.Chan:
+ track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem())
+ case *types.Map:
+ track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem())
+ case *types.Slice:
+ track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem())
+ case *types.Pointer:
+ track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem())
+ case *types.Array, *types.Struct:
+ // No need to look at field types since they will follow (flattened).
+ default:
+ // Includes *types.Tuple, which are never address-taken.
+ panic(ft)
+ }
+ if track {
+ break
+ }
+ }
+ a.trackTypes[T] = track
+ if !track && a.log != nil {
+ fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T)
+ }
+ }
+ return track
+}
+
+// offsetOf returns the (abstract) offset of field index within struct
+// or tuple typ.
+func (a *analysis) offsetOf(typ types.Type, index int) uint32 {
+ var offset uint32
+ switch t := typ.Underlying().(type) {
+ case *types.Tuple:
+ for i := 0; i < index; i++ {
+ offset += a.sizeof(t.At(i).Type())
+ }
+ case *types.Struct:
+ offset++ // the node for the struct itself
+ for i := 0; i < index; i++ {
+ offset += a.sizeof(t.Field(i).Type())
+ }
+ default:
+ panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ))
+ }
+ return offset
+}
+
+// sliceToArray returns the type representing the arrays to which
+// slice type slice points.
+func sliceToArray(slice types.Type) *types.Array {
+ return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1)
+}
+
+// Node set -------------------------------------------------------------------
+
+type nodeset struct {
+ intsets.Sparse
+}
+
+func (ns *nodeset) String() string {
+ var buf bytes.Buffer
+ buf.WriteRune('{')
+ var space [50]int
+ for i, n := range ns.AppendTo(space[:0]) {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteRune('n')
+ fmt.Fprintf(&buf, "%d", n)
+ }
+ buf.WriteRune('}')
+ return buf.String()
+}
+
+func (ns *nodeset) add(n nodeid) bool {
+ return ns.Sparse.Insert(int(n))
+}
+
+func (x *nodeset) addAll(y *nodeset) bool {
+ return x.UnionWith(&y.Sparse)
+}
+
+// Profiling & debugging -------------------------------------------------------
+
+var timers = make(map[string]time.Time)
+
+func start(name string) {
+ if debugTimers {
+ timers[name] = time.Now()
+ log.Printf("%s...\n", name)
+ }
+}
+
+func stop(name string) {
+ if debugTimers {
+ log.Printf("%s took %s\n", name, time.Since(timers[name]))
+ }
+}
+
+// diff runs the command "diff a b" and reports its success.
+func diff(a, b string) bool {
+ var cmd *exec.Cmd
+ switch runtime.GOOS {
+ case "plan9":
+ cmd = exec.Command("/bin/diff", "-c", a, b)
+ default:
+ cmd = exec.Command("/usr/bin/diff", "-u", a, b)
+ }
+ cmd.Stdout = os.Stderr
+ cmd.Stderr = os.Stderr
+ return cmd.Run() == nil
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/builder.go b/go/src/golang.org/x/tools/go/ssa/builder.go
index 5b8ce0e..4707ebe 100644
--- a/go/src/golang.org/x/tools/go/ssa/builder.go
+++ b/go/src/golang.org/x/tools/go/ssa/builder.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file implements the BUILD phase of SSA construction.
@@ -32,13 +34,11 @@
import (
"fmt"
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"os"
"sync"
- "sync/atomic"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
)
type opaqueType struct {
@@ -243,7 +243,7 @@
}
if m, ok := m.(*Const); ok {
// treat make([]T, n, m) as new([m]T)[:n]
- cap, _ := exact.Int64Val(m.Value)
+ cap := m.Int64()
at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap)
alloc := emitNew(fn, at, pos)
alloc.Comment = "makeslice"
@@ -1055,6 +1055,7 @@
} else {
// e.g. x, y = pos()
tuple := b.exprN(fn, rhss[0])
+ emitDebugRef(fn, rhss[0], tuple, false)
for i, lval := range lvals {
lval.store(fn, emitExtract(fn, tuple, i))
}
@@ -1194,9 +1195,26 @@
fn.emit(m)
for _, e := range e.Elts {
e := e.(*ast.KeyValueExpr)
+
+ // If a key expression in a map literal is itself a
+ // composite literal, the type may be omitted.
+ // For example:
+ // map[*struct{}]bool{{}: true}
+ // An &-operation may be implied:
+ // map[*struct{}]bool{&struct{}{}: true}
+ var key Value
+ if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) {
+ // A CompositeLit never evaluates to a pointer,
+ // so if the type of the location is a pointer,
+ // an &-operation is implied.
+ key = b.addr(fn, e.Key, true).address(fn)
+ } else {
+ key = b.expr(fn, e.Key)
+ }
+
loc := element{
m: m,
- k: emitConv(fn, b.expr(fn, e.Key), t.Key()),
+ k: emitConv(fn, key, t.Key()),
t: t.Elem(),
pos: e.Colon,
}
@@ -2217,9 +2235,7 @@
//
// BuildAll is idempotent and thread-safe.
//
-// TODO(adonovan): rename to Build.
-//
-func (prog *Program) BuildAll() {
+func (prog *Program) Build() {
var wg sync.WaitGroup
for _, p := range prog.packages {
if prog.mode&BuildSerially != 0 {
@@ -2243,10 +2259,9 @@
//
// Build is idempotent and thread-safe.
//
-func (p *Package) Build() {
- if !atomic.CompareAndSwapInt32(&p.started, 0, 1) {
- return // already started
- }
+func (p *Package) Build() { p.buildOnce.Do(p.build) }
+
+func (p *Package) build() {
if p.info == nil {
return // synthetic package, e.g. "testmain"
}
@@ -2281,10 +2296,10 @@
emitStore(init, initguard, vTrue, token.NoPos)
// Call the init() function of each package we import.
- for _, pkg := range p.Object.Imports() {
+ for _, pkg := range p.Pkg.Imports() {
prereq := p.Prog.packages[pkg]
if prereq == nil {
- panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Object.Path(), pkg.Path()))
+ panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path()))
}
var v Call
v.Call.Value = prereq.init
diff --git a/go/src/golang.org/x/tools/go/ssa/builder14.go b/go/src/golang.org/x/tools/go/ssa/builder14.go
new file mode 100644
index 0000000..fcc4b92
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/builder14.go
@@ -0,0 +1,2386 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file implements the BUILD phase of SSA construction.
+//
+// SSA construction has two phases, CREATE and BUILD. In the CREATE phase
+// (create.go), all packages are constructed and type-checked and
+// definitions of all package members are created, method-sets are
+// computed, and wrapper methods are synthesized.
+// ssa.Packages are created in arbitrary order.
+//
+// In the BUILD phase (builder.go), the builder traverses the AST of
+// each Go source function and generates SSA instructions for the
+// function body. Initializer expressions for package-level variables
+// are emitted to the package's init() function in the order specified
+// by go/types.Info.InitOrder, then code for each function in the
+// package is generated in lexical order.
+// The BUILD phases for distinct packages are independent and are
+// executed in parallel.
+//
+// TODO(adonovan): indeed, building functions is now embarrassingly parallel.
+// Audit for concurrency then benchmark using more goroutines.
+//
+// The builder's and Program's indices (maps) are populated and
+// mutated during the CREATE phase, but during the BUILD phase they
+// remain constant. The sole exception is Prog.methodSets and its
+// related maps, which are protected by a dedicated mutex.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "os"
+ "sync"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+)
+
+type opaqueType struct {
+ types.Type
+ name string
+}
+
+func (t *opaqueType) String() string { return t.name }
+
+var (
+ varOk = newVar("ok", tBool)
+ varIndex = newVar("index", tInt)
+
+ // Type constants.
+ tBool = types.Typ[types.Bool]
+ tByte = types.Typ[types.Byte]
+ tInt = types.Typ[types.Int]
+ tInvalid = types.Typ[types.Invalid]
+ tString = types.Typ[types.String]
+ tUntypedNil = types.Typ[types.UntypedNil]
+ tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators
+ tEface = new(types.Interface)
+
+ // SSA Value constants.
+ vZero = intConst(0)
+ vOne = intConst(1)
+ vTrue = NewConst(exact.MakeBool(true), tBool)
+)
+
+// builder holds state associated with the package currently being built.
+// Its methods contain all the logic for AST-to-SSA conversion.
+type builder struct{}
+
+// cond emits to fn code to evaluate boolean condition e and jump
+// to t or f depending on its value, performing various simplifications.
+//
+// Postcondition: fn.currentBlock is nil.
+//
+func (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) {
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ b.cond(fn, e.X, t, f)
+ return
+
+ case *ast.BinaryExpr:
+ switch e.Op {
+ case token.LAND:
+ ltrue := fn.newBasicBlock("cond.true")
+ b.cond(fn, e.X, ltrue, f)
+ fn.currentBlock = ltrue
+ b.cond(fn, e.Y, t, f)
+ return
+
+ case token.LOR:
+ lfalse := fn.newBasicBlock("cond.false")
+ b.cond(fn, e.X, t, lfalse)
+ fn.currentBlock = lfalse
+ b.cond(fn, e.Y, t, f)
+ return
+ }
+
+ case *ast.UnaryExpr:
+ if e.Op == token.NOT {
+ b.cond(fn, e.X, f, t)
+ return
+ }
+ }
+
+ // A traditional compiler would simplify "if false" (etc) here
+ // but we do not, for better fidelity to the source code.
+ //
+ // The value of a constant condition may be platform-specific,
+ // and may cause blocks that are reachable in some configuration
+ // to be hidden from subsequent analyses such as bug-finding tools.
+ emitIf(fn, b.expr(fn, e), t, f)
+}
+
+// logicalBinop emits code to fn to evaluate e, a &&- or
+// ||-expression whose reified boolean value is wanted.
+// The value is returned.
+//
+func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
+ rhs := fn.newBasicBlock("binop.rhs")
+ done := fn.newBasicBlock("binop.done")
+
+ // T(e) = T(e.X) = T(e.Y) after untyped constants have been
+ // eliminated.
+ // TODO(adonovan): not true; MyBool==MyBool yields UntypedBool.
+ t := fn.Pkg.typeOf(e)
+
+ var short Value // value of the short-circuit path
+ switch e.Op {
+ case token.LAND:
+ b.cond(fn, e.X, rhs, done)
+ short = NewConst(exact.MakeBool(false), t)
+
+ case token.LOR:
+ b.cond(fn, e.X, done, rhs)
+ short = NewConst(exact.MakeBool(true), t)
+ }
+
+ // Is rhs unreachable?
+ if rhs.Preds == nil {
+ // Simplify false&&y to false, true||y to true.
+ fn.currentBlock = done
+ return short
+ }
+
+ // Is done unreachable?
+ if done.Preds == nil {
+ // Simplify true&&y (or false||y) to y.
+ fn.currentBlock = rhs
+ return b.expr(fn, e.Y)
+ }
+
+ // All edges from e.X to done carry the short-circuit value.
+ var edges []Value
+ for _ = range done.Preds {
+ edges = append(edges, short)
+ }
+
+ // The edge from e.Y to done carries the value of e.Y.
+ fn.currentBlock = rhs
+ edges = append(edges, b.expr(fn, e.Y))
+ emitJump(fn, done)
+ fn.currentBlock = done
+
+ phi := &Phi{Edges: edges, Comment: e.Op.String()}
+ phi.pos = e.OpPos
+ phi.typ = t
+ return done.emit(phi)
+}
+
+// exprN lowers a multi-result expression e to SSA form, emitting code
+// to fn and returning a single Value whose type is a *types.Tuple.
+// The caller must access the components via Extract.
+//
+// Multi-result expressions include CallExprs in a multi-value
+// assignment or return statement, and "value,ok" uses of
+// TypeAssertExpr, IndexExpr (when X is a map), and UnaryExpr (when Op
+// is token.ARROW).
+//
+func (b *builder) exprN(fn *Function, e ast.Expr) Value {
+ typ := fn.Pkg.typeOf(e).(*types.Tuple)
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ return b.exprN(fn, e.X)
+
+ case *ast.CallExpr:
+ // Currently, no built-in function nor type conversion
+ // has multiple results, so we can avoid some of the
+ // cases for single-valued CallExpr.
+ var c Call
+ b.setCall(fn, e, &c.Call)
+ c.typ = typ
+ return fn.emit(&c)
+
+ case *ast.IndexExpr:
+ mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
+ lookup := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
+ CommaOk: true,
+ }
+ lookup.setType(typ)
+ lookup.setPos(e.Lbrack)
+ return fn.emit(lookup)
+
+ case *ast.TypeAssertExpr:
+ return emitTypeTest(fn, b.expr(fn, e.X), typ.At(0).Type(), e.Lparen)
+
+ case *ast.UnaryExpr: // must be receive <-
+ unop := &UnOp{
+ Op: token.ARROW,
+ X: b.expr(fn, e.X),
+ CommaOk: true,
+ }
+ unop.setType(typ)
+ unop.setPos(e.OpPos)
+ return fn.emit(unop)
+ }
+ panic(fmt.Sprintf("exprN(%T) in %s", e, fn))
+}
+
+// builtin emits to fn SSA instructions to implement a call to the
+// built-in function obj with the specified arguments
+// and return type. It returns the value defined by the result.
+//
+// The result is nil if no special handling was required; in this case
+// the caller should treat this like an ordinary library function
+// call.
+//
+func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, pos token.Pos) Value {
+ switch obj.Name() {
+ case "make":
+ switch typ.Underlying().(type) {
+ case *types.Slice:
+ n := b.expr(fn, args[1])
+ m := n
+ if len(args) == 3 {
+ m = b.expr(fn, args[2])
+ }
+ if m, ok := m.(*Const); ok {
+ // treat make([]T, n, m) as new([m]T)[:n]
+ cap, _ := exact.Int64Val(m.Value)
+ at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap)
+ alloc := emitNew(fn, at, pos)
+ alloc.Comment = "makeslice"
+ v := &Slice{
+ X: alloc,
+ High: n,
+ }
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+ }
+ v := &MakeSlice{
+ Len: n,
+ Cap: m,
+ }
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+
+ case *types.Map:
+ var res Value
+ if len(args) == 2 {
+ res = b.expr(fn, args[1])
+ }
+ v := &MakeMap{Reserve: res}
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+
+ case *types.Chan:
+ var sz Value = vZero
+ if len(args) == 2 {
+ sz = b.expr(fn, args[1])
+ }
+ v := &MakeChan{Size: sz}
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+ }
+
+ case "new":
+ alloc := emitNew(fn, deref(typ), pos)
+ alloc.Comment = "new"
+ return alloc
+
+ case "len", "cap":
+ // Special case: len or cap of an array or *array is
+ // based on the type, not the value which may be nil.
+ // We must still evaluate the value, though. (If it
+ // was side-effect free, the whole call would have
+ // been constant-folded.)
+ t := deref(fn.Pkg.typeOf(args[0])).Underlying()
+ if at, ok := t.(*types.Array); ok {
+ b.expr(fn, args[0]) // for effects only
+ return intConst(at.Len())
+ }
+ // Otherwise treat as normal.
+
+ case "panic":
+ fn.emit(&Panic{
+ X: emitConv(fn, b.expr(fn, args[0]), tEface),
+ pos: pos,
+ })
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ return vTrue // any non-nil Value will do
+ }
+ return nil // treat all others as a regular function call
+}
+
+// addr lowers a single-result addressable expression e to SSA form,
+// emitting code to fn and returning the location (an lvalue) defined
+// by the expression.
+//
+// If escaping is true, addr marks the base variable of the
+// addressable expression e as being a potentially escaping pointer
+// value. For example, in this code:
+//
+// a := A{
+// b: [1]B{B{c: 1}}
+// }
+// return &a.b[0].c
+//
+// the application of & causes a.b[0].c to have its address taken,
+// which means that ultimately the local variable a must be
+// heap-allocated. This is a simple but very conservative escape
+// analysis.
+//
+// Operations forming potentially escaping pointers include:
+// - &x, including when implicit in method call or composite literals.
+// - a[:] iff a is an array (not *array)
+// - references to variables in lexically enclosing functions.
+//
+func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
+ switch e := e.(type) {
+ case *ast.Ident:
+ if isBlankIdent(e) {
+ return blank{}
+ }
+ obj := fn.Pkg.objectOf(e)
+ v := fn.Prog.packageLevelValue(obj) // var (address)
+ if v == nil {
+ v = fn.lookup(obj, escaping)
+ }
+ return &address{addr: v, pos: e.Pos(), expr: e}
+
+ case *ast.CompositeLit:
+ t := deref(fn.Pkg.typeOf(e))
+ var v *Alloc
+ if escaping {
+ v = emitNew(fn, t, e.Lbrace)
+ } else {
+ v = fn.addLocal(t, e.Lbrace)
+ }
+ v.Comment = "complit"
+ var sb storebuf
+ b.compLit(fn, v, e, true, &sb)
+ sb.emit(fn)
+ return &address{addr: v, pos: e.Lbrace, expr: e}
+
+ case *ast.ParenExpr:
+ return b.addr(fn, e.X, escaping)
+
+ case *ast.SelectorExpr:
+ sel, ok := fn.Pkg.info.Selections[e]
+ if !ok {
+ // qualified identifier
+ return b.addr(fn, e.Sel, escaping)
+ }
+ if sel.Kind() != types.FieldVal {
+ panic(sel)
+ }
+ wantAddr := true
+ v := b.receiver(fn, e.X, wantAddr, escaping, sel)
+ last := len(sel.Index()) - 1
+ return &address{
+ addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel),
+ pos: e.Sel.Pos(),
+ expr: e.Sel,
+ }
+
+ case *ast.IndexExpr:
+ var x Value
+ var et types.Type
+ switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
+ case *types.Array:
+ x = b.addr(fn, e.X, escaping).address(fn)
+ et = types.NewPointer(t.Elem())
+ case *types.Pointer: // *array
+ x = b.expr(fn, e.X)
+ et = types.NewPointer(t.Elem().Underlying().(*types.Array).Elem())
+ case *types.Slice:
+ x = b.expr(fn, e.X)
+ et = types.NewPointer(t.Elem())
+ case *types.Map:
+ return &element{
+ m: b.expr(fn, e.X),
+ k: emitConv(fn, b.expr(fn, e.Index), t.Key()),
+ t: t.Elem(),
+ pos: e.Lbrack,
+ }
+ default:
+ panic("unexpected container type in IndexExpr: " + t.String())
+ }
+ v := &IndexAddr{
+ X: x,
+ Index: emitConv(fn, b.expr(fn, e.Index), tInt),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(et)
+ return &address{addr: fn.emit(v), pos: e.Lbrack, expr: e}
+
+ case *ast.StarExpr:
+ return &address{addr: b.expr(fn, e.X), pos: e.Star, expr: e}
+ }
+
+ panic(fmt.Sprintf("unexpected address expression: %T", e))
+}
+
+type store struct {
+ lhs lvalue
+ rhs Value
+}
+
+type storebuf struct{ stores []store }
+
+func (sb *storebuf) store(lhs lvalue, rhs Value) {
+ sb.stores = append(sb.stores, store{lhs, rhs})
+}
+
+func (sb *storebuf) emit(fn *Function) {
+ for _, s := range sb.stores {
+ s.lhs.store(fn, s.rhs)
+ }
+}
+
+// assign emits to fn code to initialize the lvalue loc with the value
+// of expression e. If isZero is true, assign assumes that loc holds
+// the zero value for its type.
+//
+// This is equivalent to loc.store(fn, b.expr(fn, e)), but may generate
+// better code in some cases, e.g., for composite literals in an
+// addressable location.
+//
+// If sb is not nil, assign generates code to evaluate expression e, but
+// not to update loc. Instead, the necessary stores are appended to the
+// storebuf sb so that they can be executed later. This allows correct
+// in-place update of existing variables when the RHS is a composite
+// literal that may reference parts of the LHS.
+//
+func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf) {
+ // Can we initialize it in place?
+ if e, ok := unparen(e).(*ast.CompositeLit); ok {
+ // A CompositeLit never evaluates to a pointer,
+ // so if the type of the location is a pointer,
+ // an &-operation is implied.
+ if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
+ if isPointer(loc.typ()) {
+ ptr := b.addr(fn, e, true).address(fn)
+ // copy address
+ if sb != nil {
+ sb.store(loc, ptr)
+ } else {
+ loc.store(fn, ptr)
+ }
+ return
+ }
+ }
+
+ if _, ok := loc.(*address); ok {
+ if isInterface(loc.typ()) {
+ // e.g. var x interface{} = T{...}
+ // Can't in-place initialize an interface value.
+ // Fall back to copying.
+ } else {
+ // x = T{...} or x := T{...}
+ addr := loc.address(fn)
+ if sb != nil {
+ b.compLit(fn, addr, e, isZero, sb)
+ } else {
+ var sb storebuf
+ b.compLit(fn, addr, e, isZero, &sb)
+ sb.emit(fn)
+ }
+
+ // Subtle: emit debug ref for aggregate types only;
+ // slice and map are handled by store ops in compLit.
+ switch loc.typ().Underlying().(type) {
+ case *types.Struct, *types.Array:
+ emitDebugRef(fn, e, addr, true)
+ }
+
+ return
+ }
+ }
+ }
+
+ // simple case: just copy
+ rhs := b.expr(fn, e)
+ if sb != nil {
+ sb.store(loc, rhs)
+ } else {
+ loc.store(fn, rhs)
+ }
+}
+
+// expr lowers a single-result expression e to SSA form, emitting code
+// to fn and returning the Value defined by the expression.
+//
+func (b *builder) expr(fn *Function, e ast.Expr) Value {
+ e = unparen(e)
+
+ tv := fn.Pkg.info.Types[e]
+
+ // Is expression a constant?
+ if tv.Value != nil {
+ return NewConst(tv.Value, tv.Type)
+ }
+
+ var v Value
+ if tv.Addressable() {
+ // Prefer pointer arithmetic ({Index,Field}Addr) followed
+ // by Load over subelement extraction (e.g. Index, Field),
+ // to avoid large copies.
+ v = b.addr(fn, e, false).load(fn)
+ } else {
+ v = b.expr0(fn, e, tv)
+ }
+ if fn.debugInfo() {
+ emitDebugRef(fn, e, v, false)
+ }
+ return v
+}
+
+func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
+ switch e := e.(type) {
+ case *ast.BasicLit:
+ panic("non-constant BasicLit") // unreachable
+
+ case *ast.FuncLit:
+ fn2 := &Function{
+ name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
+ Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
+ pos: e.Type.Func,
+ parent: fn,
+ Pkg: fn.Pkg,
+ Prog: fn.Prog,
+ syntax: e,
+ }
+ fn.AnonFuncs = append(fn.AnonFuncs, fn2)
+ b.buildFunction(fn2)
+ if fn2.FreeVars == nil {
+ return fn2
+ }
+ v := &MakeClosure{Fn: fn2}
+ v.setType(tv.Type)
+ for _, fv := range fn2.FreeVars {
+ v.Bindings = append(v.Bindings, fv.outer)
+ fv.outer = nil
+ }
+ return fn.emit(v)
+
+ case *ast.TypeAssertExpr: // single-result form only
+ return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e.Lparen)
+
+ case *ast.CallExpr:
+ if fn.Pkg.info.Types[e.Fun].IsType() {
+ // Explicit type conversion, e.g. string(x) or big.Int(x)
+ x := b.expr(fn, e.Args[0])
+ y := emitConv(fn, x, tv.Type)
+ if y != x {
+ switch y := y.(type) {
+ case *Convert:
+ y.pos = e.Lparen
+ case *ChangeType:
+ y.pos = e.Lparen
+ case *MakeInterface:
+ y.pos = e.Lparen
+ }
+ }
+ return y
+ }
+ // Call to "intrinsic" built-ins, e.g. new, make, panic.
+ if id, ok := unparen(e.Fun).(*ast.Ident); ok {
+ if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {
+ if v := b.builtin(fn, obj, e.Args, tv.Type, e.Lparen); v != nil {
+ return v
+ }
+ }
+ }
+ // Regular function call.
+ var v Call
+ b.setCall(fn, e, &v.Call)
+ v.setType(tv.Type)
+ return fn.emit(&v)
+
+ case *ast.UnaryExpr:
+ switch e.Op {
+ case token.AND: // &X --- potentially escaping.
+ addr := b.addr(fn, e.X, true)
+ if _, ok := unparen(e.X).(*ast.StarExpr); ok {
+ // &*p must panic if p is nil (http://golang.org/s/go12nil).
+ // For simplicity, we'll just (suboptimally) rely
+ // on the side effects of a load.
+ // TODO(adonovan): emit dedicated nilcheck.
+ addr.load(fn)
+ }
+ return addr.address(fn)
+ case token.ADD:
+ return b.expr(fn, e.X)
+ case token.NOT, token.ARROW, token.SUB, token.XOR: // ! <- - ^
+ v := &UnOp{
+ Op: e.Op,
+ X: b.expr(fn, e.X),
+ }
+ v.setPos(e.OpPos)
+ v.setType(tv.Type)
+ return fn.emit(v)
+ default:
+ panic(e.Op)
+ }
+
+ case *ast.BinaryExpr:
+ switch e.Op {
+ case token.LAND, token.LOR:
+ return b.logicalBinop(fn, e)
+ case token.SHL, token.SHR:
+ fallthrough
+ case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+ return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e.OpPos)
+
+ case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
+ cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos)
+ // The type of x==y may be UntypedBool.
+ return emitConv(fn, cmp, DefaultType(tv.Type))
+ default:
+ panic("illegal op in BinaryExpr: " + e.Op.String())
+ }
+
+ case *ast.SliceExpr:
+ var low, high, max Value
+ var x Value
+ switch fn.Pkg.typeOf(e.X).Underlying().(type) {
+ case *types.Array:
+ // Potentially escaping.
+ x = b.addr(fn, e.X, true).address(fn)
+ case *types.Basic, *types.Slice, *types.Pointer: // *array
+ x = b.expr(fn, e.X)
+ default:
+ panic("unreachable")
+ }
+ if e.High != nil {
+ high = b.expr(fn, e.High)
+ }
+ if e.Low != nil {
+ low = b.expr(fn, e.Low)
+ }
+ if e.Slice3 {
+ max = b.expr(fn, e.Max)
+ }
+ v := &Slice{
+ X: x,
+ Low: low,
+ High: high,
+ Max: max,
+ }
+ v.setPos(e.Lbrack)
+ v.setType(tv.Type)
+ return fn.emit(v)
+
+ case *ast.Ident:
+ obj := fn.Pkg.info.Uses[e]
+ // Universal built-in or nil?
+ switch obj := obj.(type) {
+ case *types.Builtin:
+ return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
+ case *types.Nil:
+ return nilConst(tv.Type)
+ }
+ // Package-level func or var?
+ if v := fn.Prog.packageLevelValue(obj); v != nil {
+ if _, ok := obj.(*types.Var); ok {
+ return emitLoad(fn, v) // var (address)
+ }
+ return v // (func)
+ }
+ // Local var.
+ return emitLoad(fn, fn.lookup(obj, false)) // var (address)
+
+ case *ast.SelectorExpr:
+ sel, ok := fn.Pkg.info.Selections[e]
+ if !ok {
+ // qualified identifier
+ return b.expr(fn, e.Sel)
+ }
+ switch sel.Kind() {
+ case types.MethodExpr:
+ // (*T).f or T.f, the method f from the method-set of type T.
+ // The result is a "thunk".
+ return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type)
+
+ case types.MethodVal:
+ // e.f where e is an expression and f is a method.
+ // The result is a "bound".
+ obj := sel.Obj().(*types.Func)
+ rt := recvType(obj)
+ wantAddr := isPointer(rt)
+ escaping := true
+ v := b.receiver(fn, e.X, wantAddr, escaping, sel)
+ if isInterface(rt) {
+ // If v has interface type I,
+ // we must emit a check that v is non-nil.
+ // We use: typeassert v.(I).
+ emitTypeAssert(fn, v, rt, token.NoPos)
+ }
+ c := &MakeClosure{
+ Fn: makeBound(fn.Prog, obj),
+ Bindings: []Value{v},
+ }
+ c.setPos(e.Sel.Pos())
+ c.setType(tv.Type)
+ return fn.emit(c)
+
+ case types.FieldVal:
+ indices := sel.Index()
+ last := len(indices) - 1
+ v := b.expr(fn, e.X)
+ v = emitImplicitSelections(fn, v, indices[:last])
+ v = emitFieldSelection(fn, v, indices[last], false, e.Sel)
+ return v
+ }
+
+ panic("unexpected expression-relative selector")
+
+ case *ast.IndexExpr:
+ switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
+ case *types.Array:
+ // Non-addressable array (in a register).
+ v := &Index{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), tInt),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(t.Elem())
+ return fn.emit(v)
+
+ case *types.Map:
+ // Maps are not addressable.
+ mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
+ v := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(mapt.Elem())
+ return fn.emit(v)
+
+ case *types.Basic: // => string
+ // Strings are not addressable.
+ v := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: b.expr(fn, e.Index),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(tByte)
+ return fn.emit(v)
+
+ case *types.Slice, *types.Pointer: // *array
+ // Addressable slice/array; use IndexAddr and Load.
+ return b.addr(fn, e, false).load(fn)
+
+ default:
+ panic("unexpected container type in IndexExpr: " + t.String())
+ }
+
+ case *ast.CompositeLit, *ast.StarExpr:
+ // Addressable types (lvalues)
+ return b.addr(fn, e, false).load(fn)
+ }
+
+ panic(fmt.Sprintf("unexpected expr: %T", e))
+}
+
+// stmtList emits to fn code for all statements in list.
+func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
+ for _, s := range list {
+ b.stmt(fn, s)
+ }
+}
+
+// receiver emits to fn code for expression e in the "receiver"
+// position of selection e.f (where f may be a field or a method) and
+// returns the effective receiver after applying the implicit field
+// selections of sel.
+//
+// wantAddr requests that the result is an an address. If
+// !sel.Indirect(), this may require that e be built in addr() mode; it
+// must thus be addressable.
+//
+// escaping is defined as per builder.addr().
+//
+func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection) Value {
+ var v Value
+ if wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) {
+ v = b.addr(fn, e, escaping).address(fn)
+ } else {
+ v = b.expr(fn, e)
+ }
+
+ last := len(sel.Index()) - 1
+ v = emitImplicitSelections(fn, v, sel.Index()[:last])
+ if !wantAddr && isPointer(v.Type()) {
+ v = emitLoad(fn, v)
+ }
+ return v
+}
+
+// setCallFunc populates the function parts of a CallCommon structure
+// (Func, Method, Recv, Args[0]) based on the kind of invocation
+// occurring in e.
+//
+func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
+ c.pos = e.Lparen
+
+ // Is this a method call?
+ if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
+ sel, ok := fn.Pkg.info.Selections[selector]
+ if ok && sel.Kind() == types.MethodVal {
+ obj := sel.Obj().(*types.Func)
+ recv := recvType(obj)
+ wantAddr := isPointer(recv)
+ escaping := true
+ v := b.receiver(fn, selector.X, wantAddr, escaping, sel)
+ if isInterface(recv) {
+ // Invoke-mode call.
+ c.Value = v
+ c.Method = obj
+ } else {
+ // "Call"-mode call.
+ c.Value = fn.Prog.declaredFunc(obj)
+ c.Args = append(c.Args, v)
+ }
+ return
+ }
+
+ // sel.Kind()==MethodExpr indicates T.f() or (*T).f():
+ // a statically dispatched call to the method f in the
+ // method-set of T or *T. T may be an interface.
+ //
+ // e.Fun would evaluate to a concrete method, interface
+ // wrapper function, or promotion wrapper.
+ //
+ // For now, we evaluate it in the usual way.
+ //
+ // TODO(adonovan): opt: inline expr() here, to make the
+ // call static and to avoid generation of wrappers.
+ // It's somewhat tricky as it may consume the first
+ // actual parameter if the call is "invoke" mode.
+ //
+ // Examples:
+ // type T struct{}; func (T) f() {} // "call" mode
+ // type T interface { f() } // "invoke" mode
+ //
+ // type S struct{ T }
+ //
+ // var s S
+ // S.f(s)
+ // (*S).f(&s)
+ //
+ // Suggested approach:
+ // - consume the first actual parameter expression
+ // and build it with b.expr().
+ // - apply implicit field selections.
+ // - use MethodVal logic to populate fields of c.
+ }
+
+ // Evaluate the function operand in the usual way.
+ c.Value = b.expr(fn, e.Fun)
+}
+
+// emitCallArgs emits to f code for the actual parameters of call e to
+// a (possibly built-in) function of effective type sig.
+// The argument values are appended to args, which is then returned.
+//
+func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value {
+ // f(x, y, z...): pass slice z straight through.
+ if e.Ellipsis != 0 {
+ for i, arg := range e.Args {
+ v := emitConv(fn, b.expr(fn, arg), sig.Params().At(i).Type())
+ args = append(args, v)
+ }
+ return args
+ }
+
+ offset := len(args) // 1 if call has receiver, 0 otherwise
+
+ // Evaluate actual parameter expressions.
+ //
+ // If this is a chained call of the form f(g()) where g has
+ // multiple return values (MRV), they are flattened out into
+ // args; a suffix of them may end up in a varargs slice.
+ for _, arg := range e.Args {
+ v := b.expr(fn, arg)
+ if ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain
+ for i, n := 0, ttuple.Len(); i < n; i++ {
+ args = append(args, emitExtract(fn, v, i))
+ }
+ } else {
+ args = append(args, v)
+ }
+ }
+
+ // Actual->formal assignability conversions for normal parameters.
+ np := sig.Params().Len() // number of normal parameters
+ if sig.Variadic() {
+ np--
+ }
+ for i := 0; i < np; i++ {
+ args[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type())
+ }
+
+ // Actual->formal assignability conversions for variadic parameter,
+ // and construction of slice.
+ if sig.Variadic() {
+ varargs := args[offset+np:]
+ st := sig.Params().At(np).Type().(*types.Slice)
+ vt := st.Elem()
+ if len(varargs) == 0 {
+ args = append(args, nilConst(st))
+ } else {
+ // Replace a suffix of args with a slice containing it.
+ at := types.NewArray(vt, int64(len(varargs)))
+ a := emitNew(fn, at, token.NoPos)
+ a.setPos(e.Rparen)
+ a.Comment = "varargs"
+ for i, arg := range varargs {
+ iaddr := &IndexAddr{
+ X: a,
+ Index: intConst(int64(i)),
+ }
+ iaddr.setType(types.NewPointer(vt))
+ fn.emit(iaddr)
+ emitStore(fn, iaddr, arg, arg.Pos())
+ }
+ s := &Slice{X: a}
+ s.setType(st)
+ args[offset+np] = fn.emit(s)
+ args = args[:offset+np+1]
+ }
+ }
+ return args
+}
+
+// setCall emits to fn code to evaluate all the parameters of a function
+// call e, and populates *c with those values.
+//
+func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
+ // First deal with the f(...) part and optional receiver.
+ b.setCallFunc(fn, e, c)
+
+ // Then append the other actual parameters.
+ sig, _ := fn.Pkg.typeOf(e.Fun).Underlying().(*types.Signature)
+ if sig == nil {
+ panic(fmt.Sprintf("no signature for call of %s", e.Fun))
+ }
+ c.Args = b.emitCallArgs(fn, sig, e, c.Args)
+}
+
+// assignOp emits to fn code to perform loc += incr or loc -= incr.
+func (b *builder) assignOp(fn *Function, loc lvalue, incr Value, op token.Token) {
+ oldv := loc.load(fn)
+ loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ(), token.NoPos))
+}
+
+// localValueSpec emits to fn code to define all of the vars in the
+// function-local ValueSpec, spec.
+//
+func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
+ switch {
+ case len(spec.Values) == len(spec.Names):
+ // e.g. var x, y = 0, 1
+ // 1:1 assignment
+ for i, id := range spec.Names {
+ if !isBlankIdent(id) {
+ fn.addLocalForIdent(id)
+ }
+ lval := b.addr(fn, id, false) // non-escaping
+ b.assign(fn, lval, spec.Values[i], true, nil)
+ }
+
+ case len(spec.Values) == 0:
+ // e.g. var x, y int
+ // Locals are implicitly zero-initialized.
+ for _, id := range spec.Names {
+ if !isBlankIdent(id) {
+ lhs := fn.addLocalForIdent(id)
+ if fn.debugInfo() {
+ emitDebugRef(fn, id, lhs, true)
+ }
+ }
+ }
+
+ default:
+ // e.g. var x, y = pos()
+ tuple := b.exprN(fn, spec.Values[0])
+ for i, id := range spec.Names {
+ if !isBlankIdent(id) {
+ fn.addLocalForIdent(id)
+ lhs := b.addr(fn, id, false) // non-escaping
+ lhs.store(fn, emitExtract(fn, tuple, i))
+ }
+ }
+ }
+}
+
+// assignStmt emits code to fn for a parallel assignment of rhss to lhss.
+// isDef is true if this is a short variable declaration (:=).
+//
+// Note the similarity with localValueSpec.
+//
+func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
+ // Side effects of all LHSs and RHSs must occur in left-to-right order.
+ lvals := make([]lvalue, len(lhss))
+ isZero := make([]bool, len(lhss))
+ for i, lhs := range lhss {
+ var lval lvalue = blank{}
+ if !isBlankIdent(lhs) {
+ if isDef {
+ if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil {
+ fn.addNamedLocal(obj)
+ isZero[i] = true
+ }
+ }
+ lval = b.addr(fn, lhs, false) // non-escaping
+ }
+ lvals[i] = lval
+ }
+ if len(lhss) == len(rhss) {
+ // Simple assignment: x = f() (!isDef)
+ // Parallel assignment: x, y = f(), g() (!isDef)
+ // or short var decl: x, y := f(), g() (isDef)
+ //
+ // In all cases, the RHSs may refer to the LHSs,
+ // so we need a storebuf.
+ var sb storebuf
+ for i := range rhss {
+ b.assign(fn, lvals[i], rhss[i], isZero[i], &sb)
+ }
+ sb.emit(fn)
+ } else {
+ // e.g. x, y = pos()
+ tuple := b.exprN(fn, rhss[0])
+ emitDebugRef(fn, rhss[0], tuple, false)
+ for i, lval := range lvals {
+ lval.store(fn, emitExtract(fn, tuple, i))
+ }
+ }
+}
+
+// arrayLen returns the length of the array whose composite literal elements are elts.
+func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
+ var max int64 = -1
+ var i int64 = -1
+ for _, e := range elts {
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ i = b.expr(fn, kv.Key).(*Const).Int64()
+ } else {
+ i++
+ }
+ if i > max {
+ max = i
+ }
+ }
+ return max + 1
+}
+
+// compLit emits to fn code to initialize a composite literal e at
+// address addr with type typ.
+//
+// Nested composite literals are recursively initialized in place
+// where possible. If isZero is true, compLit assumes that addr
+// holds the zero value for typ.
+//
+// Because the elements of a composite literal may refer to the
+// variables being updated, as in the second line below,
+// x := T{a: 1}
+// x = T{a: x.a}
+// all the reads must occur before all the writes. Thus all stores to
+// loc are emitted to the storebuf sb for later execution.
+//
+// A CompositeLit may have pointer type only in the recursive (nested)
+// case when the type name is implicit. e.g. in []*T{{}}, the inner
+// literal has type *T behaves like &T{}.
+// In that case, addr must hold a T, not a *T.
+//
+func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
+ typ := deref(fn.Pkg.typeOf(e))
+ switch t := typ.Underlying().(type) {
+ case *types.Struct:
+ if !isZero && len(e.Elts) != t.NumFields() {
+ // memclear
+ sb.store(&address{addr, e.Lbrace, nil},
+ zeroValue(fn, deref(addr.Type())))
+ isZero = true
+ }
+ for i, e := range e.Elts {
+ fieldIndex := i
+ pos := e.Pos()
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ fname := kv.Key.(*ast.Ident).Name
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ sf := t.Field(i)
+ if sf.Name() == fname {
+ fieldIndex = i
+ pos = kv.Colon
+ e = kv.Value
+ break
+ }
+ }
+ }
+ sf := t.Field(fieldIndex)
+ faddr := &FieldAddr{
+ X: addr,
+ Field: fieldIndex,
+ }
+ faddr.setType(types.NewPointer(sf.Type()))
+ fn.emit(faddr)
+ b.assign(fn, &address{addr: faddr, pos: pos, expr: e}, e, isZero, sb)
+ }
+
+ case *types.Array, *types.Slice:
+ var at *types.Array
+ var array Value
+ switch t := t.(type) {
+ case *types.Slice:
+ at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts))
+ alloc := emitNew(fn, at, e.Lbrace)
+ alloc.Comment = "slicelit"
+ array = alloc
+ case *types.Array:
+ at = t
+ array = addr
+
+ if !isZero && int64(len(e.Elts)) != at.Len() {
+ // memclear
+ sb.store(&address{array, e.Lbrace, nil},
+ zeroValue(fn, deref(array.Type())))
+ }
+ }
+
+ var idx *Const
+ for _, e := range e.Elts {
+ pos := e.Pos()
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ idx = b.expr(fn, kv.Key).(*Const)
+ pos = kv.Colon
+ e = kv.Value
+ } else {
+ var idxval int64
+ if idx != nil {
+ idxval = idx.Int64() + 1
+ }
+ idx = intConst(idxval)
+ }
+ iaddr := &IndexAddr{
+ X: array,
+ Index: idx,
+ }
+ iaddr.setType(types.NewPointer(at.Elem()))
+ fn.emit(iaddr)
+ if t != at { // slice
+ // backing array is unaliased => storebuf not needed.
+ b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, nil)
+ } else {
+ b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, sb)
+ }
+ }
+
+ if t != at { // slice
+ s := &Slice{X: array}
+ s.setPos(e.Lbrace)
+ s.setType(typ)
+ sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, fn.emit(s))
+ }
+
+ case *types.Map:
+ m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
+ m.setPos(e.Lbrace)
+ m.setType(typ)
+ fn.emit(m)
+ for _, e := range e.Elts {
+ e := e.(*ast.KeyValueExpr)
+
+ // If a key expression in a map literal is itself a
+ // composite literal, the type may be omitted.
+ // For example:
+ // map[*struct{}]bool{{}: true}
+ // An &-operation may be implied:
+ // map[*struct{}]bool{&struct{}{}: true}
+ var key Value
+ if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) {
+ // A CompositeLit never evaluates to a pointer,
+ // so if the type of the location is a pointer,
+ // an &-operation is implied.
+ key = b.addr(fn, e.Key, true).address(fn)
+ } else {
+ key = b.expr(fn, e.Key)
+ }
+
+ loc := element{
+ m: m,
+ k: emitConv(fn, key, t.Key()),
+ t: t.Elem(),
+ pos: e.Colon,
+ }
+
+ // We call assign() only because it takes care
+ // of any &-operation required in the recursive
+ // case, e.g.,
+ // map[int]*struct{}{0: {}} implies &struct{}{}.
+ // In-place update is of course impossible,
+ // and no storebuf is needed.
+ b.assign(fn, &loc, e.Value, true, nil)
+ }
+ sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, m)
+
+ default:
+ panic("unexpected CompositeLit type: " + t.String())
+ }
+}
+
+// switchStmt emits to fn code for the switch statement s, optionally
+// labelled by label.
+//
+func (b *builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) {
+ // We treat SwitchStmt like a sequential if-else chain.
+ // Multiway dispatch can be recovered later by ssautil.Switches()
+ // to those cases that are free of side effects.
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ var tag Value = vTrue
+ if s.Tag != nil {
+ tag = b.expr(fn, s.Tag)
+ }
+ done := fn.newBasicBlock("switch.done")
+ if label != nil {
+ label._break = done
+ }
+ // We pull the default case (if present) down to the end.
+ // But each fallthrough label must point to the next
+ // body block in source order, so we preallocate a
+ // body block (fallthru) for the next case.
+ // Unfortunately this makes for a confusing block order.
+ var dfltBody *[]ast.Stmt
+ var dfltFallthrough *BasicBlock
+ var fallthru, dfltBlock *BasicBlock
+ ncases := len(s.Body.List)
+ for i, clause := range s.Body.List {
+ body := fallthru
+ if body == nil {
+ body = fn.newBasicBlock("switch.body") // first case only
+ }
+
+ // Preallocate body block for the next case.
+ fallthru = done
+ if i+1 < ncases {
+ fallthru = fn.newBasicBlock("switch.body")
+ }
+
+ cc := clause.(*ast.CaseClause)
+ if cc.List == nil {
+ // Default case.
+ dfltBody = &cc.Body
+ dfltFallthrough = fallthru
+ dfltBlock = body
+ continue
+ }
+
+ var nextCond *BasicBlock
+ for _, cond := range cc.List {
+ nextCond = fn.newBasicBlock("switch.next")
+ // TODO(adonovan): opt: when tag==vTrue, we'd
+ // get better code if we use b.cond(cond)
+ // instead of BinOp(EQL, tag, b.expr(cond))
+ // followed by If. Don't forget conversions
+ // though.
+ cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond), token.NoPos)
+ emitIf(fn, cond, body, nextCond)
+ fn.currentBlock = nextCond
+ }
+ fn.currentBlock = body
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _fallthrough: fallthru,
+ }
+ b.stmtList(fn, cc.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = nextCond
+ }
+ if dfltBlock != nil {
+ emitJump(fn, dfltBlock)
+ fn.currentBlock = dfltBlock
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _fallthrough: dfltFallthrough,
+ }
+ b.stmtList(fn, *dfltBody)
+ fn.targets = fn.targets.tail
+ }
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// typeSwitchStmt emits to fn code for the type switch statement s, optionally
+// labelled by label.
+//
+func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
+ // We treat TypeSwitchStmt like a sequential if-else chain.
+ // Multiway dispatch can be recovered later by ssautil.Switches().
+
+ // Typeswitch lowering:
+ //
+ // var x X
+ // switch y := x.(type) {
+ // case T1, T2: S1 // >1 (y := x)
+ // case nil: SN // nil (y := x)
+ // default: SD // 0 types (y := x)
+ // case T3: S3 // 1 type (y := x.(T3))
+ // }
+ //
+ // ...s.Init...
+ // x := eval x
+ // .caseT1:
+ // t1, ok1 := typeswitch,ok x <T1>
+ // if ok1 then goto S1 else goto .caseT2
+ // .caseT2:
+ // t2, ok2 := typeswitch,ok x <T2>
+ // if ok2 then goto S1 else goto .caseNil
+ // .S1:
+ // y := x
+ // ...S1...
+ // goto done
+ // .caseNil:
+ // if t2, ok2 := typeswitch,ok x <T2>
+ // if x == nil then goto SN else goto .caseT3
+ // .SN:
+ // y := x
+ // ...SN...
+ // goto done
+ // .caseT3:
+ // t3, ok3 := typeswitch,ok x <T3>
+ // if ok3 then goto S3 else goto default
+ // .S3:
+ // y := t3
+ // ...S3...
+ // goto done
+ // .default:
+ // y := x
+ // ...SD...
+ // goto done
+ // .done:
+
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+
+ var x Value
+ switch ass := s.Assign.(type) {
+ case *ast.ExprStmt: // x.(type)
+ x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
+ case *ast.AssignStmt: // y := x.(type)
+ x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
+ }
+
+ done := fn.newBasicBlock("typeswitch.done")
+ if label != nil {
+ label._break = done
+ }
+ var default_ *ast.CaseClause
+ for _, clause := range s.Body.List {
+ cc := clause.(*ast.CaseClause)
+ if cc.List == nil {
+ default_ = cc
+ continue
+ }
+ body := fn.newBasicBlock("typeswitch.body")
+ var next *BasicBlock
+ var casetype types.Type
+ var ti Value // ti, ok := typeassert,ok x <Ti>
+ for _, cond := range cc.List {
+ next = fn.newBasicBlock("typeswitch.next")
+ casetype = fn.Pkg.typeOf(cond)
+ var condv Value
+ if casetype == tUntypedNil {
+ condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
+ ti = x
+ } else {
+ yok := emitTypeTest(fn, x, casetype, cc.Case)
+ ti = emitExtract(fn, yok, 0)
+ condv = emitExtract(fn, yok, 1)
+ }
+ emitIf(fn, condv, body, next)
+ fn.currentBlock = next
+ }
+ if len(cc.List) != 1 {
+ ti = x
+ }
+ fn.currentBlock = body
+ b.typeCaseBody(fn, cc, ti, done)
+ fn.currentBlock = next
+ }
+ if default_ != nil {
+ b.typeCaseBody(fn, default_, x, done)
+ } else {
+ emitJump(fn, done)
+ }
+ fn.currentBlock = done
+}
+
+func (b *builder) typeCaseBody(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) {
+ if obj := fn.Pkg.info.Implicits[cc]; obj != nil {
+ // In a switch y := x.(type), each case clause
+ // implicitly declares a distinct object y.
+ // In a single-type case, y has that type.
+ // In multi-type cases, 'case nil' and default,
+ // y has the same type as the interface operand.
+ emitStore(fn, fn.addNamedLocal(obj), x, obj.Pos())
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, cc.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+}
+
+// selectStmt emits to fn code for the select statement s, optionally
+// labelled by label.
+//
+func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
+ // A blocking select of a single case degenerates to a
+ // simple send or receive.
+ // TODO(adonovan): opt: is this optimization worth its weight?
+ if len(s.Body.List) == 1 {
+ clause := s.Body.List[0].(*ast.CommClause)
+ if clause.Comm != nil {
+ b.stmt(fn, clause.Comm)
+ done := fn.newBasicBlock("select.done")
+ if label != nil {
+ label._break = done
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, clause.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = done
+ return
+ }
+ }
+
+ // First evaluate all channels in all cases, and find
+ // the directions of each state.
+ var states []*SelectState
+ blocking := true
+ debugInfo := fn.debugInfo()
+ for _, clause := range s.Body.List {
+ var st *SelectState
+ switch comm := clause.(*ast.CommClause).Comm.(type) {
+ case nil: // default case
+ blocking = false
+ continue
+
+ case *ast.SendStmt: // ch<- i
+ ch := b.expr(fn, comm.Chan)
+ st = &SelectState{
+ Dir: types.SendOnly,
+ Chan: ch,
+ Send: emitConv(fn, b.expr(fn, comm.Value),
+ ch.Type().Underlying().(*types.Chan).Elem()),
+ Pos: comm.Arrow,
+ }
+ if debugInfo {
+ st.DebugNode = comm
+ }
+
+ case *ast.AssignStmt: // x := <-ch
+ recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
+ st = &SelectState{
+ Dir: types.RecvOnly,
+ Chan: b.expr(fn, recv.X),
+ Pos: recv.OpPos,
+ }
+ if debugInfo {
+ st.DebugNode = recv
+ }
+
+ case *ast.ExprStmt: // <-ch
+ recv := unparen(comm.X).(*ast.UnaryExpr)
+ st = &SelectState{
+ Dir: types.RecvOnly,
+ Chan: b.expr(fn, recv.X),
+ Pos: recv.OpPos,
+ }
+ if debugInfo {
+ st.DebugNode = recv
+ }
+ }
+ states = append(states, st)
+ }
+
+ // We dispatch on the (fair) result of Select using a
+ // sequential if-else chain, in effect:
+ //
+ // idx, recvOk, r0...r_n-1 := select(...)
+ // if idx == 0 { // receive on channel 0 (first receive => r0)
+ // x, ok := r0, recvOk
+ // ...state0...
+ // } else if v == 1 { // send on channel 1
+ // ...state1...
+ // } else {
+ // ...default...
+ // }
+ sel := &Select{
+ States: states,
+ Blocking: blocking,
+ }
+ sel.setPos(s.Select)
+ var vars []*types.Var
+ vars = append(vars, varIndex, varOk)
+ for _, st := range states {
+ if st.Dir == types.RecvOnly {
+ tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
+ vars = append(vars, anonVar(tElem))
+ }
+ }
+ sel.setType(types.NewTuple(vars...))
+
+ fn.emit(sel)
+ idx := emitExtract(fn, sel, 0)
+
+ done := fn.newBasicBlock("select.done")
+ if label != nil {
+ label._break = done
+ }
+
+ var defaultBody *[]ast.Stmt
+ state := 0
+ r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
+ for _, cc := range s.Body.List {
+ clause := cc.(*ast.CommClause)
+ if clause.Comm == nil {
+ defaultBody = &clause.Body
+ continue
+ }
+ body := fn.newBasicBlock("select.body")
+ next := fn.newBasicBlock("select.next")
+ emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next)
+ fn.currentBlock = body
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ switch comm := clause.Comm.(type) {
+ case *ast.ExprStmt: // <-ch
+ if debugInfo {
+ v := emitExtract(fn, sel, r)
+ emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
+ }
+ r++
+
+ case *ast.AssignStmt: // x := <-states[state].Chan
+ if comm.Tok == token.DEFINE {
+ fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident))
+ }
+ x := b.addr(fn, comm.Lhs[0], false) // non-escaping
+ v := emitExtract(fn, sel, r)
+ if debugInfo {
+ emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
+ }
+ x.store(fn, v)
+
+ if len(comm.Lhs) == 2 { // x, ok := ...
+ if comm.Tok == token.DEFINE {
+ fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
+ }
+ ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
+ ok.store(fn, emitExtract(fn, sel, 1))
+ }
+ r++
+ }
+ b.stmtList(fn, clause.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = next
+ state++
+ }
+ if defaultBody != nil {
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, *defaultBody)
+ fn.targets = fn.targets.tail
+ } else {
+ // A blocking select must match some case.
+ // (This should really be a runtime.errorString, not a string.)
+ fn.emit(&Panic{
+ X: emitConv(fn, stringConst("blocking select matched no case"), tEface),
+ })
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ }
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// forStmt emits to fn code for the for statement s, optionally
+// labelled by label.
+//
+func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) {
+ // ...init...
+ // jump loop
+ // loop:
+ // if cond goto body else done
+ // body:
+ // ...body...
+ // jump post
+ // post: (target of continue)
+ // ...post...
+ // jump loop
+ // done: (target of break)
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ body := fn.newBasicBlock("for.body")
+ done := fn.newBasicBlock("for.done") // target of 'break'
+ loop := body // target of back-edge
+ if s.Cond != nil {
+ loop = fn.newBasicBlock("for.loop")
+ }
+ cont := loop // target of 'continue'
+ if s.Post != nil {
+ cont = fn.newBasicBlock("for.post")
+ }
+ if label != nil {
+ label._break = done
+ label._continue = cont
+ }
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+ if loop != body {
+ b.cond(fn, s.Cond, body, done)
+ fn.currentBlock = body
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _continue: cont,
+ }
+ b.stmt(fn, s.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, cont)
+
+ if s.Post != nil {
+ fn.currentBlock = cont
+ b.stmt(fn, s.Post)
+ emitJump(fn, loop) // back-edge
+ }
+ fn.currentBlock = done
+}
+
+// rangeIndexed emits to fn the header for an integer-indexed loop
+// over array, *array or slice value x.
+// The v result is defined only if tv is non-nil.
+// forPos is the position of the "for" token.
+//
+func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) {
+ //
+ // length = len(x)
+ // index = -1
+ // loop: (target of continue)
+ // index++
+ // if index < length goto body else done
+ // body:
+ // k = index
+ // v = x[index]
+ // ...body...
+ // jump loop
+ // done: (target of break)
+
+ // Determine number of iterations.
+ var length Value
+ if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok {
+ // For array or *array, the number of iterations is
+ // known statically thanks to the type. We avoid a
+ // data dependence upon x, permitting later dead-code
+ // elimination if x is pure, static unrolling, etc.
+ // Ranging over a nil *array may have >0 iterations.
+ // We still generate code for x, in case it has effects.
+ length = intConst(arr.Len())
+ } else {
+ // length = len(x).
+ var c Call
+ c.Call.Value = makeLen(x.Type())
+ c.Call.Args = []Value{x}
+ c.setType(tInt)
+ length = fn.emit(&c)
+ }
+
+ index := fn.addLocal(tInt, token.NoPos)
+ emitStore(fn, index, intConst(-1), pos)
+
+ loop = fn.newBasicBlock("rangeindex.loop")
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+
+ incr := &BinOp{
+ Op: token.ADD,
+ X: emitLoad(fn, index),
+ Y: vOne,
+ }
+ incr.setType(tInt)
+ emitStore(fn, index, fn.emit(incr), pos)
+
+ body := fn.newBasicBlock("rangeindex.body")
+ done = fn.newBasicBlock("rangeindex.done")
+ emitIf(fn, emitCompare(fn, token.LSS, incr, length, token.NoPos), body, done)
+ fn.currentBlock = body
+
+ k = emitLoad(fn, index)
+ if tv != nil {
+ switch t := x.Type().Underlying().(type) {
+ case *types.Array:
+ instr := &Index{
+ X: x,
+ Index: k,
+ }
+ instr.setType(t.Elem())
+ v = fn.emit(instr)
+
+ case *types.Pointer: // *array
+ instr := &IndexAddr{
+ X: x,
+ Index: k,
+ }
+ instr.setType(types.NewPointer(t.Elem().Underlying().(*types.Array).Elem()))
+ v = emitLoad(fn, fn.emit(instr))
+
+ case *types.Slice:
+ instr := &IndexAddr{
+ X: x,
+ Index: k,
+ }
+ instr.setType(types.NewPointer(t.Elem()))
+ v = emitLoad(fn, fn.emit(instr))
+
+ default:
+ panic("rangeIndexed x:" + t.String())
+ }
+ }
+ return
+}
+
+// rangeIter emits to fn the header for a loop using
+// Range/Next/Extract to iterate over map or string value x.
+// tk and tv are the types of the key/value results k and v, or nil
+// if the respective component is not wanted.
+//
+func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) {
+ //
+ // it = range x
+ // loop: (target of continue)
+ // okv = next it (ok, key, value)
+ // ok = extract okv #0
+ // if ok goto body else done
+ // body:
+ // k = extract okv #1
+ // v = extract okv #2
+ // ...body...
+ // jump loop
+ // done: (target of break)
+ //
+
+ if tk == nil {
+ tk = tInvalid
+ }
+ if tv == nil {
+ tv = tInvalid
+ }
+
+ rng := &Range{X: x}
+ rng.setPos(pos)
+ rng.setType(tRangeIter)
+ it := fn.emit(rng)
+
+ loop = fn.newBasicBlock("rangeiter.loop")
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+
+ _, isString := x.Type().Underlying().(*types.Basic)
+
+ okv := &Next{
+ Iter: it,
+ IsString: isString,
+ }
+ okv.setType(types.NewTuple(
+ varOk,
+ newVar("k", tk),
+ newVar("v", tv),
+ ))
+ fn.emit(okv)
+
+ body := fn.newBasicBlock("rangeiter.body")
+ done = fn.newBasicBlock("rangeiter.done")
+ emitIf(fn, emitExtract(fn, okv, 0), body, done)
+ fn.currentBlock = body
+
+ if tk != tInvalid {
+ k = emitExtract(fn, okv, 1)
+ }
+ if tv != tInvalid {
+ v = emitExtract(fn, okv, 2)
+ }
+ return
+}
+
+// rangeChan emits to fn the header for a loop that receives from
+// channel x until it fails.
+// tk is the channel's element type, or nil if the k result is
+// not wanted
+// pos is the position of the '=' or ':=' token.
+//
+func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, pos token.Pos) (k Value, loop, done *BasicBlock) {
+ //
+ // loop: (target of continue)
+ // ko = <-x (key, ok)
+ // ok = extract ko #1
+ // if ok goto body else done
+ // body:
+ // k = extract ko #0
+ // ...
+ // goto loop
+ // done: (target of break)
+
+ loop = fn.newBasicBlock("rangechan.loop")
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+ recv := &UnOp{
+ Op: token.ARROW,
+ X: x,
+ CommaOk: true,
+ }
+ recv.setPos(pos)
+ recv.setType(types.NewTuple(
+ newVar("k", x.Type().Underlying().(*types.Chan).Elem()),
+ varOk,
+ ))
+ ko := fn.emit(recv)
+ body := fn.newBasicBlock("rangechan.body")
+ done = fn.newBasicBlock("rangechan.done")
+ emitIf(fn, emitExtract(fn, ko, 1), body, done)
+ fn.currentBlock = body
+ if tk != nil {
+ k = emitExtract(fn, ko, 0)
+ }
+ return
+}
+
+// rangeStmt emits to fn code for the range statement s, optionally
+// labelled by label.
+//
+func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) {
+ var tk, tv types.Type
+ if s.Key != nil && !isBlankIdent(s.Key) {
+ tk = fn.Pkg.typeOf(s.Key)
+ }
+ if s.Value != nil && !isBlankIdent(s.Value) {
+ tv = fn.Pkg.typeOf(s.Value)
+ }
+
+ // If iteration variables are defined (:=), this
+ // occurs once outside the loop.
+ //
+ // Unlike a short variable declaration, a RangeStmt
+ // using := never redeclares an existing variable; it
+ // always creates a new one.
+ if s.Tok == token.DEFINE {
+ if tk != nil {
+ fn.addLocalForIdent(s.Key.(*ast.Ident))
+ }
+ if tv != nil {
+ fn.addLocalForIdent(s.Value.(*ast.Ident))
+ }
+ }
+
+ x := b.expr(fn, s.X)
+
+ var k, v Value
+ var loop, done *BasicBlock
+ switch rt := x.Type().Underlying().(type) {
+ case *types.Slice, *types.Array, *types.Pointer: // *array
+ k, v, loop, done = b.rangeIndexed(fn, x, tv, s.For)
+
+ case *types.Chan:
+ k, loop, done = b.rangeChan(fn, x, tk, s.For)
+
+ case *types.Map, *types.Basic: // string
+ k, v, loop, done = b.rangeIter(fn, x, tk, tv, s.For)
+
+ default:
+ panic("Cannot range over: " + rt.String())
+ }
+
+ // Evaluate both LHS expressions before we update either.
+ var kl, vl lvalue
+ if tk != nil {
+ kl = b.addr(fn, s.Key, false) // non-escaping
+ }
+ if tv != nil {
+ vl = b.addr(fn, s.Value, false) // non-escaping
+ }
+ if tk != nil {
+ kl.store(fn, k)
+ }
+ if tv != nil {
+ vl.store(fn, v)
+ }
+
+ if label != nil {
+ label._break = done
+ label._continue = loop
+ }
+
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _continue: loop,
+ }
+ b.stmt(fn, s.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, loop) // back-edge
+ fn.currentBlock = done
+}
+
+// stmt lowers statement s to SSA form, emitting code to fn.
+func (b *builder) stmt(fn *Function, _s ast.Stmt) {
+ // The label of the current statement. If non-nil, its _goto
+ // target is always set; its _break and _continue are set only
+ // within the body of switch/typeswitch/select/for/range.
+ // It is effectively an additional default-nil parameter of stmt().
+ var label *lblock
+start:
+ switch s := _s.(type) {
+ case *ast.EmptyStmt:
+ // ignore. (Usually removed by gofmt.)
+
+ case *ast.DeclStmt: // Con, Var or Typ
+ d := s.Decl.(*ast.GenDecl)
+ if d.Tok == token.VAR {
+ for _, spec := range d.Specs {
+ if vs, ok := spec.(*ast.ValueSpec); ok {
+ b.localValueSpec(fn, vs)
+ }
+ }
+ }
+
+ case *ast.LabeledStmt:
+ label = fn.labelledBlock(s.Label)
+ emitJump(fn, label._goto)
+ fn.currentBlock = label._goto
+ _s = s.Stmt
+ goto start // effectively: tailcall stmt(fn, s.Stmt, label)
+
+ case *ast.ExprStmt:
+ b.expr(fn, s.X)
+
+ case *ast.SendStmt:
+ fn.emit(&Send{
+ Chan: b.expr(fn, s.Chan),
+ X: emitConv(fn, b.expr(fn, s.Value),
+ fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()),
+ pos: s.Arrow,
+ })
+
+ case *ast.IncDecStmt:
+ op := token.ADD
+ if s.Tok == token.DEC {
+ op = token.SUB
+ }
+ loc := b.addr(fn, s.X, false)
+ b.assignOp(fn, loc, NewConst(exact.MakeInt64(1), loc.typ()), op)
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE)
+
+ default: // +=, etc.
+ op := s.Tok + token.ADD - token.ADD_ASSIGN
+ b.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op)
+ }
+
+ case *ast.GoStmt:
+ // The "intrinsics" new/make/len/cap are forbidden here.
+ // panic is treated like an ordinary function call.
+ v := Go{pos: s.Go}
+ b.setCall(fn, s.Call, &v.Call)
+ fn.emit(&v)
+
+ case *ast.DeferStmt:
+ // The "intrinsics" new/make/len/cap are forbidden here.
+ // panic is treated like an ordinary function call.
+ v := Defer{pos: s.Defer}
+ b.setCall(fn, s.Call, &v.Call)
+ fn.emit(&v)
+
+ // A deferred call can cause recovery from panic,
+ // and control resumes at the Recover block.
+ createRecoverBlock(fn)
+
+ case *ast.ReturnStmt:
+ var results []Value
+ if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 {
+ // Return of one expression in a multi-valued function.
+ tuple := b.exprN(fn, s.Results[0])
+ ttuple := tuple.Type().(*types.Tuple)
+ for i, n := 0, ttuple.Len(); i < n; i++ {
+ results = append(results,
+ emitConv(fn, emitExtract(fn, tuple, i),
+ fn.Signature.Results().At(i).Type()))
+ }
+ } else {
+ // 1:1 return, or no-arg return in non-void function.
+ for i, r := range s.Results {
+ v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type())
+ results = append(results, v)
+ }
+ }
+ if fn.namedResults != nil {
+ // Function has named result parameters (NRPs).
+ // Perform parallel assignment of return operands to NRPs.
+ for i, r := range results {
+ emitStore(fn, fn.namedResults[i], r, s.Return)
+ }
+ }
+ // Run function calls deferred in this
+ // function when explicitly returning from it.
+ fn.emit(new(RunDefers))
+ if fn.namedResults != nil {
+ // Reload NRPs to form the result tuple.
+ results = results[:0]
+ for _, r := range fn.namedResults {
+ results = append(results, emitLoad(fn, r))
+ }
+ }
+ fn.emit(&Return{Results: results, pos: s.Return})
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+
+ case *ast.BranchStmt:
+ var block *BasicBlock
+ switch s.Tok {
+ case token.BREAK:
+ if s.Label != nil {
+ block = fn.labelledBlock(s.Label)._break
+ } else {
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._break
+ }
+ }
+
+ case token.CONTINUE:
+ if s.Label != nil {
+ block = fn.labelledBlock(s.Label)._continue
+ } else {
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._continue
+ }
+ }
+
+ case token.FALLTHROUGH:
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._fallthrough
+ }
+
+ case token.GOTO:
+ block = fn.labelledBlock(s.Label)._goto
+ }
+ emitJump(fn, block)
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+
+ case *ast.BlockStmt:
+ b.stmtList(fn, s.List)
+
+ case *ast.IfStmt:
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ then := fn.newBasicBlock("if.then")
+ done := fn.newBasicBlock("if.done")
+ els := done
+ if s.Else != nil {
+ els = fn.newBasicBlock("if.else")
+ }
+ b.cond(fn, s.Cond, then, els)
+ fn.currentBlock = then
+ b.stmt(fn, s.Body)
+ emitJump(fn, done)
+
+ if s.Else != nil {
+ fn.currentBlock = els
+ b.stmt(fn, s.Else)
+ emitJump(fn, done)
+ }
+
+ fn.currentBlock = done
+
+ case *ast.SwitchStmt:
+ b.switchStmt(fn, s, label)
+
+ case *ast.TypeSwitchStmt:
+ b.typeSwitchStmt(fn, s, label)
+
+ case *ast.SelectStmt:
+ b.selectStmt(fn, s, label)
+
+ case *ast.ForStmt:
+ b.forStmt(fn, s, label)
+
+ case *ast.RangeStmt:
+ b.rangeStmt(fn, s, label)
+
+ default:
+ panic(fmt.Sprintf("unexpected statement kind: %T", s))
+ }
+}
+
+// buildFunction builds SSA code for the body of function fn. Idempotent.
+func (b *builder) buildFunction(fn *Function) {
+ if fn.Blocks != nil {
+ return // building already started
+ }
+
+ var recvField *ast.FieldList
+ var body *ast.BlockStmt
+ var functype *ast.FuncType
+ switch n := fn.syntax.(type) {
+ case nil:
+ return // not a Go source function. (Synthetic, or from object file.)
+ case *ast.FuncDecl:
+ functype = n.Type
+ recvField = n.Recv
+ body = n.Body
+ case *ast.FuncLit:
+ functype = n.Type
+ body = n.Body
+ default:
+ panic(n)
+ }
+
+ if body == nil {
+ // External function.
+ if fn.Params == nil {
+ // This condition ensures we add a non-empty
+ // params list once only, but we may attempt
+ // the degenerate empty case repeatedly.
+ // TODO(adonovan): opt: don't do that.
+
+ // We set Function.Params even though there is no body
+ // code to reference them. This simplifies clients.
+ if recv := fn.Signature.Recv(); recv != nil {
+ fn.addParamObj(recv)
+ }
+ params := fn.Signature.Params()
+ for i, n := 0, params.Len(); i < n; i++ {
+ fn.addParamObj(params.At(i))
+ }
+ }
+ return
+ }
+ if fn.Prog.mode&LogSource != 0 {
+ defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))()
+ }
+ fn.startBody()
+ fn.createSyntacticParams(recvField, functype)
+ b.stmt(fn, body)
+ if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) {
+ // Control fell off the end of the function's body block.
+ //
+ // Block optimizations eliminate the current block, if
+ // unreachable. It is a builder invariant that
+ // if this no-arg return is ill-typed for
+ // fn.Signature.Results, this block must be
+ // unreachable. The sanity checker checks this.
+ fn.emit(new(RunDefers))
+ fn.emit(new(Return))
+ }
+ fn.finishBody()
+}
+
+// buildFuncDecl builds SSA code for the function or method declared
+// by decl in package pkg.
+//
+func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
+ id := decl.Name
+ if isBlankIdent(id) {
+ return // discard
+ }
+ fn := pkg.values[pkg.info.Defs[id]].(*Function)
+ if decl.Recv == nil && id.Name == "init" {
+ var v Call
+ v.Call.Value = fn
+ v.setType(types.NewTuple())
+ pkg.init.emit(&v)
+ }
+ b.buildFunction(fn)
+}
+
+// BuildAll calls Package.Build() for each package in prog.
+// Building occurs in parallel unless the BuildSerially mode flag was set.
+//
+// BuildAll is intended for whole-program analysis; a typical compiler
+// need only build a single package.
+//
+// BuildAll is idempotent and thread-safe.
+//
+func (prog *Program) Build() {
+ var wg sync.WaitGroup
+ for _, p := range prog.packages {
+ if prog.mode&BuildSerially != 0 {
+ p.Build()
+ } else {
+ wg.Add(1)
+ go func(p *Package) {
+ p.Build()
+ wg.Done()
+ }(p)
+ }
+ }
+ wg.Wait()
+}
+
+// Build builds SSA code for all functions and vars in package p.
+//
+// Precondition: CreatePackage must have been called for all of p's
+// direct imports (and hence its direct imports must have been
+// error-free).
+//
+// Build is idempotent and thread-safe.
+//
+func (p *Package) Build() { p.buildOnce.Do(p.build) }
+
+func (p *Package) build() {
+ if p.info == nil {
+ return // synthetic package, e.g. "testmain"
+ }
+ if p.files == nil {
+ p.info = nil
+ return // package loaded from export data
+ }
+
+ // Ensure we have runtime type info for all exported members.
+ // TODO(adonovan): ideally belongs in memberFromObject, but
+ // that would require package creation in topological order.
+ for name, mem := range p.Members {
+ if ast.IsExported(name) {
+ p.Prog.needMethodsOf(mem.Type())
+ }
+ }
+ if p.Prog.mode&LogSource != 0 {
+ defer logStack("build %s", p)()
+ }
+ init := p.init
+ init.startBody()
+
+ var done *BasicBlock
+
+ if p.Prog.mode&BareInits == 0 {
+ // Make init() skip if package is already initialized.
+ initguard := p.Var("init$guard")
+ doinit := init.newBasicBlock("init.start")
+ done = init.newBasicBlock("init.done")
+ emitIf(init, emitLoad(init, initguard), done, doinit)
+ init.currentBlock = doinit
+ emitStore(init, initguard, vTrue, token.NoPos)
+
+ // Call the init() function of each package we import.
+ for _, pkg := range p.Pkg.Imports() {
+ prereq := p.Prog.packages[pkg]
+ if prereq == nil {
+ panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path()))
+ }
+ var v Call
+ v.Call.Value = prereq.init
+ v.Call.pos = init.pos
+ v.setType(types.NewTuple())
+ init.emit(&v)
+ }
+ }
+
+ var b builder
+
+ // Initialize package-level vars in correct order.
+ for _, varinit := range p.info.InitOrder {
+ if init.Prog.mode&LogSource != 0 {
+ fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n",
+ varinit.Lhs, p.Prog.Fset.Position(varinit.Rhs.Pos()))
+ }
+ if len(varinit.Lhs) == 1 {
+ // 1:1 initialization: var x, y = a(), b()
+ var lval lvalue
+ if v := varinit.Lhs[0]; v.Name() != "_" {
+ lval = &address{addr: p.values[v].(*Global), pos: v.Pos()}
+ } else {
+ lval = blank{}
+ }
+ b.assign(init, lval, varinit.Rhs, true, nil)
+ } else {
+ // n:1 initialization: var x, y := f()
+ tuple := b.exprN(init, varinit.Rhs)
+ for i, v := range varinit.Lhs {
+ if v.Name() == "_" {
+ continue
+ }
+ emitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i), v.Pos())
+ }
+ }
+ }
+
+ // Build all package-level functions, init functions
+ // and methods, including unreachable/blank ones.
+ // We build them in source order, but it's not significant.
+ for _, file := range p.files {
+ for _, decl := range file.Decls {
+ if decl, ok := decl.(*ast.FuncDecl); ok {
+ b.buildFuncDecl(p, decl)
+ }
+ }
+ }
+
+ // Finish up init().
+ if p.Prog.mode&BareInits == 0 {
+ emitJump(init, done)
+ init.currentBlock = done
+ }
+ init.emit(new(Return))
+ init.finishBody()
+
+ p.info = nil // We no longer need ASTs or go/types deductions.
+
+ if p.Prog.mode&SanityCheckFunctions != 0 {
+ sanityCheckPackage(p)
+ }
+}
+
+// Like ObjectOf, but panics instead of returning nil.
+// Only valid during p's create and build phases.
+func (p *Package) objectOf(id *ast.Ident) types.Object {
+ if o := p.info.ObjectOf(id); o != nil {
+ return o
+ }
+ panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
+ id.Name, p.Prog.Fset.Position(id.Pos())))
+}
+
+// Like TypeOf, but panics instead of returning nil.
+// Only valid during p's create and build phases.
+func (p *Package) typeOf(e ast.Expr) types.Type {
+ if T := p.info.TypeOf(e); T != nil {
+ return T
+ }
+ panic(fmt.Sprintf("no type for %T @ %s",
+ e, p.Prog.Fset.Position(e.Pos())))
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/builder14_test.go b/go/src/golang.org/x/tools/go/ssa/builder14_test.go
new file mode 100644
index 0000000..3eaa825
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/builder14_test.go
@@ -0,0 +1,421 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa_test
+
+import (
+ "bytes"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+
+ _ "golang.org/x/tools/go/gcimporter"
+)
+
+func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
+
+// Tests that programs partially loaded from gc object files contain
+// functions with no code for the external portions, but are otherwise ok.
+func TestBuildPackage(t *testing.T) {
+ input := `
+package main
+
+import (
+ "bytes"
+ "io"
+ "testing"
+)
+
+func main() {
+ var t testing.T
+ t.Parallel() // static call to external declared method
+ t.Fail() // static call to promoted external declared method
+ testing.Short() // static call to external package-level function
+
+ var w io.Writer = new(bytes.Buffer)
+ w.Write(nil) // interface invoke of external declared method
+}
+`
+
+ // Parse the file.
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "input.go", input, 0)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // Build an SSA program from the parsed file.
+ // Load its dependencies from gc binary export data.
+ mainPkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // The main package, its direct and indirect dependencies are loaded.
+ deps := []string{
+ // directly imported dependencies:
+ "bytes", "io", "testing",
+ // indirect dependencies (partial list):
+ "errors", "fmt", "os", "runtime",
+ }
+
+ prog := mainPkg.Prog
+ all := prog.AllPackages()
+ if len(all) <= len(deps) {
+ t.Errorf("unexpected set of loaded packages: %q", all)
+ }
+ for _, path := range deps {
+ pkg := prog.ImportedPackage(path)
+ if pkg == nil {
+ t.Errorf("package not loaded: %q", path)
+ continue
+ }
+
+ // External packages should have no function bodies (except for wrappers).
+ isExt := pkg != mainPkg
+
+ // init()
+ if isExt && !isEmpty(pkg.Func("init")) {
+ t.Errorf("external package %s has non-empty init", pkg)
+ } else if !isExt && isEmpty(pkg.Func("init")) {
+ t.Errorf("main package %s has empty init", pkg)
+ }
+
+ for _, mem := range pkg.Members {
+ switch mem := mem.(type) {
+ case *ssa.Function:
+ // Functions at package level.
+ if isExt && !isEmpty(mem) {
+ t.Errorf("external function %s is non-empty", mem)
+ } else if !isExt && isEmpty(mem) {
+ t.Errorf("function %s is empty", mem)
+ }
+
+ case *ssa.Type:
+ // Methods of named types T.
+ // (In this test, all exported methods belong to *T not T.)
+ if !isExt {
+ t.Fatalf("unexpected name type in main package: %s", mem)
+ }
+ mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
+ for i, n := 0, mset.Len(); i < n; i++ {
+ m := prog.MethodValue(mset.At(i))
+ // For external types, only synthetic wrappers have code.
+ expExt := !strings.Contains(m.Synthetic, "wrapper")
+ if expExt && !isEmpty(m) {
+ t.Errorf("external method %s is non-empty: %s",
+ m, m.Synthetic)
+ } else if !expExt && isEmpty(m) {
+ t.Errorf("method function %s is empty: %s",
+ m, m.Synthetic)
+ }
+ }
+ }
+ }
+ }
+
+ expectedCallee := []string{
+ "(*testing.T).Parallel",
+ "(*testing.common).Fail",
+ "testing.Short",
+ "N/A",
+ }
+ callNum := 0
+ for _, b := range mainPkg.Func("main").Blocks {
+ for _, instr := range b.Instrs {
+ switch instr := instr.(type) {
+ case ssa.CallInstruction:
+ call := instr.Common()
+ if want := expectedCallee[callNum]; want != "N/A" {
+ got := call.StaticCallee().String()
+ if want != got {
+ t.Errorf("call #%d from main.main: got callee %s, want %s",
+ callNum, got, want)
+ }
+ }
+ callNum++
+ }
+ }
+ }
+ if callNum != 4 {
+ t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
+ }
+}
+
+// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
+func TestRuntimeTypes(t *testing.T) {
+ tests := []struct {
+ input string
+ want []string
+ }{
+ // An exported package-level type is needed.
+ {`package A; type T struct{}; func (T) f() {}`,
+ []string{"*p.T", "p.T"},
+ },
+ // An unexported package-level type is not needed.
+ {`package B; type t struct{}; func (t) f() {}`,
+ nil,
+ },
+ // Subcomponents of type of exported package-level var are needed.
+ {`package C; import "bytes"; var V struct {*bytes.Buffer}`,
+ []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
+ },
+ // Subcomponents of type of unexported package-level var are not needed.
+ {`package D; import "bytes"; var v struct {*bytes.Buffer}`,
+ nil,
+ },
+ // Subcomponents of type of exported package-level function are needed.
+ {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
+ []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
+ },
+ // Subcomponents of type of unexported package-level function are not needed.
+ {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
+ nil,
+ },
+ // Subcomponents of type of exported method of uninstantiated unexported type are not needed.
+ {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
+ nil,
+ },
+ // ...unless used by MakeInterface.
+ {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
+ []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
+ },
+ // Subcomponents of type of unexported method are not needed.
+ {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
+ []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
+ },
+ // Local types aren't needed.
+ {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
+ nil,
+ },
+ // ...unless used by MakeInterface.
+ {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
+ []string{"*bytes.Buffer", "*p.T", "p.T"},
+ },
+ // Types used as operand of MakeInterface are needed.
+ {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
+ []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
+ },
+ // MakeInterface is optimized away when storing to a blank.
+ {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
+ nil,
+ },
+ }
+ for _, test := range tests {
+ // Parse the file.
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "input.go", test.input, 0)
+ if err != nil {
+ t.Errorf("test %q: %s", test.input[:15], err)
+ continue
+ }
+
+ // Create a single-file main package.
+ // Load dependencies from gc binary export data.
+ ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
+ if err != nil {
+ t.Errorf("test %q: %s", test.input[:15], err)
+ continue
+ }
+
+ var typstrs []string
+ for _, T := range ssapkg.Prog.RuntimeTypes() {
+ typstrs = append(typstrs, T.String())
+ }
+ sort.Strings(typstrs)
+
+ if !reflect.DeepEqual(typstrs, test.want) {
+ t.Errorf("test 'package %s': got %q, want %q",
+ f.Name.Name, typstrs, test.want)
+ }
+ }
+}
+
+// TestInit tests that synthesized init functions are correctly formed.
+// Bare init functions omit calls to dependent init functions and the use of
+// an init guard. They are useful in cases where the client uses a different
+// calling convention for init functions, or cases where it is easier for a
+// client to analyze bare init functions. Both of these aspects are used by
+// the llgo compiler for simpler integration with gccgo's runtime library,
+// and to simplify the analysis whereby it deduces which stores to globals
+// can be lowered to global initializers.
+func TestInit(t *testing.T) {
+ tests := []struct {
+ mode ssa.BuilderMode
+ input, want string
+ }{
+ {0, `package A; import _ "errors"; var i int = 42`,
+ `# Name: A.init
+# Package: A
+# Synthetic: package initializer
+func init():
+0: entry P:0 S:2
+ t0 = *init$guard bool
+ if t0 goto 2 else 1
+1: init.start P:1 S:1
+ *init$guard = true:bool
+ t1 = errors.init() ()
+ *i = 42:int
+ jump 2
+2: init.done P:2 S:0
+ return
+
+`},
+ {ssa.BareInits, `package B; import _ "errors"; var i int = 42`,
+ `# Name: B.init
+# Package: B
+# Synthetic: package initializer
+func init():
+0: entry P:0 S:0
+ *i = 42:int
+ return
+
+`},
+ }
+ for _, test := range tests {
+ // Create a single-file main package.
+ var conf loader.Config
+ f, err := conf.ParseFile("<input>", test.input)
+ if err != nil {
+ t.Errorf("test %q: %s", test.input[:15], err)
+ continue
+ }
+ conf.CreateFromFiles(f.Name.Name, f)
+
+ lprog, err := conf.Load()
+ if err != nil {
+ t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
+ continue
+ }
+ prog := ssautil.CreateProgram(lprog, test.mode)
+ mainPkg := prog.Package(lprog.Created[0].Pkg)
+ prog.Build()
+ initFunc := mainPkg.Func("init")
+ if initFunc == nil {
+ t.Errorf("test 'package %s': no init function", f.Name.Name)
+ continue
+ }
+
+ var initbuf bytes.Buffer
+ _, err = initFunc.WriteTo(&initbuf)
+ if err != nil {
+ t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
+ continue
+ }
+
+ if initbuf.String() != test.want {
+ t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
+ }
+ }
+}
+
+// TestSyntheticFuncs checks that the expected synthetic functions are
+// created, reachable, and not duplicated.
+func TestSyntheticFuncs(t *testing.T) {
+ const input = `package P
+type T int
+func (T) f() int
+func (*T) g() int
+var (
+ // thunks
+ a = T.f
+ b = T.f
+ c = (struct{T}).f
+ d = (struct{T}).f
+ e = (*T).g
+ f = (*T).g
+ g = (struct{*T}).g
+ h = (struct{*T}).g
+
+ // bounds
+ i = T(0).f
+ j = T(0).f
+ k = new(T).g
+ l = new(T).g
+
+ // wrappers
+ m interface{} = struct{T}{}
+ n interface{} = struct{T}{}
+ o interface{} = struct{*T}{}
+ p interface{} = struct{*T}{}
+ q interface{} = new(struct{T})
+ r interface{} = new(struct{T})
+ s interface{} = new(struct{*T})
+ t interface{} = new(struct{*T})
+)
+`
+ // Parse
+ var conf loader.Config
+ f, err := conf.ParseFile("<input>", input)
+ if err != nil {
+ t.Fatalf("parse: %v", err)
+ }
+ conf.CreateFromFiles(f.Name.Name, f)
+
+ // Load
+ lprog, err := conf.Load()
+ if err != nil {
+ t.Fatalf("Load: %v", err)
+ }
+
+ // Create and build SSA
+ prog := ssautil.CreateProgram(lprog, 0)
+ prog.Build()
+
+ // Enumerate reachable synthetic functions
+ want := map[string]string{
+ "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
+ "(P.T).f$bound": "bound method wrapper for func (P.T).f() int",
+
+ "(*P.T).g$thunk": "thunk for func (*P.T).g() int",
+ "(P.T).f$thunk": "thunk for func (P.T).f() int",
+ "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
+ "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int",
+
+ "(*P.T).f": "wrapper for func (P.T).f() int",
+ "(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
+ "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
+ "(*struct{P.T}).f": "wrapper for func (P.T).f() int",
+ "(*struct{P.T}).g": "wrapper for func (*P.T).g() int",
+ "(struct{*P.T}).f": "wrapper for func (P.T).f() int",
+ "(struct{*P.T}).g": "wrapper for func (*P.T).g() int",
+ "(struct{P.T}).f": "wrapper for func (P.T).f() int",
+
+ "P.init": "package initializer",
+ }
+ for fn := range ssautil.AllFunctions(prog) {
+ if fn.Synthetic == "" {
+ continue
+ }
+ name := fn.String()
+ wantDescr, ok := want[name]
+ if !ok {
+ t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
+ continue
+ }
+ delete(want, name)
+
+ if wantDescr != fn.Synthetic {
+ t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
+ }
+ }
+ for fn, descr := range want {
+ t.Errorf("want func: %q: %q", fn, descr)
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/builder_test.go b/go/src/golang.org/x/tools/go/ssa/builder_test.go
index e7ac838..fdb58ff 100644
--- a/go/src/golang.org/x/tools/go/ssa/builder_test.go
+++ b/go/src/golang.org/x/tools/go/ssa/builder_test.go
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa_test
import (
"bytes"
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
"reflect"
"sort"
"strings"
@@ -17,9 +21,6 @@
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
-
- _ "golang.org/x/tools/go/gcimporter"
)
func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
@@ -57,7 +58,7 @@
// Build an SSA program from the parsed file.
// Load its dependencies from gc binary export data.
- mainPkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
t.Error(err)
@@ -112,7 +113,7 @@
}
mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
- m := prog.Method(mset.At(i))
+ m := prog.MethodValue(mset.At(i))
// For external types, only synthetic wrappers have code.
expExt := !strings.Contains(m.Synthetic, "wrapper")
if expExt && !isEmpty(m) {
@@ -225,7 +226,7 @@
// Create a single-file main package.
// Load dependencies from gc binary export data.
- ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
t.Errorf("test %q: %s", test.input[:15], err)
@@ -303,7 +304,7 @@
}
prog := ssautil.CreateProgram(lprog, test.mode)
mainPkg := prog.Package(lprog.Created[0].Pkg)
- prog.BuildAll()
+ prog.Build()
initFunc := mainPkg.Func("init")
if initFunc == nil {
t.Errorf("test 'package %s': no init function", f.Name.Name)
@@ -374,7 +375,7 @@
// Create and build SSA
prog := ssautil.CreateProgram(lprog, 0)
- prog.BuildAll()
+ prog.Build()
// Enumerate reachable synthetic functions
want := map[string]string{
diff --git a/go/src/golang.org/x/tools/go/ssa/const.go b/go/src/golang.org/x/tools/go/ssa/const.go
index 304096e..0690463 100644
--- a/go/src/golang.org/x/tools/go/ssa/const.go
+++ b/go/src/golang.org/x/tools/go/ssa/const.go
@@ -2,17 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.6
+
package ssa
// This file defines the Const SSA value type.
import (
"fmt"
+ exact "go/constant"
"go/token"
+ "go/types"
"strconv"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
)
// NewConst returns a new constant of the specified value and type.
@@ -116,11 +117,13 @@
return c.Value == nil
}
+// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
+
// Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer.
//
func (c *Const) Int64() int64 {
- switch x := c.Value; x.Kind() {
+ switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int:
if i, ok := exact.Int64Val(x); ok {
return i
@@ -137,7 +140,7 @@
// an unsigned 64-bit integer.
//
func (c *Const) Uint64() uint64 {
- switch x := c.Value; x.Kind() {
+ switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int:
if u, ok := exact.Uint64Val(x); ok {
return u
diff --git a/go/src/golang.org/x/tools/go/ssa/const14.go b/go/src/golang.org/x/tools/go/ssa/const14.go
new file mode 100644
index 0000000..0ec43b6
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/const14.go
@@ -0,0 +1,170 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file defines the Const SSA value type.
+
+import (
+ "fmt"
+ "go/token"
+ "strconv"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+)
+
+// NewConst returns a new constant of the specified value and type.
+// val must be valid according to the specification of Const.Value.
+//
+func NewConst(val exact.Value, typ types.Type) *Const {
+ return &Const{typ, val}
+}
+
+// intConst returns an 'int' constant that evaluates to i.
+// (i is an int64 in case the host is narrower than the target.)
+func intConst(i int64) *Const {
+ return NewConst(exact.MakeInt64(i), tInt)
+}
+
+// nilConst returns a nil constant of the specified type, which may
+// be any reference type, including interfaces.
+//
+func nilConst(typ types.Type) *Const {
+ return NewConst(nil, typ)
+}
+
+// stringConst returns a 'string' constant that evaluates to s.
+func stringConst(s string) *Const {
+ return NewConst(exact.MakeString(s), tString)
+}
+
+// zeroConst returns a new "zero" constant of the specified type,
+// which must not be an array or struct type: the zero values of
+// aggregates are well-defined but cannot be represented by Const.
+//
+func zeroConst(t types.Type) *Const {
+ switch t := t.(type) {
+ case *types.Basic:
+ switch {
+ case t.Info()&types.IsBoolean != 0:
+ return NewConst(exact.MakeBool(false), t)
+ case t.Info()&types.IsNumeric != 0:
+ return NewConst(exact.MakeInt64(0), t)
+ case t.Info()&types.IsString != 0:
+ return NewConst(exact.MakeString(""), t)
+ case t.Kind() == types.UnsafePointer:
+ fallthrough
+ case t.Kind() == types.UntypedNil:
+ return nilConst(t)
+ default:
+ panic(fmt.Sprint("zeroConst for unexpected type:", t))
+ }
+ case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
+ return nilConst(t)
+ case *types.Named:
+ return NewConst(zeroConst(t.Underlying()).Value, t)
+ case *types.Array, *types.Struct, *types.Tuple:
+ panic(fmt.Sprint("zeroConst applied to aggregate:", t))
+ }
+ panic(fmt.Sprint("zeroConst: unexpected ", t))
+}
+
+func (c *Const) RelString(from *types.Package) string {
+ var s string
+ if c.Value == nil {
+ s = "nil"
+ } else if c.Value.Kind() == exact.String {
+ s = exact.StringVal(c.Value)
+ const max = 20
+ // TODO(adonovan): don't cut a rune in half.
+ if len(s) > max {
+ s = s[:max-3] + "..." // abbreviate
+ }
+ s = strconv.Quote(s)
+ } else {
+ s = c.Value.String()
+ }
+ return s + ":" + relType(c.Type(), from)
+}
+
+func (c *Const) Name() string {
+ return c.RelString(nil)
+}
+
+func (c *Const) String() string {
+ return c.Name()
+}
+
+func (c *Const) Type() types.Type {
+ return c.typ
+}
+
+func (c *Const) Referrers() *[]Instruction {
+ return nil
+}
+
+func (c *Const) Parent() *Function { return nil }
+
+func (c *Const) Pos() token.Pos {
+ return token.NoPos
+}
+
+// IsNil returns true if this constant represents a typed or untyped nil value.
+func (c *Const) IsNil() bool {
+ return c.Value == nil
+}
+
+// Int64 returns the numeric value of this constant truncated to fit
+// a signed 64-bit integer.
+//
+func (c *Const) Int64() int64 {
+ switch x := c.Value; x.Kind() {
+ case exact.Int:
+ if i, ok := exact.Int64Val(x); ok {
+ return i
+ }
+ return 0
+ case exact.Float:
+ f, _ := exact.Float64Val(x)
+ return int64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Uint64 returns the numeric value of this constant truncated to fit
+// an unsigned 64-bit integer.
+//
+func (c *Const) Uint64() uint64 {
+ switch x := c.Value; x.Kind() {
+ case exact.Int:
+ if u, ok := exact.Uint64Val(x); ok {
+ return u
+ }
+ return 0
+ case exact.Float:
+ f, _ := exact.Float64Val(x)
+ return uint64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Float64 returns the numeric value of this constant truncated to fit
+// a float64.
+//
+func (c *Const) Float64() float64 {
+ f, _ := exact.Float64Val(c.Value)
+ return f
+}
+
+// Complex128 returns the complex value of this constant truncated to
+// fit a complex128.
+//
+func (c *Const) Complex128() complex128 {
+ re, _ := exact.Float64Val(exact.Real(c.Value))
+ im, _ := exact.Float64Val(exact.Imag(c.Value))
+ return complex(re, im)
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/const15.go b/go/src/golang.org/x/tools/go/ssa/const15.go
new file mode 100644
index 0000000..a42b255
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/const15.go
@@ -0,0 +1,171 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5,!go1.6
+
+package ssa
+
+// This file defines the Const SSA value type.
+
+import (
+ "fmt"
+ exact "go/constant"
+ "go/token"
+ "go/types"
+ "strconv"
+)
+
+// NewConst returns a new constant of the specified value and type.
+// val must be valid according to the specification of Const.Value.
+//
+func NewConst(val exact.Value, typ types.Type) *Const {
+ return &Const{typ, val}
+}
+
+// intConst returns an 'int' constant that evaluates to i.
+// (i is an int64 in case the host is narrower than the target.)
+func intConst(i int64) *Const {
+ return NewConst(exact.MakeInt64(i), tInt)
+}
+
+// nilConst returns a nil constant of the specified type, which may
+// be any reference type, including interfaces.
+//
+func nilConst(typ types.Type) *Const {
+ return NewConst(nil, typ)
+}
+
+// stringConst returns a 'string' constant that evaluates to s.
+func stringConst(s string) *Const {
+ return NewConst(exact.MakeString(s), tString)
+}
+
+// zeroConst returns a new "zero" constant of the specified type,
+// which must not be an array or struct type: the zero values of
+// aggregates are well-defined but cannot be represented by Const.
+//
+func zeroConst(t types.Type) *Const {
+ switch t := t.(type) {
+ case *types.Basic:
+ switch {
+ case t.Info()&types.IsBoolean != 0:
+ return NewConst(exact.MakeBool(false), t)
+ case t.Info()&types.IsNumeric != 0:
+ return NewConst(exact.MakeInt64(0), t)
+ case t.Info()&types.IsString != 0:
+ return NewConst(exact.MakeString(""), t)
+ case t.Kind() == types.UnsafePointer:
+ fallthrough
+ case t.Kind() == types.UntypedNil:
+ return nilConst(t)
+ default:
+ panic(fmt.Sprint("zeroConst for unexpected type:", t))
+ }
+ case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
+ return nilConst(t)
+ case *types.Named:
+ return NewConst(zeroConst(t.Underlying()).Value, t)
+ case *types.Array, *types.Struct, *types.Tuple:
+ panic(fmt.Sprint("zeroConst applied to aggregate:", t))
+ }
+ panic(fmt.Sprint("zeroConst: unexpected ", t))
+}
+
+func (c *Const) RelString(from *types.Package) string {
+ var s string
+ if c.Value == nil {
+ s = "nil"
+ } else if c.Value.Kind() == exact.String {
+ s = exact.StringVal(c.Value)
+ const max = 20
+ // TODO(adonovan): don't cut a rune in half.
+ if len(s) > max {
+ s = s[:max-3] + "..." // abbreviate
+ }
+ s = strconv.Quote(s)
+ } else {
+ s = c.Value.String()
+ }
+ return s + ":" + relType(c.Type(), from)
+}
+
+func (c *Const) Name() string {
+ return c.RelString(nil)
+}
+
+func (c *Const) String() string {
+ return c.Name()
+}
+
+func (c *Const) Type() types.Type {
+ return c.typ
+}
+
+func (c *Const) Referrers() *[]Instruction {
+ return nil
+}
+
+func (c *Const) Parent() *Function { return nil }
+
+func (c *Const) Pos() token.Pos {
+ return token.NoPos
+}
+
+// IsNil returns true if this constant represents a typed or untyped nil value.
+func (c *Const) IsNil() bool {
+ return c.Value == nil
+}
+
+// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
+
+// Int64 returns the numeric value of this constant truncated to fit
+// a signed 64-bit integer.
+//
+func (c *Const) Int64() int64 {
+ switch x := c.Value; x.Kind() {
+ case exact.Int:
+ if i, ok := exact.Int64Val(x); ok {
+ return i
+ }
+ return 0
+ case exact.Float:
+ f, _ := exact.Float64Val(x)
+ return int64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Uint64 returns the numeric value of this constant truncated to fit
+// an unsigned 64-bit integer.
+//
+func (c *Const) Uint64() uint64 {
+ switch x := c.Value; x.Kind() {
+ case exact.Int:
+ if u, ok := exact.Uint64Val(x); ok {
+ return u
+ }
+ return 0
+ case exact.Float:
+ f, _ := exact.Float64Val(x)
+ return uint64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Float64 returns the numeric value of this constant truncated to fit
+// a float64.
+//
+func (c *Const) Float64() float64 {
+ f, _ := exact.Float64Val(c.Value)
+ return f
+}
+
+// Complex128 returns the complex value of this constant truncated to
+// fit a complex128.
+//
+func (c *Const) Complex128() complex128 {
+ re, _ := exact.Float64Val(exact.Real(c.Value))
+ im, _ := exact.Float64Val(exact.Imag(c.Value))
+ return complex(re, im)
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/create.go b/go/src/golang.org/x/tools/go/ssa/create.go
index 88226ae..372d1c7 100644
--- a/go/src/golang.org/x/tools/go/ssa/create.go
+++ b/go/src/golang.org/x/tools/go/ssa/create.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file implements the CREATE phase of SSA construction.
@@ -11,10 +13,10 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"os"
"sync"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -162,7 +164,7 @@
Prog: prog,
Members: make(map[string]Member),
values: make(map[types.Object]Value),
- Object: pkg,
+ Pkg: pkg,
info: info, // transient (CREATE and BUILD phases)
files: files, // transient (CREATE and BUILD phases)
}
@@ -190,7 +192,7 @@
// GC-compiled binary package.
// No code.
// No position information.
- scope := p.Object.Scope()
+ scope := p.Pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
memberFromObject(p, obj, nil)
@@ -224,9 +226,9 @@
}
if importable {
- prog.imported[p.Object.Path()] = p
+ prog.imported[p.Pkg.Path()] = p
}
- prog.packages[p.Object] = p
+ prog.packages[p.Pkg] = p
return p
}
diff --git a/go/src/golang.org/x/tools/go/ssa/create14.go b/go/src/golang.org/x/tools/go/ssa/create14.go
new file mode 100644
index 0000000..58be47e
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/create14.go
@@ -0,0 +1,259 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file implements the CREATE phase of SSA construction.
+// See builder.go for explanation.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "os"
+ "sync"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// NewProgram returns a new SSA Program.
+//
+// mode controls diagnostics and checking during SSA construction.
+//
+func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
+ prog := &Program{
+ Fset: fset,
+ imported: make(map[string]*Package),
+ packages: make(map[*types.Package]*Package),
+ thunks: make(map[selectionKey]*Function),
+ bounds: make(map[*types.Func]*Function),
+ mode: mode,
+ }
+
+ h := typeutil.MakeHasher() // protected by methodsMu, in effect
+ prog.methodSets.SetHasher(h)
+ prog.canon.SetHasher(h)
+
+ return prog
+}
+
+// memberFromObject populates package pkg with a member for the
+// typechecker object obj.
+//
+// For objects from Go source code, syntax is the associated syntax
+// tree (for funcs and vars only); it will be used during the build
+// phase.
+//
+func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
+ name := obj.Name()
+ switch obj := obj.(type) {
+ case *types.TypeName:
+ pkg.Members[name] = &Type{
+ object: obj,
+ pkg: pkg,
+ }
+
+ case *types.Const:
+ c := &NamedConst{
+ object: obj,
+ Value: NewConst(obj.Val(), obj.Type()),
+ pkg: pkg,
+ }
+ pkg.values[obj] = c.Value
+ pkg.Members[name] = c
+
+ case *types.Var:
+ g := &Global{
+ Pkg: pkg,
+ name: name,
+ object: obj,
+ typ: types.NewPointer(obj.Type()), // address
+ pos: obj.Pos(),
+ }
+ pkg.values[obj] = g
+ pkg.Members[name] = g
+
+ case *types.Func:
+ sig := obj.Type().(*types.Signature)
+ if sig.Recv() == nil && name == "init" {
+ pkg.ninit++
+ name = fmt.Sprintf("init#%d", pkg.ninit)
+ }
+ fn := &Function{
+ name: name,
+ object: obj,
+ Signature: sig,
+ syntax: syntax,
+ pos: obj.Pos(),
+ Pkg: pkg,
+ Prog: pkg.Prog,
+ }
+ if syntax == nil {
+ fn.Synthetic = "loaded from gc object file"
+ }
+
+ pkg.values[obj] = fn
+ if sig.Recv() == nil {
+ pkg.Members[name] = fn // package-level function
+ }
+
+ default: // (incl. *types.Package)
+ panic("unexpected Object type: " + obj.String())
+ }
+}
+
+// membersFromDecl populates package pkg with members for each
+// typechecker object (var, func, const or type) associated with the
+// specified decl.
+//
+func membersFromDecl(pkg *Package, decl ast.Decl) {
+ switch decl := decl.(type) {
+ case *ast.GenDecl: // import, const, type or var
+ switch decl.Tok {
+ case token.CONST:
+ for _, spec := range decl.Specs {
+ for _, id := range spec.(*ast.ValueSpec).Names {
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], nil)
+ }
+ }
+ }
+
+ case token.VAR:
+ for _, spec := range decl.Specs {
+ for _, id := range spec.(*ast.ValueSpec).Names {
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], spec)
+ }
+ }
+ }
+
+ case token.TYPE:
+ for _, spec := range decl.Specs {
+ id := spec.(*ast.TypeSpec).Name
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], nil)
+ }
+ }
+ }
+
+ case *ast.FuncDecl:
+ id := decl.Name
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], decl)
+ }
+ }
+}
+
+// CreatePackage constructs and returns an SSA Package from the
+// specified type-checked, error-free file ASTs, and populates its
+// Members mapping.
+//
+// importable determines whether this package should be returned by a
+// subsequent call to ImportedPackage(pkg.Path()).
+//
+// The real work of building SSA form for each function is not done
+// until a subsequent call to Package.Build().
+//
+func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
+ p := &Package{
+ Prog: prog,
+ Members: make(map[string]Member),
+ values: make(map[types.Object]Value),
+ Pkg: pkg,
+ info: info, // transient (CREATE and BUILD phases)
+ files: files, // transient (CREATE and BUILD phases)
+ }
+
+ // Add init() function.
+ p.init = &Function{
+ name: "init",
+ Signature: new(types.Signature),
+ Synthetic: "package initializer",
+ Pkg: p,
+ Prog: prog,
+ }
+ p.Members[p.init.name] = p.init
+
+ // CREATE phase.
+ // Allocate all package members: vars, funcs, consts and types.
+ if len(files) > 0 {
+ // Go source package.
+ for _, file := range files {
+ for _, decl := range file.Decls {
+ membersFromDecl(p, decl)
+ }
+ }
+ } else {
+ // GC-compiled binary package.
+ // No code.
+ // No position information.
+ scope := p.Pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ memberFromObject(p, obj, nil)
+ if obj, ok := obj.(*types.TypeName); ok {
+ named := obj.Type().(*types.Named)
+ for i, n := 0, named.NumMethods(); i < n; i++ {
+ memberFromObject(p, named.Method(i), nil)
+ }
+ }
+ }
+ }
+
+ if prog.mode&BareInits == 0 {
+ // Add initializer guard variable.
+ initguard := &Global{
+ Pkg: p,
+ name: "init$guard",
+ typ: types.NewPointer(tBool),
+ }
+ p.Members[initguard.Name()] = initguard
+ }
+
+ if prog.mode&GlobalDebug != 0 {
+ p.SetDebugMode(true)
+ }
+
+ if prog.mode&PrintPackages != 0 {
+ printMu.Lock()
+ p.WriteTo(os.Stdout)
+ printMu.Unlock()
+ }
+
+ if importable {
+ prog.imported[p.Pkg.Path()] = p
+ }
+ prog.packages[p.Pkg] = p
+
+ return p
+}
+
+// printMu serializes printing of Packages/Functions to stdout.
+var printMu sync.Mutex
+
+// AllPackages returns a new slice containing all packages in the
+// program prog in unspecified order.
+//
+func (prog *Program) AllPackages() []*Package {
+ pkgs := make([]*Package, 0, len(prog.packages))
+ for _, pkg := range prog.packages {
+ pkgs = append(pkgs, pkg)
+ }
+ return pkgs
+}
+
+// ImportedPackage returns the importable SSA Package whose import
+// path is path, or nil if no such SSA package has been created.
+//
+// Not all packages are importable. For example, no import
+// declaration can resolve to the x_test package created by 'go test'
+// or the ad-hoc main package created 'go build foo.go'.
+//
+func (prog *Program) ImportedPackage(path string) *Package {
+ return prog.imported[path]
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/emit.go b/go/src/golang.org/x/tools/go/ssa/emit.go
index fa9646b..238c070 100644
--- a/go/src/golang.org/x/tools/go/ssa/emit.go
+++ b/go/src/golang.org/x/tools/go/ssa/emit.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// Helpers for emitting SSA instructions.
@@ -10,8 +12,7 @@
"fmt"
"go/ast"
"go/token"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// emitNew emits to f a new (heap Alloc) instruction allocating an
diff --git a/go/src/golang.org/x/tools/go/ssa/emit14.go b/go/src/golang.org/x/tools/go/ssa/emit14.go
new file mode 100644
index 0000000..454aea0
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/emit14.go
@@ -0,0 +1,471 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// Helpers for emitting SSA instructions.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/types"
+)
+
+// emitNew emits to f a new (heap Alloc) instruction allocating an
+// object of type typ. pos is the optional source location.
+//
+func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc {
+ v := &Alloc{Heap: true}
+ v.setType(types.NewPointer(typ))
+ v.setPos(pos)
+ f.emit(v)
+ return v
+}
+
+// emitLoad emits to f an instruction to load the address addr into a
+// new temporary, and returns the value so defined.
+//
+func emitLoad(f *Function, addr Value) *UnOp {
+ v := &UnOp{Op: token.MUL, X: addr}
+ v.setType(deref(addr.Type()))
+ f.emit(v)
+ return v
+}
+
+// emitDebugRef emits to f a DebugRef pseudo-instruction associating
+// expression e with value v.
+//
+func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
+ if !f.debugInfo() {
+ return // debugging not enabled
+ }
+ if v == nil || e == nil {
+ panic("nil")
+ }
+ var obj types.Object
+ e = unparen(e)
+ if id, ok := e.(*ast.Ident); ok {
+ if isBlankIdent(id) {
+ return
+ }
+ obj = f.Pkg.objectOf(id)
+ switch obj.(type) {
+ case *types.Nil, *types.Const, *types.Builtin:
+ return
+ }
+ }
+ f.emit(&DebugRef{
+ X: v,
+ Expr: e,
+ IsAddr: isAddr,
+ object: obj,
+ })
+}
+
+// emitArith emits to f code to compute the binary operation op(x, y)
+// where op is an eager shift, logical or arithmetic operation.
+// (Use emitCompare() for comparisons and Builder.logicalBinop() for
+// non-eager operations.)
+//
+func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value {
+ switch op {
+ case token.SHL, token.SHR:
+ x = emitConv(f, x, t)
+ // y may be signed or an 'untyped' constant.
+ // TODO(adonovan): whence signed values?
+ if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
+ y = emitConv(f, y, types.Typ[types.Uint64])
+ }
+
+ case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+ x = emitConv(f, x, t)
+ y = emitConv(f, y, t)
+
+ default:
+ panic("illegal op in emitArith: " + op.String())
+
+ }
+ v := &BinOp{
+ Op: op,
+ X: x,
+ Y: y,
+ }
+ v.setPos(pos)
+ v.setType(t)
+ return f.emit(v)
+}
+
+// emitCompare emits to f code compute the boolean result of
+// comparison comparison 'x op y'.
+//
+func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
+ xt := x.Type().Underlying()
+ yt := y.Type().Underlying()
+
+ // Special case to optimise a tagless SwitchStmt so that
+ // these are equivalent
+ // switch { case e: ...}
+ // switch true { case e: ... }
+ // if e==true { ... }
+ // even in the case when e's type is an interface.
+ // TODO(adonovan): opt: generalise to x==true, false!=y, etc.
+ if x == vTrue && op == token.EQL {
+ if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
+ return y
+ }
+ }
+
+ if types.Identical(xt, yt) {
+ // no conversion necessary
+ } else if _, ok := xt.(*types.Interface); ok {
+ y = emitConv(f, y, x.Type())
+ } else if _, ok := yt.(*types.Interface); ok {
+ x = emitConv(f, x, y.Type())
+ } else if _, ok := x.(*Const); ok {
+ x = emitConv(f, x, y.Type())
+ } else if _, ok := y.(*Const); ok {
+ y = emitConv(f, y, x.Type())
+ } else {
+ // other cases, e.g. channels. No-op.
+ }
+
+ v := &BinOp{
+ Op: op,
+ X: x,
+ Y: y,
+ }
+ v.setPos(pos)
+ v.setType(tBool)
+ return f.emit(v)
+}
+
+// isValuePreserving returns true if a conversion from ut_src to
+// ut_dst is value-preserving, i.e. just a change of type.
+// Precondition: neither argument is a named type.
+//
+func isValuePreserving(ut_src, ut_dst types.Type) bool {
+ // Identical underlying types?
+ if types.Identical(ut_dst, ut_src) {
+ return true
+ }
+
+ switch ut_dst.(type) {
+ case *types.Chan:
+ // Conversion between channel types?
+ _, ok := ut_src.(*types.Chan)
+ return ok
+
+ case *types.Pointer:
+ // Conversion between pointers with identical base types?
+ _, ok := ut_src.(*types.Pointer)
+ return ok
+ }
+ return false
+}
+
+// emitConv emits to f code to convert Value val to exactly type typ,
+// and returns the converted value. Implicit conversions are required
+// by language assignability rules in assignments, parameter passing,
+// etc. Conversions cannot fail dynamically.
+//
+func emitConv(f *Function, val Value, typ types.Type) Value {
+ t_src := val.Type()
+
+ // Identical types? Conversion is a no-op.
+ if types.Identical(t_src, typ) {
+ return val
+ }
+
+ ut_dst := typ.Underlying()
+ ut_src := t_src.Underlying()
+
+ // Just a change of type, but not value or representation?
+ if isValuePreserving(ut_src, ut_dst) {
+ c := &ChangeType{X: val}
+ c.setType(typ)
+ return f.emit(c)
+ }
+
+ // Conversion to, or construction of a value of, an interface type?
+ if _, ok := ut_dst.(*types.Interface); ok {
+ // Assignment from one interface type to another?
+ if _, ok := ut_src.(*types.Interface); ok {
+ c := &ChangeInterface{X: val}
+ c.setType(typ)
+ return f.emit(c)
+ }
+
+ // Untyped nil constant? Return interface-typed nil constant.
+ if ut_src == tUntypedNil {
+ return nilConst(typ)
+ }
+
+ // Convert (non-nil) "untyped" literals to their default type.
+ if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
+ val = emitConv(f, val, DefaultType(ut_src))
+ }
+
+ f.Pkg.Prog.needMethodsOf(val.Type())
+ mi := &MakeInterface{X: val}
+ mi.setType(typ)
+ return f.emit(mi)
+ }
+
+ // Conversion of a compile-time constant value?
+ if c, ok := val.(*Const); ok {
+ if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() {
+ // Conversion of a compile-time constant to
+ // another constant type results in a new
+ // constant of the destination type and
+ // (initially) the same abstract value.
+ // We don't truncate the value yet.
+ return NewConst(c.Value, typ)
+ }
+
+ // We're converting from constant to non-constant type,
+ // e.g. string -> []byte/[]rune.
+ }
+
+ // A representation-changing conversion?
+ // At least one of {ut_src,ut_dst} must be *Basic.
+ // (The other may be []byte or []rune.)
+ _, ok1 := ut_src.(*types.Basic)
+ _, ok2 := ut_dst.(*types.Basic)
+ if ok1 || ok2 {
+ c := &Convert{X: val}
+ c.setType(typ)
+ return f.emit(c)
+ }
+
+ panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
+}
+
+// emitStore emits to f an instruction to store value val at location
+// addr, applying implicit conversions as required by assignability rules.
+//
+func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
+ s := &Store{
+ Addr: addr,
+ Val: emitConv(f, val, deref(addr.Type())),
+ pos: pos,
+ }
+ f.emit(s)
+ return s
+}
+
+// emitJump emits to f a jump to target, and updates the control-flow graph.
+// Postcondition: f.currentBlock is nil.
+//
+func emitJump(f *Function, target *BasicBlock) {
+ b := f.currentBlock
+ b.emit(new(Jump))
+ addEdge(b, target)
+ f.currentBlock = nil
+}
+
+// emitIf emits to f a conditional jump to tblock or fblock based on
+// cond, and updates the control-flow graph.
+// Postcondition: f.currentBlock is nil.
+//
+func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
+ b := f.currentBlock
+ b.emit(&If{Cond: cond})
+ addEdge(b, tblock)
+ addEdge(b, fblock)
+ f.currentBlock = nil
+}
+
+// emitExtract emits to f an instruction to extract the index'th
+// component of tuple. It returns the extracted value.
+//
+func emitExtract(f *Function, tuple Value, index int) Value {
+ e := &Extract{Tuple: tuple, Index: index}
+ e.setType(tuple.Type().(*types.Tuple).At(index).Type())
+ return f.emit(e)
+}
+
+// emitTypeAssert emits to f a type assertion value := x.(t) and
+// returns the value. x.Type() must be an interface.
+//
+func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
+ a := &TypeAssert{X: x, AssertedType: t}
+ a.setPos(pos)
+ a.setType(t)
+ return f.emit(a)
+}
+
+// emitTypeTest emits to f a type test value,ok := x.(t) and returns
+// a (value, ok) tuple. x.Type() must be an interface.
+//
+func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
+ a := &TypeAssert{
+ X: x,
+ AssertedType: t,
+ CommaOk: true,
+ }
+ a.setPos(pos)
+ a.setType(types.NewTuple(
+ newVar("value", t),
+ varOk,
+ ))
+ return f.emit(a)
+}
+
+// emitTailCall emits to f a function call in tail position. The
+// caller is responsible for all fields of 'call' except its type.
+// Intended for wrapper methods.
+// Precondition: f does/will not use deferred procedure calls.
+// Postcondition: f.currentBlock is nil.
+//
+func emitTailCall(f *Function, call *Call) {
+ tresults := f.Signature.Results()
+ nr := tresults.Len()
+ if nr == 1 {
+ call.typ = tresults.At(0).Type()
+ } else {
+ call.typ = tresults
+ }
+ tuple := f.emit(call)
+ var ret Return
+ switch nr {
+ case 0:
+ // no-op
+ case 1:
+ ret.Results = []Value{tuple}
+ default:
+ for i := 0; i < nr; i++ {
+ v := emitExtract(f, tuple, i)
+ // TODO(adonovan): in principle, this is required:
+ // v = emitConv(f, o.Type, f.Signature.Results[i].Type)
+ // but in practice emitTailCall is only used when
+ // the types exactly match.
+ ret.Results = append(ret.Results, v)
+ }
+ }
+ f.emit(&ret)
+ f.currentBlock = nil
+}
+
+// emitImplicitSelections emits to f code to apply the sequence of
+// implicit field selections specified by indices to base value v, and
+// returns the selected value.
+//
+// If v is the address of a struct, the result will be the address of
+// a field; if it is the value of a struct, the result will be the
+// value of a field.
+//
+func emitImplicitSelections(f *Function, v Value, indices []int) Value {
+ for _, index := range indices {
+ fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
+
+ if isPointer(v.Type()) {
+ instr := &FieldAddr{
+ X: v,
+ Field: index,
+ }
+ instr.setType(types.NewPointer(fld.Type()))
+ v = f.emit(instr)
+ // Load the field's value iff indirectly embedded.
+ if isPointer(fld.Type()) {
+ v = emitLoad(f, v)
+ }
+ } else {
+ instr := &Field{
+ X: v,
+ Field: index,
+ }
+ instr.setType(fld.Type())
+ v = f.emit(instr)
+ }
+ }
+ return v
+}
+
+// emitFieldSelection emits to f code to select the index'th field of v.
+//
+// If wantAddr, the input must be a pointer-to-struct and the result
+// will be the field's address; otherwise the result will be the
+// field's value.
+// Ident id is used for position and debug info.
+//
+func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
+ fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
+ if isPointer(v.Type()) {
+ instr := &FieldAddr{
+ X: v,
+ Field: index,
+ }
+ instr.setPos(id.Pos())
+ instr.setType(types.NewPointer(fld.Type()))
+ v = f.emit(instr)
+ // Load the field's value iff we don't want its address.
+ if !wantAddr {
+ v = emitLoad(f, v)
+ }
+ } else {
+ instr := &Field{
+ X: v,
+ Field: index,
+ }
+ instr.setPos(id.Pos())
+ instr.setType(fld.Type())
+ v = f.emit(instr)
+ }
+ emitDebugRef(f, id, v, wantAddr)
+ return v
+}
+
+// zeroValue emits to f code to produce a zero value of type t,
+// and returns it.
+//
+func zeroValue(f *Function, t types.Type) Value {
+ switch t.Underlying().(type) {
+ case *types.Struct, *types.Array:
+ return emitLoad(f, f.addLocal(t, token.NoPos))
+ default:
+ return zeroConst(t)
+ }
+}
+
+// createRecoverBlock emits to f a block of code to return after a
+// recovered panic, and sets f.Recover to it.
+//
+// If f's result parameters are named, the code loads and returns
+// their current values, otherwise it returns the zero values of their
+// type.
+//
+// Idempotent.
+//
+func createRecoverBlock(f *Function) {
+ if f.Recover != nil {
+ return // already created
+ }
+ saved := f.currentBlock
+
+ f.Recover = f.newBasicBlock("recover")
+ f.currentBlock = f.Recover
+
+ var results []Value
+ if f.namedResults != nil {
+ // Reload NRPs to form value tuple.
+ for _, r := range f.namedResults {
+ results = append(results, emitLoad(f, r))
+ }
+ } else {
+ R := f.Signature.Results()
+ for i, n := 0, R.Len(); i < n; i++ {
+ T := R.At(i).Type()
+
+ // Return zero value of each result type.
+ results = append(results, zeroValue(f, T))
+ }
+ }
+ f.emit(&Return{Results: results})
+
+ f.currentBlock = saved
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/example14_test.go b/go/src/golang.org/x/tools/go/ssa/example14_test.go
new file mode 100644
index 0000000..0171d4f
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/example14_test.go
@@ -0,0 +1,140 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa_test
+
+import (
+ "fmt"
+ "os"
+
+ "go/ast"
+ "go/parser"
+ "go/token"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+const hello = `
+package main
+
+import "fmt"
+
+const message = "Hello, World!"
+
+func main() {
+ fmt.Println(message)
+}
+`
+
+// This program demonstrates how to run the SSA builder on a single
+// package of one or more already-parsed files. Its dependencies are
+// loaded from compiler export data. This is what you'd typically use
+// for a compiler; it does not depend on golang.org/x/tools/go/loader.
+//
+// It shows the printed representation of packages, functions, and
+// instructions. Within the function listing, the name of each
+// BasicBlock such as ".0.entry" is printed left-aligned, followed by
+// the block's Instructions.
+//
+// For each instruction that defines an SSA virtual register
+// (i.e. implements Value), the type of that value is shown in the
+// right column.
+//
+// Build and run the ssadump.go program if you want a standalone tool
+// with similar functionality. It is located at
+// golang.org/x/tools/cmd/ssadump.
+//
+func ExampleBuildPackage() {
+ // Parse the source files.
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
+ if err != nil {
+ fmt.Print(err) // parse error
+ return
+ }
+ files := []*ast.File{f}
+
+ // Create the type-checker's package.
+ pkg := types.NewPackage("hello", "")
+
+ // Type-check the package, load dependencies.
+ // Create and build the SSA program.
+ hello, _, err := ssautil.BuildPackage(
+ new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions)
+ if err != nil {
+ fmt.Print(err) // type error in some package
+ return
+ }
+
+ // Print out the package.
+ hello.WriteTo(os.Stdout)
+
+ // Print out the package-level functions.
+ hello.Func("init").WriteTo(os.Stdout)
+ hello.Func("main").WriteTo(os.Stdout)
+
+ // Output:
+ //
+ // package hello:
+ // func init func()
+ // var init$guard bool
+ // func main func()
+ // const message message = "Hello, World!":untyped string
+ //
+ // # Name: hello.init
+ // # Package: hello
+ // # Synthetic: package initializer
+ // func init():
+ // 0: entry P:0 S:2
+ // t0 = *init$guard bool
+ // if t0 goto 2 else 1
+ // 1: init.start P:1 S:1
+ // *init$guard = true:bool
+ // t1 = fmt.init() ()
+ // jump 2
+ // 2: init.done P:2 S:0
+ // return
+ //
+ // # Name: hello.main
+ // # Package: hello
+ // # Location: hello.go:8:6
+ // func main():
+ // 0: entry P:0 S:0
+ // t0 = new [1]interface{} (varargs) *[1]interface{}
+ // t1 = &t0[0:int] *interface{}
+ // t2 = make interface{} <- string ("Hello, World!":string) interface{}
+ // *t1 = t2
+ // t3 = slice t0[:] []interface{}
+ // t4 = fmt.Println(t3...) (n int, err error)
+ // return
+}
+
+// This program shows how to load a main package (cmd/cover) and all its
+// dependencies from source, using the loader, and then build SSA code
+// for the entire program. This is what you'd typically use for a
+// whole-program analysis.
+//
+func ExampleLoadProgram() {
+ // Load cmd/cover and its dependencies.
+ var conf loader.Config
+ conf.Import("cmd/cover")
+ lprog, err := conf.Load()
+ if err != nil {
+ fmt.Print(err) // type error in some package
+ return
+ }
+
+ // Create SSA-form program representation.
+ prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions)
+
+ // Build SSA code for the entire cmd/cover program.
+ prog.Build()
+
+ // Output:
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/example_test.go b/go/src/golang.org/x/tools/go/ssa/example_test.go
index 3e095b8..718817c 100644
--- a/go/src/golang.org/x/tools/go/ssa/example_test.go
+++ b/go/src/golang.org/x/tools/go/ssa/example_test.go
@@ -2,20 +2,22 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa_test
import (
"fmt"
- "os"
-
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
+ "os"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
const hello = `
@@ -64,7 +66,7 @@
// Type-check the package, load dependencies.
// Create and build the SSA program.
hello, _, err := ssautil.BuildPackage(
- new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions)
+ &types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions)
if err != nil {
fmt.Print(err) // type error in some package
return
@@ -113,15 +115,15 @@
// return
}
-// This program shows how to load a main package (cmd/nm) and all its
+// This program shows how to load a main package (cmd/cover) and all its
// dependencies from source, using the loader, and then build SSA code
// for the entire program. This is what you'd typically use for a
// whole-program analysis.
//
func ExampleLoadProgram() {
- // Load cmd/nm and its dependencies.
+ // Load cmd/cover and its dependencies.
var conf loader.Config
- conf.Import("cmd/nm")
+ conf.Import("cmd/cover")
lprog, err := conf.Load()
if err != nil {
fmt.Print(err) // type error in some package
@@ -131,8 +133,8 @@
// Create SSA-form program representation.
prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions)
- // Build SSA code for the entire cmd/nm program.
- prog.BuildAll()
+ // Build SSA code for the entire cmd/cover program.
+ prog.Build()
// Output:
}
diff --git a/go/src/golang.org/x/tools/go/ssa/func.go b/go/src/golang.org/x/tools/go/ssa/func.go
index a9c0f75..88052c3 100644
--- a/go/src/golang.org/x/tools/go/ssa/func.go
+++ b/go/src/golang.org/x/tools/go/ssa/func.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file implements the Function and BasicBlock types.
@@ -11,11 +13,10 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"io"
"os"
"strings"
-
- "golang.org/x/tools/go/types"
)
// addEdge adds a control-flow graph edge from from to to.
@@ -501,7 +502,7 @@
// Package-level function?
// Prefix with package name for cross-package references only.
- if p := f.pkgobj(); p != nil && p != from {
+ if p := f.pkg(); p != nil && p != from {
return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
@@ -529,9 +530,9 @@
types.WriteSignature(buf, sig, types.RelativeTo(from))
}
-func (f *Function) pkgobj() *types.Package {
+func (f *Function) pkg() *types.Package {
if f.Pkg != nil {
- return f.Pkg.Object
+ return f.Pkg.Pkg
}
return nil
}
@@ -549,7 +550,7 @@
func WriteFunction(buf *bytes.Buffer, f *Function) {
fmt.Fprintf(buf, "# Name: %s\n", f.String())
if f.Pkg != nil {
- fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Object.Path())
+ fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
}
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(buf, "# Synthetic:", syn)
@@ -566,7 +567,7 @@
fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
}
- from := f.pkgobj()
+ from := f.pkg()
if f.FreeVars != nil {
buf.WriteString("# Free variables:\n")
diff --git a/go/src/golang.org/x/tools/go/ssa/func14.go b/go/src/golang.org/x/tools/go/ssa/func14.go
new file mode 100644
index 0000000..528f66e
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/func14.go
@@ -0,0 +1,692 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file implements the Function and BasicBlock types.
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "io"
+ "os"
+ "strings"
+
+ "golang.org/x/tools/go/types"
+)
+
+// addEdge adds a control-flow graph edge from from to to.
+func addEdge(from, to *BasicBlock) {
+ from.Succs = append(from.Succs, to)
+ to.Preds = append(to.Preds, from)
+}
+
+// Parent returns the function that contains block b.
+func (b *BasicBlock) Parent() *Function { return b.parent }
+
+// String returns a human-readable label of this block.
+// It is not guaranteed unique within the function.
+//
+func (b *BasicBlock) String() string {
+ return fmt.Sprintf("%d", b.Index)
+}
+
+// emit appends an instruction to the current basic block.
+// If the instruction defines a Value, it is returned.
+//
+func (b *BasicBlock) emit(i Instruction) Value {
+ i.setBlock(b)
+ b.Instrs = append(b.Instrs, i)
+ v, _ := i.(Value)
+ return v
+}
+
+// predIndex returns the i such that b.Preds[i] == c or panics if
+// there is none.
+func (b *BasicBlock) predIndex(c *BasicBlock) int {
+ for i, pred := range b.Preds {
+ if pred == c {
+ return i
+ }
+ }
+ panic(fmt.Sprintf("no edge %s -> %s", c, b))
+}
+
+// hasPhi returns true if b.Instrs contains φ-nodes.
+func (b *BasicBlock) hasPhi() bool {
+ _, ok := b.Instrs[0].(*Phi)
+ return ok
+}
+
+// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
+func (b *BasicBlock) phis() []Instruction {
+ for i, instr := range b.Instrs {
+ if _, ok := instr.(*Phi); !ok {
+ return b.Instrs[:i]
+ }
+ }
+ return nil // unreachable in well-formed blocks
+}
+
+// replacePred replaces all occurrences of p in b's predecessor list with q.
+// Ordinarily there should be at most one.
+//
+func (b *BasicBlock) replacePred(p, q *BasicBlock) {
+ for i, pred := range b.Preds {
+ if pred == p {
+ b.Preds[i] = q
+ }
+ }
+}
+
+// replaceSucc replaces all occurrences of p in b's successor list with q.
+// Ordinarily there should be at most one.
+//
+func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
+ for i, succ := range b.Succs {
+ if succ == p {
+ b.Succs[i] = q
+ }
+ }
+}
+
+// removePred removes all occurrences of p in b's
+// predecessor list and φ-nodes.
+// Ordinarily there should be at most one.
+//
+func (b *BasicBlock) removePred(p *BasicBlock) {
+ phis := b.phis()
+
+ // We must preserve edge order for φ-nodes.
+ j := 0
+ for i, pred := range b.Preds {
+ if pred != p {
+ b.Preds[j] = b.Preds[i]
+ // Strike out φ-edge too.
+ for _, instr := range phis {
+ phi := instr.(*Phi)
+ phi.Edges[j] = phi.Edges[i]
+ }
+ j++
+ }
+ }
+ // Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
+ for i := j; i < len(b.Preds); i++ {
+ b.Preds[i] = nil
+ for _, instr := range phis {
+ instr.(*Phi).Edges[i] = nil
+ }
+ }
+ b.Preds = b.Preds[:j]
+ for _, instr := range phis {
+ phi := instr.(*Phi)
+ phi.Edges = phi.Edges[:j]
+ }
+}
+
+// Destinations associated with unlabelled for/switch/select stmts.
+// We push/pop one of these as we enter/leave each construct and for
+// each BranchStmt we scan for the innermost target of the right type.
+//
+type targets struct {
+ tail *targets // rest of stack
+ _break *BasicBlock
+ _continue *BasicBlock
+ _fallthrough *BasicBlock
+}
+
+// Destinations associated with a labelled block.
+// We populate these as labels are encountered in forward gotos or
+// labelled statements.
+//
+type lblock struct {
+ _goto *BasicBlock
+ _break *BasicBlock
+ _continue *BasicBlock
+}
+
+// labelledBlock returns the branch target associated with the
+// specified label, creating it if needed.
+//
+func (f *Function) labelledBlock(label *ast.Ident) *lblock {
+ lb := f.lblocks[label.Obj]
+ if lb == nil {
+ lb = &lblock{_goto: f.newBasicBlock(label.Name)}
+ if f.lblocks == nil {
+ f.lblocks = make(map[*ast.Object]*lblock)
+ }
+ f.lblocks[label.Obj] = lb
+ }
+ return lb
+}
+
+// addParam adds a (non-escaping) parameter to f.Params of the
+// specified name, type and source position.
+//
+func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
+ v := &Parameter{
+ name: name,
+ typ: typ,
+ pos: pos,
+ parent: f,
+ }
+ f.Params = append(f.Params, v)
+ return v
+}
+
+func (f *Function) addParamObj(obj types.Object) *Parameter {
+ name := obj.Name()
+ if name == "" {
+ name = fmt.Sprintf("arg%d", len(f.Params))
+ }
+ param := f.addParam(name, obj.Type(), obj.Pos())
+ param.object = obj
+ return param
+}
+
+// addSpilledParam declares a parameter that is pre-spilled to the
+// stack; the function body will load/store the spilled location.
+// Subsequent lifting will eliminate spills where possible.
+//
+func (f *Function) addSpilledParam(obj types.Object) {
+ param := f.addParamObj(obj)
+ spill := &Alloc{Comment: obj.Name()}
+ spill.setType(types.NewPointer(obj.Type()))
+ spill.setPos(obj.Pos())
+ f.objects[obj] = spill
+ f.Locals = append(f.Locals, spill)
+ f.emit(spill)
+ f.emit(&Store{Addr: spill, Val: param})
+}
+
+// startBody initializes the function prior to generating SSA code for its body.
+// Precondition: f.Type() already set.
+//
+func (f *Function) startBody() {
+ f.currentBlock = f.newBasicBlock("entry")
+ f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init
+}
+
+// createSyntacticParams populates f.Params and generates code (spills
+// and named result locals) for all the parameters declared in the
+// syntax. In addition it populates the f.objects mapping.
+//
+// Preconditions:
+// f.startBody() was called.
+// Postcondition:
+// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
+//
+func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
+ // Receiver (at most one inner iteration).
+ if recv != nil {
+ for _, field := range recv.List {
+ for _, n := range field.Names {
+ f.addSpilledParam(f.Pkg.info.Defs[n])
+ }
+ // Anonymous receiver? No need to spill.
+ if field.Names == nil {
+ f.addParamObj(f.Signature.Recv())
+ }
+ }
+ }
+
+ // Parameters.
+ if functype.Params != nil {
+ n := len(f.Params) // 1 if has recv, 0 otherwise
+ for _, field := range functype.Params.List {
+ for _, n := range field.Names {
+ f.addSpilledParam(f.Pkg.info.Defs[n])
+ }
+ // Anonymous parameter? No need to spill.
+ if field.Names == nil {
+ f.addParamObj(f.Signature.Params().At(len(f.Params) - n))
+ }
+ }
+ }
+
+ // Named results.
+ if functype.Results != nil {
+ for _, field := range functype.Results.List {
+ // Implicit "var" decl of locals for named results.
+ for _, n := range field.Names {
+ f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
+ }
+ }
+ }
+}
+
+// numberRegisters assigns numbers to all SSA registers
+// (value-defining Instructions) in f, to aid debugging.
+// (Non-Instruction Values are named at construction.)
+//
+func numberRegisters(f *Function) {
+ v := 0
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ switch instr.(type) {
+ case Value:
+ instr.(interface {
+ setNum(int)
+ }).setNum(v)
+ v++
+ }
+ }
+ }
+}
+
+// buildReferrers populates the def/use information in all non-nil
+// Value.Referrers slice.
+// Precondition: all such slices are initially empty.
+func buildReferrers(f *Function) {
+ var rands []*Value
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ rands = instr.Operands(rands[:0]) // recycle storage
+ for _, rand := range rands {
+ if r := *rand; r != nil {
+ if ref := r.Referrers(); ref != nil {
+ *ref = append(*ref, instr)
+ }
+ }
+ }
+ }
+ }
+}
+
+// finishBody() finalizes the function after SSA code generation of its body.
+func (f *Function) finishBody() {
+ f.objects = nil
+ f.currentBlock = nil
+ f.lblocks = nil
+
+ // Don't pin the AST in memory (except in debug mode).
+ if n := f.syntax; n != nil && !f.debugInfo() {
+ f.syntax = extentNode{n.Pos(), n.End()}
+ }
+
+ // Remove from f.Locals any Allocs that escape to the heap.
+ j := 0
+ for _, l := range f.Locals {
+ if !l.Heap {
+ f.Locals[j] = l
+ j++
+ }
+ }
+ // Nil out f.Locals[j:] to aid GC.
+ for i := j; i < len(f.Locals); i++ {
+ f.Locals[i] = nil
+ }
+ f.Locals = f.Locals[:j]
+
+ optimizeBlocks(f)
+
+ buildReferrers(f)
+
+ buildDomTree(f)
+
+ if f.Prog.mode&NaiveForm == 0 {
+ // For debugging pre-state of lifting pass:
+ // numberRegisters(f)
+ // f.WriteTo(os.Stderr)
+ lift(f)
+ }
+
+ f.namedResults = nil // (used by lifting)
+
+ numberRegisters(f)
+
+ if f.Prog.mode&PrintFunctions != 0 {
+ printMu.Lock()
+ f.WriteTo(os.Stdout)
+ printMu.Unlock()
+ }
+
+ if f.Prog.mode&SanityCheckFunctions != 0 {
+ mustSanityCheck(f, nil)
+ }
+}
+
+// removeNilBlocks eliminates nils from f.Blocks and updates each
+// BasicBlock.Index. Use this after any pass that may delete blocks.
+//
+func (f *Function) removeNilBlocks() {
+ j := 0
+ for _, b := range f.Blocks {
+ if b != nil {
+ b.Index = j
+ f.Blocks[j] = b
+ j++
+ }
+ }
+ // Nil out f.Blocks[j:] to aid GC.
+ for i := j; i < len(f.Blocks); i++ {
+ f.Blocks[i] = nil
+ }
+ f.Blocks = f.Blocks[:j]
+}
+
+// SetDebugMode sets the debug mode for package pkg. If true, all its
+// functions will include full debug info. This greatly increases the
+// size of the instruction stream, and causes Functions to depend upon
+// the ASTs, potentially keeping them live in memory for longer.
+//
+func (pkg *Package) SetDebugMode(debug bool) {
+ // TODO(adonovan): do we want ast.File granularity?
+ pkg.debug = debug
+}
+
+// debugInfo reports whether debug info is wanted for this function.
+func (f *Function) debugInfo() bool {
+ return f.Pkg != nil && f.Pkg.debug
+}
+
+// addNamedLocal creates a local variable, adds it to function f and
+// returns it. Its name and type are taken from obj. Subsequent
+// calls to f.lookup(obj) will return the same local.
+//
+func (f *Function) addNamedLocal(obj types.Object) *Alloc {
+ l := f.addLocal(obj.Type(), obj.Pos())
+ l.Comment = obj.Name()
+ f.objects[obj] = l
+ return l
+}
+
+func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc {
+ return f.addNamedLocal(f.Pkg.info.Defs[id])
+}
+
+// addLocal creates an anonymous local variable of type typ, adds it
+// to function f and returns it. pos is the optional source location.
+//
+func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
+ v := &Alloc{}
+ v.setType(types.NewPointer(typ))
+ v.setPos(pos)
+ f.Locals = append(f.Locals, v)
+ f.emit(v)
+ return v
+}
+
+// lookup returns the address of the named variable identified by obj
+// that is local to function f or one of its enclosing functions.
+// If escaping, the reference comes from a potentially escaping pointer
+// expression and the referent must be heap-allocated.
+//
+func (f *Function) lookup(obj types.Object, escaping bool) Value {
+ if v, ok := f.objects[obj]; ok {
+ if alloc, ok := v.(*Alloc); ok && escaping {
+ alloc.Heap = true
+ }
+ return v // function-local var (address)
+ }
+
+ // Definition must be in an enclosing function;
+ // plumb it through intervening closures.
+ if f.parent == nil {
+ panic("no ssa.Value for " + obj.String())
+ }
+ outer := f.parent.lookup(obj, true) // escaping
+ v := &FreeVar{
+ name: obj.Name(),
+ typ: outer.Type(),
+ pos: outer.Pos(),
+ outer: outer,
+ parent: f,
+ }
+ f.objects[obj] = v
+ f.FreeVars = append(f.FreeVars, v)
+ return v
+}
+
+// emit emits the specified instruction to function f.
+func (f *Function) emit(instr Instruction) Value {
+ return f.currentBlock.emit(instr)
+}
+
+// RelString returns the full name of this function, qualified by
+// package name, receiver type, etc.
+//
+// The specific formatting rules are not guaranteed and may change.
+//
+// Examples:
+// "math.IsNaN" // a package-level function
+// "(*bytes.Buffer).Bytes" // a declared method or a wrapper
+// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
+// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
+// "main.main$1" // an anonymous function in main
+// "main.init#1" // a declared init function
+// "main.init" // the synthesized package initializer
+//
+// When these functions are referred to from within the same package
+// (i.e. from == f.Pkg.Object), they are rendered without the package path.
+// For example: "IsNaN", "(*Buffer).Bytes", etc.
+//
+// All non-synthetic functions have distinct package-qualified names.
+// (But two methods may have the same name "(T).f" if one is a synthetic
+// wrapper promoting a non-exported method "f" from another package; in
+// that case, the strings are equal but the identifiers "f" are distinct.)
+//
+func (f *Function) RelString(from *types.Package) string {
+ // Anonymous?
+ if f.parent != nil {
+ // An anonymous function's Name() looks like "parentName$1",
+ // but its String() should include the type/package/etc.
+ parent := f.parent.RelString(from)
+ for i, anon := range f.parent.AnonFuncs {
+ if anon == f {
+ return fmt.Sprintf("%s$%d", parent, 1+i)
+ }
+ }
+
+ return f.name // should never happen
+ }
+
+ // Method (declared or wrapper)?
+ if recv := f.Signature.Recv(); recv != nil {
+ return f.relMethod(from, recv.Type())
+ }
+
+ // Thunk?
+ if f.method != nil {
+ return f.relMethod(from, f.method.Recv())
+ }
+
+ // Bound?
+ if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
+ return f.relMethod(from, f.FreeVars[0].Type())
+ }
+
+ // Package-level function?
+ // Prefix with package name for cross-package references only.
+ if p := f.pkg(); p != nil && p != from {
+ return fmt.Sprintf("%s.%s", p.Path(), f.name)
+ }
+
+ // Unknown.
+ return f.name
+}
+
+func (f *Function) relMethod(from *types.Package, recv types.Type) string {
+ return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
+}
+
+// writeSignature writes to buf the signature sig in declaration syntax.
+func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) {
+ buf.WriteString("func ")
+ if recv := sig.Recv(); recv != nil {
+ buf.WriteString("(")
+ if n := params[0].Name(); n != "" {
+ buf.WriteString(n)
+ buf.WriteString(" ")
+ }
+ types.WriteType(buf, params[0].Type(), types.RelativeTo(from))
+ buf.WriteString(") ")
+ }
+ buf.WriteString(name)
+ types.WriteSignature(buf, sig, types.RelativeTo(from))
+}
+
+func (f *Function) pkg() *types.Package {
+ if f.Pkg != nil {
+ return f.Pkg.Pkg
+ }
+ return nil
+}
+
+var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
+
+func (f *Function) WriteTo(w io.Writer) (int64, error) {
+ var buf bytes.Buffer
+ WriteFunction(&buf, f)
+ n, err := w.Write(buf.Bytes())
+ return int64(n), err
+}
+
+// WriteFunction writes to buf a human-readable "disassembly" of f.
+func WriteFunction(buf *bytes.Buffer, f *Function) {
+ fmt.Fprintf(buf, "# Name: %s\n", f.String())
+ if f.Pkg != nil {
+ fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
+ }
+ if syn := f.Synthetic; syn != "" {
+ fmt.Fprintln(buf, "# Synthetic:", syn)
+ }
+ if pos := f.Pos(); pos.IsValid() {
+ fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
+ }
+
+ if f.parent != nil {
+ fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
+ }
+
+ if f.Recover != nil {
+ fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
+ }
+
+ from := f.pkg()
+
+ if f.FreeVars != nil {
+ buf.WriteString("# Free variables:\n")
+ for i, fv := range f.FreeVars {
+ fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
+ }
+ }
+
+ if len(f.Locals) > 0 {
+ buf.WriteString("# Locals:\n")
+ for i, l := range f.Locals {
+ fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from))
+ }
+ }
+ writeSignature(buf, from, f.Name(), f.Signature, f.Params)
+ buf.WriteString(":\n")
+
+ if f.Blocks == nil {
+ buf.WriteString("\t(external)\n")
+ }
+
+ // NB. column calculations are confused by non-ASCII
+ // characters and assume 8-space tabs.
+ const punchcard = 80 // for old time's sake.
+ const tabwidth = 8
+ for _, b := range f.Blocks {
+ if b == nil {
+ // Corrupt CFG.
+ fmt.Fprintf(buf, ".nil:\n")
+ continue
+ }
+ n, _ := fmt.Fprintf(buf, "%d:", b.Index)
+ bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
+ fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
+
+ if false { // CFG debugging
+ fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
+ }
+ for _, instr := range b.Instrs {
+ buf.WriteString("\t")
+ switch v := instr.(type) {
+ case Value:
+ l := punchcard - tabwidth
+ // Left-align the instruction.
+ if name := v.Name(); name != "" {
+ n, _ := fmt.Fprintf(buf, "%s = ", name)
+ l -= n
+ }
+ n, _ := buf.WriteString(instr.String())
+ l -= n
+ // Right-align the type if there's space.
+ if t := v.Type(); t != nil {
+ buf.WriteByte(' ')
+ ts := relType(t, from)
+ l -= len(ts) + len(" ") // (spaces before and after type)
+ if l > 0 {
+ fmt.Fprintf(buf, "%*s", l, "")
+ }
+ buf.WriteString(ts)
+ }
+ case nil:
+ // Be robust against bad transforms.
+ buf.WriteString("<deleted>")
+ default:
+ buf.WriteString(instr.String())
+ }
+ buf.WriteString("\n")
+ }
+ }
+ fmt.Fprintf(buf, "\n")
+}
+
+// newBasicBlock adds to f a new basic block and returns it. It does
+// not automatically become the current block for subsequent calls to emit.
+// comment is an optional string for more readable debugging output.
+//
+func (f *Function) newBasicBlock(comment string) *BasicBlock {
+ b := &BasicBlock{
+ Index: len(f.Blocks),
+ Comment: comment,
+ parent: f,
+ }
+ b.Succs = b.succs2[:0]
+ f.Blocks = append(f.Blocks, b)
+ return b
+}
+
+// NewFunction returns a new synthetic Function instance belonging to
+// prog, with its name and signature fields set as specified.
+//
+// The caller is responsible for initializing the remaining fields of
+// the function object, e.g. Pkg, Params, Blocks.
+//
+// It is practically impossible for clients to construct well-formed
+// SSA functions/packages/programs directly, so we assume this is the
+// job of the Builder alone. NewFunction exists to provide clients a
+// little flexibility. For example, analysis tools may wish to
+// construct fake Functions for the root of the callgraph, a fake
+// "reflect" package, etc.
+//
+// TODO(adonovan): think harder about the API here.
+//
+func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
+ return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
+}
+
+type extentNode [2]token.Pos
+
+func (n extentNode) Pos() token.Pos { return n[0] }
+func (n extentNode) End() token.Pos { return n[1] }
+
+// Syntax returns an ast.Node whose Pos/End methods provide the
+// lexical extent of the function if it was defined by Go source code
+// (f.Synthetic==""), or nil otherwise.
+//
+// If f was built with debug information (see Package.SetDebugRef),
+// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
+// function. Otherwise, it is an opaque Node providing only position
+// information; this avoids pinning the AST in memory.
+//
+func (f *Function) Syntax() ast.Node { return f.syntax }
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/external.go b/go/src/golang.org/x/tools/go/ssa/interp/external.go
index eb0757f..8ae81b2 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/external.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/external.go
@@ -2,21 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package interp
// Emulated functions that we cannot interpret because they are
// external or because they use "unsafe" or "reflect" operations.
import (
+ "go/types"
"math"
"os"
"runtime"
+ "strings"
"syscall"
"time"
"unsafe"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
type externalFn func(fr *frame, args []value) value
@@ -80,6 +83,8 @@
"math.Ldexp": extÛ°mathÛ°Ldexp,
"math.Log": extÛ°mathÛ°Log,
"math.Min": extÛ°mathÛ°Min,
+ "math.hasSSE4": extÛ°mathÛ°hasSSE4,
+ "os.Pipe": extÛ°osÛ°Pipe,
"os.runtime_args": extÛ°osÛ°runtime_args,
"os.runtime_beforeExit": extÛ°osÛ°runtime_beforeExit,
"reflect.New": extÛ°reflectÛ°New,
@@ -106,6 +111,7 @@
"(*runtime.Func).Name": extÛ°runtimeÛ°FuncÛ°Name,
"runtime.environ": extÛ°runtimeÛ°environ,
"runtime.getgoroot": extÛ°runtimeÛ°getgoroot,
+ "strings.Index": extÛ°stringsÛ°Index,
"strings.IndexByte": extÛ°stringsÛ°IndexByte,
"sync.runtime_Semacquire": extÛ°syncÛ°runtime_Semacquire,
"sync.runtime_Semrelease": extÛ°syncÛ°runtime_Semrelease,
@@ -134,6 +140,7 @@
"syscall.Stat": extÛ°syscallÛ°Stat,
"syscall.Write": extÛ°syscallÛ°Write,
"syscall.runtime_envs": extÛ°runtimeÛ°environ,
+ "testing.runExample": extÛ°testingÛ°runExample,
"time.Sleep": extÛ°timeÛ°Sleep,
"time.now": extÛ°timeÛ°now,
}
@@ -220,6 +227,10 @@
return math.Min(args[0].(float64), args[1].(float64))
}
+func extÛ°mathÛ°hasSSE4(fr *frame, args []value) value {
+ return false
+}
+
func extÛ°mathÛ°Ldexp(fr *frame, args []value) value {
return math.Ldexp(args[0].(float64), args[1].(int))
}
@@ -256,6 +267,7 @@
if fr != nil {
fn := fr.fn
// TODO(adonovan): use pc/posn of current instruction, not start of fn.
+ // (Required to interpret the log package's tests.)
pc = uintptr(unsafe.Pointer(fn))
posn := fn.Prog.Fset.Position(fn.Pos())
file = posn.Filename
@@ -316,6 +328,11 @@
return -1
}
+func extÛ°stringsÛ°Index(fr *frame, args []value) value {
+ // Call compiled version to avoid tricky asm dependency.
+ return strings.Index(args[0].(string), args[1].(string))
+}
+
func extÛ°syncÛ°runtime_Syncsemcheck(fr *frame, args []value) value {
// TODO(adonovan): fix: implement.
return nil
@@ -460,6 +477,22 @@
return uintptr(unsafe.Pointer(f))
}
+// This is a workaround for a bug in go/ssa/testmain.go: it creates
+// InternalExamples even for Example functions with no Output comment.
+// TODO(adonovan): fix (and redesign) testmain.go after Go 1.6.
+func extÛ°testingÛ°runExample(fr *frame, args []value) value {
+ // This is a stripped down runExample that simply calls the function.
+ // It does not capture and compare output nor recover from panic.
+ //
+ // func runExample(eg testing.InternalExample) bool {
+ // eg.F()
+ // return true
+ // }
+ F := args[0].(structure)[1]
+ call(fr.i, fr, 0, F, nil)
+ return true
+}
+
func extÛ°timeÛ°now(fr *frame, args []value) value {
nano := time.Now().UnixNano()
return tuple{int64(nano / 1e9), int32(nano % 1e9)}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/external14.go b/go/src/golang.org/x/tools/go/ssa/interp/external14.go
new file mode 100644
index 0000000..c07c562
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/external14.go
@@ -0,0 +1,526 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package interp
+
+// Emulated functions that we cannot interpret because they are
+// external or because they use "unsafe" or "reflect" operations.
+
+import (
+ "math"
+ "os"
+ "runtime"
+ "strings"
+ "syscall"
+ "time"
+ "unsafe"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+type externalFn func(fr *frame, args []value) value
+
+// TODO(adonovan): fix: reflect.Value abstracts an lvalue or an
+// rvalue; Set() causes mutations that can be observed via aliases.
+// We have not captured that correctly here.
+
+// Key strings are from Function.String().
+var externals map[string]externalFn
+
+func init() {
+ // That little dot Û° is an Arabic zero numeral (U+06F0), categories [Nd].
+ externals = map[string]externalFn{
+ "(*sync.Pool).Get": extÛ°syncÛ°PoolÛ°Get,
+ "(*sync.Pool).Put": extÛ°syncÛ°PoolÛ°Put,
+ "(reflect.Value).Bool": extÛ°reflectÛ°ValueÛ°Bool,
+ "(reflect.Value).CanAddr": extÛ°reflectÛ°ValueÛ°CanAddr,
+ "(reflect.Value).CanInterface": extÛ°reflectÛ°ValueÛ°CanInterface,
+ "(reflect.Value).Elem": extÛ°reflectÛ°ValueÛ°Elem,
+ "(reflect.Value).Field": extÛ°reflectÛ°ValueÛ°Field,
+ "(reflect.Value).Float": extÛ°reflectÛ°ValueÛ°Float,
+ "(reflect.Value).Index": extÛ°reflectÛ°ValueÛ°Index,
+ "(reflect.Value).Int": extÛ°reflectÛ°ValueÛ°Int,
+ "(reflect.Value).Interface": extÛ°reflectÛ°ValueÛ°Interface,
+ "(reflect.Value).IsNil": extÛ°reflectÛ°ValueÛ°IsNil,
+ "(reflect.Value).IsValid": extÛ°reflectÛ°ValueÛ°IsValid,
+ "(reflect.Value).Kind": extÛ°reflectÛ°ValueÛ°Kind,
+ "(reflect.Value).Len": extÛ°reflectÛ°ValueÛ°Len,
+ "(reflect.Value).MapIndex": extÛ°reflectÛ°ValueÛ°MapIndex,
+ "(reflect.Value).MapKeys": extÛ°reflectÛ°ValueÛ°MapKeys,
+ "(reflect.Value).NumField": extÛ°reflectÛ°ValueÛ°NumField,
+ "(reflect.Value).NumMethod": extÛ°reflectÛ°ValueÛ°NumMethod,
+ "(reflect.Value).Pointer": extÛ°reflectÛ°ValueÛ°Pointer,
+ "(reflect.Value).Set": extÛ°reflectÛ°ValueÛ°Set,
+ "(reflect.Value).String": extÛ°reflectÛ°ValueÛ°String,
+ "(reflect.Value).Type": extÛ°reflectÛ°ValueÛ°Type,
+ "(reflect.Value).Uint": extÛ°reflectÛ°ValueÛ°Uint,
+ "(reflect.error).Error": extÛ°reflectÛ°errorÛ°Error,
+ "(reflect.rtype).Bits": extÛ°reflectÛ°rtypeÛ°Bits,
+ "(reflect.rtype).Elem": extÛ°reflectÛ°rtypeÛ°Elem,
+ "(reflect.rtype).Field": extÛ°reflectÛ°rtypeÛ°Field,
+ "(reflect.rtype).In": extÛ°reflectÛ°rtypeÛ°In,
+ "(reflect.rtype).Kind": extÛ°reflectÛ°rtypeÛ°Kind,
+ "(reflect.rtype).NumField": extÛ°reflectÛ°rtypeÛ°NumField,
+ "(reflect.rtype).NumIn": extÛ°reflectÛ°rtypeÛ°NumIn,
+ "(reflect.rtype).NumMethod": extÛ°reflectÛ°rtypeÛ°NumMethod,
+ "(reflect.rtype).NumOut": extÛ°reflectÛ°rtypeÛ°NumOut,
+ "(reflect.rtype).Out": extÛ°reflectÛ°rtypeÛ°Out,
+ "(reflect.rtype).Size": extÛ°reflectÛ°rtypeÛ°Size,
+ "(reflect.rtype).String": extÛ°reflectÛ°rtypeÛ°String,
+ "bytes.Equal": extÛ°bytesÛ°Equal,
+ "bytes.IndexByte": extÛ°bytesÛ°IndexByte,
+ "hash/crc32.haveSSE42": extÛ°crc32Û°haveSSE42,
+ "math.Abs": extÛ°mathÛ°Abs,
+ "math.Exp": extÛ°mathÛ°Exp,
+ "math.Float32bits": extÛ°mathÛ°Float32bits,
+ "math.Float32frombits": extÛ°mathÛ°Float32frombits,
+ "math.Float64bits": extÛ°mathÛ°Float64bits,
+ "math.Float64frombits": extÛ°mathÛ°Float64frombits,
+ "math.Ldexp": extÛ°mathÛ°Ldexp,
+ "math.Log": extÛ°mathÛ°Log,
+ "math.Min": extÛ°mathÛ°Min,
+ "math.hasSSE4": extÛ°mathÛ°hasSSE4,
+ "os.Pipe": extÛ°osÛ°Pipe,
+ "os.runtime_args": extÛ°osÛ°runtime_args,
+ "os.runtime_beforeExit": extÛ°osÛ°runtime_beforeExit,
+ "reflect.New": extÛ°reflectÛ°New,
+ "reflect.SliceOf": extÛ°reflectÛ°SliceOf,
+ "reflect.TypeOf": extÛ°reflectÛ°TypeOf,
+ "reflect.ValueOf": extÛ°reflectÛ°ValueOf,
+ "reflect.Zero": extÛ°reflectÛ°Zero,
+ "reflect.init": extÛ°reflectÛ°Init,
+ "reflect.valueInterface": extÛ°reflectÛ°valueInterface,
+ "runtime.Breakpoint": extÛ°runtimeÛ°Breakpoint,
+ "runtime.Caller": extÛ°runtimeÛ°Caller,
+ "runtime.Callers": extÛ°runtimeÛ°Callers,
+ "runtime.FuncForPC": extÛ°runtimeÛ°FuncForPC,
+ "runtime.GC": extÛ°runtimeÛ°GC,
+ "runtime.GOMAXPROCS": extÛ°runtimeÛ°GOMAXPROCS,
+ "runtime.Goexit": extÛ°runtimeÛ°Goexit,
+ "runtime.Gosched": extÛ°runtimeÛ°Gosched,
+ "runtime.init": extÛ°runtimeÛ°init,
+ "runtime.NumCPU": extÛ°runtimeÛ°NumCPU,
+ "runtime.ReadMemStats": extÛ°runtimeÛ°ReadMemStats,
+ "runtime.SetFinalizer": extÛ°runtimeÛ°SetFinalizer,
+ "(*runtime.Func).Entry": extÛ°runtimeÛ°FuncÛ°Entry,
+ "(*runtime.Func).FileLine": extÛ°runtimeÛ°FuncÛ°FileLine,
+ "(*runtime.Func).Name": extÛ°runtimeÛ°FuncÛ°Name,
+ "runtime.environ": extÛ°runtimeÛ°environ,
+ "runtime.getgoroot": extÛ°runtimeÛ°getgoroot,
+ "strings.Index": extÛ°stringsÛ°Index,
+ "strings.IndexByte": extÛ°stringsÛ°IndexByte,
+ "sync.runtime_Semacquire": extÛ°syncÛ°runtime_Semacquire,
+ "sync.runtime_Semrelease": extÛ°syncÛ°runtime_Semrelease,
+ "sync.runtime_Syncsemcheck": extÛ°syncÛ°runtime_Syncsemcheck,
+ "sync.runtime_registerPoolCleanup": extÛ°syncÛ°runtime_registerPoolCleanup,
+ "sync/atomic.AddInt32": extÛ°atomicÛ°AddInt32,
+ "sync/atomic.AddUint32": extÛ°atomicÛ°AddUint32,
+ "sync/atomic.AddUint64": extÛ°atomicÛ°AddUint64,
+ "sync/atomic.CompareAndSwapInt32": extÛ°atomicÛ°CompareAndSwapInt32,
+ "sync/atomic.LoadInt32": extÛ°atomicÛ°LoadInt32,
+ "sync/atomic.LoadUint32": extÛ°atomicÛ°LoadUint32,
+ "sync/atomic.StoreInt32": extÛ°atomicÛ°StoreInt32,
+ "sync/atomic.StoreUint32": extÛ°atomicÛ°StoreUint32,
+ "syscall.Close": extÛ°syscallÛ°Close,
+ "syscall.Exit": extÛ°syscallÛ°Exit,
+ "syscall.Fstat": extÛ°syscallÛ°Fstat,
+ "syscall.Getpid": extÛ°syscallÛ°Getpid,
+ "syscall.Getwd": extÛ°syscallÛ°Getwd,
+ "syscall.Kill": extÛ°syscallÛ°Kill,
+ "syscall.Lstat": extÛ°syscallÛ°Lstat,
+ "syscall.Open": extÛ°syscallÛ°Open,
+ "syscall.ParseDirent": extÛ°syscallÛ°ParseDirent,
+ "syscall.RawSyscall": extÛ°syscallÛ°RawSyscall,
+ "syscall.Read": extÛ°syscallÛ°Read,
+ "syscall.ReadDirent": extÛ°syscallÛ°ReadDirent,
+ "syscall.Stat": extÛ°syscallÛ°Stat,
+ "syscall.Write": extÛ°syscallÛ°Write,
+ "syscall.runtime_envs": extÛ°runtimeÛ°environ,
+ "testing.runExample": extÛ°testingÛ°runExample,
+ "time.Sleep": extÛ°timeÛ°Sleep,
+ "time.now": extÛ°timeÛ°now,
+ }
+}
+
+// wrapError returns an interpreted 'error' interface value for err.
+func wrapError(err error) value {
+ if err == nil {
+ return iface{}
+ }
+ return iface{t: errorType, v: err.Error()}
+}
+
+func extÛ°syncÛ°PoolÛ°Get(fr *frame, args []value) value {
+ Pool := fr.i.prog.ImportedPackage("sync").Type("Pool").Object()
+ _, newIndex, _ := types.LookupFieldOrMethod(Pool.Type(), false, Pool.Pkg(), "New")
+
+ if New := (*args[0].(*value)).(structure)[newIndex[0]]; New != nil {
+ return call(fr.i, fr, 0, New, nil)
+ }
+ return nil
+}
+
+func extÛ°syncÛ°PoolÛ°Put(fr *frame, args []value) value {
+ return nil
+}
+
+func extÛ°bytesÛ°Equal(fr *frame, args []value) value {
+ // func Equal(a, b []byte) bool
+ a := args[0].([]value)
+ b := args[1].([]value)
+ if len(a) != len(b) {
+ return false
+ }
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func extÛ°bytesÛ°IndexByte(fr *frame, args []value) value {
+ // func IndexByte(s []byte, c byte) int
+ s := args[0].([]value)
+ c := args[1].(byte)
+ for i, b := range s {
+ if b.(byte) == c {
+ return i
+ }
+ }
+ return -1
+}
+
+func extÛ°crc32Û°haveSSE42(fr *frame, args []value) value {
+ return false
+}
+
+func extÛ°mathÛ°Float64frombits(fr *frame, args []value) value {
+ return math.Float64frombits(args[0].(uint64))
+}
+
+func extÛ°mathÛ°Float64bits(fr *frame, args []value) value {
+ return math.Float64bits(args[0].(float64))
+}
+
+func extÛ°mathÛ°Float32frombits(fr *frame, args []value) value {
+ return math.Float32frombits(args[0].(uint32))
+}
+
+func extÛ°mathÛ°Abs(fr *frame, args []value) value {
+ return math.Abs(args[0].(float64))
+}
+
+func extÛ°mathÛ°Exp(fr *frame, args []value) value {
+ return math.Exp(args[0].(float64))
+}
+
+func extÛ°mathÛ°Float32bits(fr *frame, args []value) value {
+ return math.Float32bits(args[0].(float32))
+}
+
+func extÛ°mathÛ°Min(fr *frame, args []value) value {
+ return math.Min(args[0].(float64), args[1].(float64))
+}
+
+func extÛ°mathÛ°hasSSE4(fr *frame, args []value) value {
+ return false
+}
+
+func extÛ°mathÛ°Ldexp(fr *frame, args []value) value {
+ return math.Ldexp(args[0].(float64), args[1].(int))
+}
+
+func extÛ°mathÛ°Log(fr *frame, args []value) value {
+ return math.Log(args[0].(float64))
+}
+
+func extÛ°osÛ°runtime_args(fr *frame, args []value) value {
+ return fr.i.osArgs
+}
+
+func extÛ°osÛ°runtime_beforeExit(fr *frame, args []value) value {
+ return nil
+}
+
+func extÛ°runtimeÛ°Breakpoint(fr *frame, args []value) value {
+ runtime.Breakpoint()
+ return nil
+}
+
+func extÛ°runtimeÛ°Caller(fr *frame, args []value) value {
+ // func Caller(skip int) (pc uintptr, file string, line int, ok bool)
+ skip := 1 + args[0].(int)
+ for i := 0; i < skip; i++ {
+ if fr != nil {
+ fr = fr.caller
+ }
+ }
+ var pc uintptr
+ var file string
+ var line int
+ var ok bool
+ if fr != nil {
+ fn := fr.fn
+ // TODO(adonovan): use pc/posn of current instruction, not start of fn.
+ // (Required to interpret the log package's tests.)
+ pc = uintptr(unsafe.Pointer(fn))
+ posn := fn.Prog.Fset.Position(fn.Pos())
+ file = posn.Filename
+ line = posn.Line
+ ok = true
+ }
+ return tuple{pc, file, line, ok}
+}
+
+func extÛ°runtimeÛ°Callers(fr *frame, args []value) value {
+ // Callers(skip int, pc []uintptr) int
+ skip := args[0].(int)
+ pc := args[1].([]value)
+ for i := 0; i < skip; i++ {
+ if fr != nil {
+ fr = fr.caller
+ }
+ }
+ i := 0
+ for fr != nil {
+ pc[i] = uintptr(unsafe.Pointer(fr.fn))
+ i++
+ fr = fr.caller
+ }
+ return i
+}
+
+func extÛ°runtimeÛ°FuncForPC(fr *frame, args []value) value {
+ // FuncForPC(pc uintptr) *Func
+ pc := args[0].(uintptr)
+ var fn *ssa.Function
+ if pc != 0 {
+ fn = (*ssa.Function)(unsafe.Pointer(pc)) // indeed unsafe!
+ }
+ var Func value
+ Func = structure{fn} // a runtime.Func
+ return &Func
+}
+
+func extÛ°runtimeÛ°environ(fr *frame, args []value) value {
+ // This function also implements syscall.runtime_envs.
+ return environ
+}
+
+func extÛ°runtimeÛ°getgoroot(fr *frame, args []value) value {
+ return os.Getenv("GOROOT")
+}
+
+func extÛ°stringsÛ°IndexByte(fr *frame, args []value) value {
+ // func IndexByte(s string, c byte) int
+ s := args[0].(string)
+ c := args[1].(byte)
+ for i := 0; i < len(s); i++ {
+ if s[i] == c {
+ return i
+ }
+ }
+ return -1
+}
+
+func extÛ°stringsÛ°Index(fr *frame, args []value) value {
+ // Call compiled version to avoid tricky asm dependency.
+ return strings.Index(args[0].(string), args[1].(string))
+}
+
+func extÛ°syncÛ°runtime_Syncsemcheck(fr *frame, args []value) value {
+ // TODO(adonovan): fix: implement.
+ return nil
+}
+
+func extÛ°syncÛ°runtime_registerPoolCleanup(fr *frame, args []value) value {
+ return nil
+}
+
+func extÛ°syncÛ°runtime_Semacquire(fr *frame, args []value) value {
+ // TODO(adonovan): fix: implement.
+ return nil
+}
+
+func extÛ°syncÛ°runtime_Semrelease(fr *frame, args []value) value {
+ // TODO(adonovan): fix: implement.
+ return nil
+}
+
+func extÛ°runtimeÛ°GOMAXPROCS(fr *frame, args []value) value {
+ // Ignore args[0]; don't let the interpreted program
+ // set the interpreter's GOMAXPROCS!
+ return runtime.GOMAXPROCS(0)
+}
+
+func extÛ°runtimeÛ°Goexit(fr *frame, args []value) value {
+ // TODO(adonovan): don't kill the interpreter's main goroutine.
+ runtime.Goexit()
+ return nil
+}
+
+func extÛ°runtimeÛ°GC(fr *frame, args []value) value {
+ runtime.GC()
+ return nil
+}
+
+func extÛ°runtimeÛ°Gosched(fr *frame, args []value) value {
+ runtime.Gosched()
+ return nil
+}
+
+func extÛ°runtimeÛ°init(fr *frame, args []value) value {
+ return nil
+}
+
+func extÛ°runtimeÛ°NumCPU(fr *frame, args []value) value {
+ return runtime.NumCPU()
+}
+
+func extÛ°runtimeÛ°ReadMemStats(fr *frame, args []value) value {
+ // TODO(adonovan): populate args[0].(Struct)
+ return nil
+}
+
+func extÛ°atomicÛ°LoadUint32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ return (*args[0].(*value)).(uint32)
+}
+
+func extÛ°atomicÛ°StoreUint32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ *args[0].(*value) = args[1].(uint32)
+ return nil
+}
+
+func extÛ°atomicÛ°LoadInt32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ return (*args[0].(*value)).(int32)
+}
+
+func extÛ°atomicÛ°StoreInt32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ *args[0].(*value) = args[1].(int32)
+ return nil
+}
+
+func extÛ°atomicÛ°CompareAndSwapInt32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ p := args[0].(*value)
+ if (*p).(int32) == args[1].(int32) {
+ *p = args[2].(int32)
+ return true
+ }
+ return false
+}
+
+func extÛ°atomicÛ°AddInt32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ p := args[0].(*value)
+ newv := (*p).(int32) + args[1].(int32)
+ *p = newv
+ return newv
+}
+
+func extÛ°atomicÛ°AddUint32(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ p := args[0].(*value)
+ newv := (*p).(uint32) + args[1].(uint32)
+ *p = newv
+ return newv
+}
+
+func extÛ°atomicÛ°AddUint64(fr *frame, args []value) value {
+ // TODO(adonovan): fix: not atomic!
+ p := args[0].(*value)
+ newv := (*p).(uint64) + args[1].(uint64)
+ *p = newv
+ return newv
+}
+
+func extÛ°runtimeÛ°SetFinalizer(fr *frame, args []value) value {
+ return nil // ignore
+}
+
+// Pretend: type runtime.Func struct { entry *ssa.Function }
+
+func extÛ°runtimeÛ°FuncÛ°FileLine(fr *frame, args []value) value {
+ // func (*runtime.Func) FileLine(uintptr) (string, int)
+ f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function)
+ pc := args[1].(uintptr)
+ _ = pc
+ if f != nil {
+ // TODO(adonovan): use position of current instruction, not fn.
+ posn := f.Prog.Fset.Position(f.Pos())
+ return tuple{posn.Filename, posn.Line}
+ }
+ return tuple{"", 0}
+}
+
+func extÛ°runtimeÛ°FuncÛ°Name(fr *frame, args []value) value {
+ // func (*runtime.Func) Name() string
+ f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function)
+ if f != nil {
+ return f.String()
+ }
+ return ""
+}
+
+func extÛ°runtimeÛ°FuncÛ°Entry(fr *frame, args []value) value {
+ // func (*runtime.Func) Entry() uintptr
+ f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function)
+ return uintptr(unsafe.Pointer(f))
+}
+
+// This is a workaround for a bug in go/ssa/testmain.go: it creates
+// InternalExamples even for Example functions with no Output comment.
+// TODO(adonovan): fix (and redesign) testmain.go after Go 1.6.
+func extÛ°testingÛ°runExample(fr *frame, args []value) value {
+ // This is a stripped down runExample that simply calls the function.
+ // It does not capture and compare output nor recover from panic.
+ //
+ // func runExample(eg testing.InternalExample) bool {
+ // eg.F()
+ // return true
+ // }
+ F := args[0].(structure)[1]
+ call(fr.i, fr, 0, F, nil)
+ return true
+}
+
+func extÛ°timeÛ°now(fr *frame, args []value) value {
+ nano := time.Now().UnixNano()
+ return tuple{int64(nano / 1e9), int32(nano % 1e9)}
+}
+
+func extÛ°timeÛ°Sleep(fr *frame, args []value) value {
+ time.Sleep(time.Duration(args[0].(int64)))
+ return nil
+}
+
+func extÛ°syscallÛ°Exit(fr *frame, args []value) value {
+ panic(exitPanic(args[0].(int)))
+}
+
+func extÛ°syscallÛ°Getwd(fr *frame, args []value) value {
+ s, err := syscall.Getwd()
+ return tuple{s, wrapError(err)}
+}
+
+func extÛ°syscallÛ°Getpid(fr *frame, args []value) value {
+ return syscall.Getpid()
+}
+
+func valueToBytes(v value) []byte {
+ in := v.([]value)
+ b := make([]byte, len(in))
+ for i := range in {
+ b[i] = in[i].(byte)
+ }
+ return b
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/external_plan9.go b/go/src/golang.org/x/tools/go/ssa/interp/external_plan9.go
index 05d02d5..81bedcf 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/external_plan9.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/external_plan9.go
@@ -6,6 +6,9 @@
import "syscall"
+func extÛ°osÛ°Pipe(fr *frame, args []value) value {
+ panic("os.Pipe not yet implemented")
+}
func extÛ°syscallÛ°Close(fr *frame, args []value) value {
panic("syscall.Close not yet implemented")
}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/external_unix.go b/go/src/golang.org/x/tools/go/ssa/interp/external_unix.go
index c482eab..be12586 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/external_unix.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/external_unix.go
@@ -8,6 +8,22 @@
import "syscall"
+func extÛ°osÛ°Pipe(fr *frame, args []value) value {
+ // func os.Pipe() (r *File, w *File, err error)
+
+ // The portable POSIX pipe(2) call is good enough for our needs.
+ var p [2]int
+ if err := syscall.Pipe(p[:]); err != nil {
+ // TODO(adonovan): fix: return an *os.SyscallError.
+ return tuple{nil, nil, wrapError(err)}
+ }
+
+ NewFile := fr.i.prog.ImportedPackage("os").Func("NewFile")
+ r := call(fr.i, fr, 0, NewFile, []value{uintptr(p[0]), "|0"})
+ w := call(fr.i, fr, 0, NewFile, []value{uintptr(p[1]), "|1"})
+ return tuple{r, w, wrapError(nil)}
+}
+
func fillStat(st *syscall.Stat_t, stat structure) {
stat[0] = st.Dev
stat[1] = st.Ino
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/external_windows.go b/go/src/golang.org/x/tools/go/ssa/interp/external_windows.go
index ef28a37..24d1a87 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/external_windows.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/external_windows.go
@@ -6,6 +6,9 @@
import "syscall"
+func extÛ°osÛ°Pipe(fr *frame, args []value) value {
+ panic("os.Pipe not yet implemented")
+}
func extÛ°syscallÛ°Close(fr *frame, args []value) value {
panic("syscall.Close not yet implemented")
}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/interp.go b/go/src/golang.org/x/tools/go/ssa/interp/interp.go
index afe4939..b855645 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/interp.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/interp.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package ssa/interp defines an interpreter for the SSA
// representation of Go programs.
//
@@ -47,12 +49,12 @@
import (
"fmt"
"go/token"
+ "go/types"
"os"
"reflect"
"runtime"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
type continuation int
@@ -619,7 +621,7 @@
*g = v
return
}
- panic("no global variable: " + pkg.Object.Path() + "." + name)
+ panic("no global variable: " + pkg.Pkg.Path() + "." + name)
}
var environ []value
@@ -687,7 +689,7 @@
}
// Ad-hoc initialization for magic system variables.
- switch pkg.Object.Path() {
+ switch pkg.Pkg.Path() {
case "syscall":
setGlobal(i, pkg, "envs", environ)
@@ -695,7 +697,7 @@
deleteBodies(pkg, "DeepEqual", "deepValueEqual")
case "runtime":
- sz := sizes.Sizeof(pkg.Object.Scope().Lookup("MemStats").Type())
+ sz := sizes.Sizeof(pkg.Pkg.Scope().Lookup("MemStats").Type())
setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz))
deleteBodies(pkg, "GOROOT", "gogetenv")
}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/interp14.go b/go/src/golang.org/x/tools/go/ssa/interp/interp14.go
new file mode 100644
index 0000000..dbd4dac
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/interp14.go
@@ -0,0 +1,752 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package ssa/interp defines an interpreter for the SSA
+// representation of Go programs.
+//
+// This interpreter is provided as an adjunct for testing the SSA
+// construction algorithm. Its purpose is to provide a minimal
+// metacircular implementation of the dynamic semantics of each SSA
+// instruction. It is not, and will never be, a production-quality Go
+// interpreter.
+//
+// The following is a partial list of Go features that are currently
+// unsupported or incomplete in the interpreter.
+//
+// * Unsafe operations, including all uses of unsafe.Pointer, are
+// impossible to support given the "boxed" value representation we
+// have chosen.
+//
+// * The reflect package is only partially implemented.
+//
+// * "sync/atomic" operations are not currently atomic due to the
+// "boxed" value representation: it is not possible to read, modify
+// and write an interface value atomically. As a consequence, Mutexes
+// are currently broken. TODO(adonovan): provide a metacircular
+// implementation of Mutex avoiding the broken atomic primitives.
+//
+// * recover is only partially implemented. Also, the interpreter
+// makes no attempt to distinguish target panics from interpreter
+// crashes.
+//
+// * map iteration is asymptotically inefficient.
+//
+// * the sizes of the int, uint and uintptr types in the target
+// program are assumed to be the same as those of the interpreter
+// itself.
+//
+// * all values occupy space, even those of types defined by the spec
+// to have zero size, e.g. struct{}. This can cause asymptotic
+// performance degradation.
+//
+// * os.Exit is implemented using panic, causing deferred functions to
+// run.
+package interp // import "golang.org/x/tools/go/ssa/interp"
+
+import (
+ "fmt"
+ "go/token"
+ "os"
+ "reflect"
+ "runtime"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+type continuation int
+
+const (
+ kNext continuation = iota
+ kReturn
+ kJump
+)
+
+// Mode is a bitmask of options affecting the interpreter.
+type Mode uint
+
+const (
+ DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead.
+ EnableTracing // Print a trace of all instructions as they are interpreted.
+)
+
+type methodSet map[string]*ssa.Function
+
+// State shared between all interpreted goroutines.
+type interpreter struct {
+ osArgs []value // the value of os.Args
+ prog *ssa.Program // the SSA program
+ globals map[ssa.Value]*value // addresses of global variables (immutable)
+ mode Mode // interpreter options
+ reflectPackage *ssa.Package // the fake reflect package
+ errorMethods methodSet // the method set of reflect.error, which implements the error interface.
+ rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface.
+ runtimeErrorString types.Type // the runtime.errorString type
+ sizes types.Sizes // the effective type-sizing function
+}
+
+type deferred struct {
+ fn value
+ args []value
+ instr *ssa.Defer
+ tail *deferred
+}
+
+type frame struct {
+ i *interpreter
+ caller *frame
+ fn *ssa.Function
+ block, prevBlock *ssa.BasicBlock
+ env map[ssa.Value]value // dynamic values of SSA variables
+ locals []value
+ defers *deferred
+ result value
+ panicking bool
+ panic interface{}
+}
+
+func (fr *frame) get(key ssa.Value) value {
+ switch key := key.(type) {
+ case nil:
+ // Hack; simplifies handling of optional attributes
+ // such as ssa.Slice.{Low,High}.
+ return nil
+ case *ssa.Function, *ssa.Builtin:
+ return key
+ case *ssa.Const:
+ return constValue(key)
+ case *ssa.Global:
+ if r, ok := fr.i.globals[key]; ok {
+ return r
+ }
+ }
+ if r, ok := fr.env[key]; ok {
+ return r
+ }
+ panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name()))
+}
+
+// runDefer runs a deferred call d.
+// It always returns normally, but may set or clear fr.panic.
+//
+func (fr *frame) runDefer(d *deferred) {
+ if fr.i.mode&EnableTracing != 0 {
+ fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n",
+ fr.i.prog.Fset.Position(d.instr.Pos()))
+ }
+ var ok bool
+ defer func() {
+ if !ok {
+ // Deferred call created a new state of panic.
+ fr.panicking = true
+ fr.panic = recover()
+ }
+ }()
+ call(fr.i, fr, d.instr.Pos(), d.fn, d.args)
+ ok = true
+}
+
+// runDefers executes fr's deferred function calls in LIFO order.
+//
+// On entry, fr.panicking indicates a state of panic; if
+// true, fr.panic contains the panic value.
+//
+// On completion, if a deferred call started a panic, or if no
+// deferred call recovered from a previous state of panic, then
+// runDefers itself panics after the last deferred call has run.
+//
+// If there was no initial state of panic, or it was recovered from,
+// runDefers returns normally.
+//
+func (fr *frame) runDefers() {
+ for d := fr.defers; d != nil; d = d.tail {
+ fr.runDefer(d)
+ }
+ fr.defers = nil
+ if fr.panicking {
+ panic(fr.panic) // new panic, or still panicking
+ }
+}
+
+// lookupMethod returns the method set for type typ, which may be one
+// of the interpreter's fake types.
+func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function {
+ switch typ {
+ case rtypeType:
+ return i.rtypeMethods[meth.Id()]
+ case errorType:
+ return i.errorMethods[meth.Id()]
+ }
+ return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name())
+}
+
+// visitInstr interprets a single ssa.Instruction within the activation
+// record frame. It returns a continuation value indicating where to
+// read the next instruction from.
+func visitInstr(fr *frame, instr ssa.Instruction) continuation {
+ switch instr := instr.(type) {
+ case *ssa.DebugRef:
+ // no-op
+
+ case *ssa.UnOp:
+ fr.env[instr] = unop(instr, fr.get(instr.X))
+
+ case *ssa.BinOp:
+ fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y))
+
+ case *ssa.Call:
+ fn, args := prepareCall(fr, &instr.Call)
+ fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args)
+
+ case *ssa.ChangeInterface:
+ fr.env[instr] = fr.get(instr.X)
+
+ case *ssa.ChangeType:
+ fr.env[instr] = fr.get(instr.X) // (can't fail)
+
+ case *ssa.Convert:
+ fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
+
+ case *ssa.MakeInterface:
+ fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)}
+
+ case *ssa.Extract:
+ fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index]
+
+ case *ssa.Slice:
+ fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max))
+
+ case *ssa.Return:
+ switch len(instr.Results) {
+ case 0:
+ case 1:
+ fr.result = fr.get(instr.Results[0])
+ default:
+ var res []value
+ for _, r := range instr.Results {
+ res = append(res, fr.get(r))
+ }
+ fr.result = tuple(res)
+ }
+ fr.block = nil
+ return kReturn
+
+ case *ssa.RunDefers:
+ fr.runDefers()
+
+ case *ssa.Panic:
+ panic(targetPanic{fr.get(instr.X)})
+
+ case *ssa.Send:
+ fr.get(instr.Chan).(chan value) <- fr.get(instr.X)
+
+ case *ssa.Store:
+ store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val))
+
+ case *ssa.If:
+ succ := 1
+ if fr.get(instr.Cond).(bool) {
+ succ = 0
+ }
+ fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ]
+ return kJump
+
+ case *ssa.Jump:
+ fr.prevBlock, fr.block = fr.block, fr.block.Succs[0]
+ return kJump
+
+ case *ssa.Defer:
+ fn, args := prepareCall(fr, &instr.Call)
+ fr.defers = &deferred{
+ fn: fn,
+ args: args,
+ instr: instr,
+ tail: fr.defers,
+ }
+
+ case *ssa.Go:
+ fn, args := prepareCall(fr, &instr.Call)
+ go call(fr.i, nil, instr.Pos(), fn, args)
+
+ case *ssa.MakeChan:
+ fr.env[instr] = make(chan value, asInt(fr.get(instr.Size)))
+
+ case *ssa.Alloc:
+ var addr *value
+ if instr.Heap {
+ // new
+ addr = new(value)
+ fr.env[instr] = addr
+ } else {
+ // local
+ addr = fr.env[instr].(*value)
+ }
+ *addr = zero(deref(instr.Type()))
+
+ case *ssa.MakeSlice:
+ slice := make([]value, asInt(fr.get(instr.Cap)))
+ tElt := instr.Type().Underlying().(*types.Slice).Elem()
+ for i := range slice {
+ slice[i] = zero(tElt)
+ }
+ fr.env[instr] = slice[:asInt(fr.get(instr.Len))]
+
+ case *ssa.MakeMap:
+ reserve := 0
+ if instr.Reserve != nil {
+ reserve = asInt(fr.get(instr.Reserve))
+ }
+ fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve)
+
+ case *ssa.Range:
+ fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type())
+
+ case *ssa.Next:
+ fr.env[instr] = fr.get(instr.Iter).(iter).next()
+
+ case *ssa.FieldAddr:
+ x := fr.get(instr.X)
+ // FIXME wrong! &global.f must not change if we do *global = zero!
+ fr.env[instr] = &(*x.(*value)).(structure)[instr.Field]
+
+ case *ssa.Field:
+ fr.env[instr] = fr.get(instr.X).(structure)[instr.Field]
+
+ case *ssa.IndexAddr:
+ x := fr.get(instr.X)
+ idx := fr.get(instr.Index)
+ switch x := x.(type) {
+ case []value:
+ fr.env[instr] = &x[asInt(idx)]
+ case *value: // *array
+ fr.env[instr] = &(*x).(array)[asInt(idx)]
+ default:
+ panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
+ }
+
+ case *ssa.Index:
+ fr.env[instr] = fr.get(instr.X).(array)[asInt(fr.get(instr.Index))]
+
+ case *ssa.Lookup:
+ fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index))
+
+ case *ssa.MapUpdate:
+ m := fr.get(instr.Map)
+ key := fr.get(instr.Key)
+ v := fr.get(instr.Value)
+ switch m := m.(type) {
+ case map[value]value:
+ m[key] = v
+ case *hashmap:
+ m.insert(key.(hashable), v)
+ default:
+ panic(fmt.Sprintf("illegal map type: %T", m))
+ }
+
+ case *ssa.TypeAssert:
+ fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface))
+
+ case *ssa.MakeClosure:
+ var bindings []value
+ for _, binding := range instr.Bindings {
+ bindings = append(bindings, fr.get(binding))
+ }
+ fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings}
+
+ case *ssa.Phi:
+ for i, pred := range instr.Block().Preds {
+ if fr.prevBlock == pred {
+ fr.env[instr] = fr.get(instr.Edges[i])
+ break
+ }
+ }
+
+ case *ssa.Select:
+ var cases []reflect.SelectCase
+ if !instr.Blocking {
+ cases = append(cases, reflect.SelectCase{
+ Dir: reflect.SelectDefault,
+ })
+ }
+ for _, state := range instr.States {
+ var dir reflect.SelectDir
+ if state.Dir == types.RecvOnly {
+ dir = reflect.SelectRecv
+ } else {
+ dir = reflect.SelectSend
+ }
+ var send reflect.Value
+ if state.Send != nil {
+ send = reflect.ValueOf(fr.get(state.Send))
+ }
+ cases = append(cases, reflect.SelectCase{
+ Dir: dir,
+ Chan: reflect.ValueOf(fr.get(state.Chan)),
+ Send: send,
+ })
+ }
+ chosen, recv, recvOk := reflect.Select(cases)
+ if !instr.Blocking {
+ chosen-- // default case should have index -1.
+ }
+ r := tuple{chosen, recvOk}
+ for i, st := range instr.States {
+ if st.Dir == types.RecvOnly {
+ var v value
+ if i == chosen && recvOk {
+ // No need to copy since send makes an unaliased copy.
+ v = recv.Interface().(value)
+ } else {
+ v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem())
+ }
+ r = append(r, v)
+ }
+ }
+ fr.env[instr] = r
+
+ default:
+ panic(fmt.Sprintf("unexpected instruction: %T", instr))
+ }
+
+ // if val, ok := instr.(ssa.Value); ok {
+ // fmt.Println(toString(fr.env[val])) // debugging
+ // }
+
+ return kNext
+}
+
+// prepareCall determines the function value and argument values for a
+// function call in a Call, Go or Defer instruction, performing
+// interface method lookup if needed.
+//
+func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
+ v := fr.get(call.Value)
+ if call.Method == nil {
+ // Function call.
+ fn = v
+ } else {
+ // Interface method invocation.
+ recv := v.(iface)
+ if recv.t == nil {
+ panic("method invoked on nil interface")
+ }
+ if f := lookupMethod(fr.i, recv.t, call.Method); f == nil {
+ // Unreachable in well-typed programs.
+ panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method))
+ } else {
+ fn = f
+ }
+ args = append(args, recv.v)
+ }
+ for _, arg := range call.Args {
+ args = append(args, fr.get(arg))
+ }
+ return
+}
+
+// call interprets a call to a function (function, builtin or closure)
+// fn with arguments args, returning its result.
+// callpos is the position of the callsite.
+//
+func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value {
+ switch fn := fn.(type) {
+ case *ssa.Function:
+ if fn == nil {
+ panic("call of nil function") // nil of func type
+ }
+ return callSSA(i, caller, callpos, fn, args, nil)
+ case *closure:
+ return callSSA(i, caller, callpos, fn.Fn, args, fn.Env)
+ case *ssa.Builtin:
+ return callBuiltin(caller, callpos, fn, args)
+ }
+ panic(fmt.Sprintf("cannot call %T", fn))
+}
+
+func loc(fset *token.FileSet, pos token.Pos) string {
+ if pos == token.NoPos {
+ return ""
+ }
+ return " at " + fset.Position(pos).String()
+}
+
+// callSSA interprets a call to function fn with arguments args,
+// and lexical environment env, returning its result.
+// callpos is the position of the callsite.
+//
+func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value {
+ if i.mode&EnableTracing != 0 {
+ fset := fn.Prog.Fset
+ // TODO(adonovan): fix: loc() lies for external functions.
+ fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos()))
+ suffix := ""
+ if caller != nil {
+ suffix = ", resuming " + caller.fn.String() + loc(fset, callpos)
+ }
+ defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix)
+ }
+ fr := &frame{
+ i: i,
+ caller: caller, // for panic/recover
+ fn: fn,
+ }
+ if fn.Parent() == nil {
+ name := fn.String()
+ if ext := externals[name]; ext != nil {
+ if i.mode&EnableTracing != 0 {
+ fmt.Fprintln(os.Stderr, "\t(external)")
+ }
+ return ext(fr, args)
+ }
+ if fn.Blocks == nil {
+ panic("no code for function: " + name)
+ }
+ }
+ fr.env = make(map[ssa.Value]value)
+ fr.block = fn.Blocks[0]
+ fr.locals = make([]value, len(fn.Locals))
+ for i, l := range fn.Locals {
+ fr.locals[i] = zero(deref(l.Type()))
+ fr.env[l] = &fr.locals[i]
+ }
+ for i, p := range fn.Params {
+ fr.env[p] = args[i]
+ }
+ for i, fv := range fn.FreeVars {
+ fr.env[fv] = env[i]
+ }
+ for fr.block != nil {
+ runFrame(fr)
+ }
+ // Destroy the locals to avoid accidental use after return.
+ for i := range fn.Locals {
+ fr.locals[i] = bad{}
+ }
+ return fr.result
+}
+
+// runFrame executes SSA instructions starting at fr.block and
+// continuing until a return, a panic, or a recovered panic.
+//
+// After a panic, runFrame panics.
+//
+// After a normal return, fr.result contains the result of the call
+// and fr.block is nil.
+//
+// A recovered panic in a function without named return parameters
+// (NRPs) becomes a normal return of the zero value of the function's
+// result type.
+//
+// After a recovered panic in a function with NRPs, fr.result is
+// undefined and fr.block contains the block at which to resume
+// control.
+//
+func runFrame(fr *frame) {
+ defer func() {
+ if fr.block == nil {
+ return // normal return
+ }
+ if fr.i.mode&DisableRecover != 0 {
+ return // let interpreter crash
+ }
+ fr.panicking = true
+ fr.panic = recover()
+ if fr.i.mode&EnableTracing != 0 {
+ fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic)
+ }
+ fr.runDefers()
+ fr.block = fr.fn.Recover
+ }()
+
+ for {
+ if fr.i.mode&EnableTracing != 0 {
+ fmt.Fprintf(os.Stderr, ".%s:\n", fr.block)
+ }
+ block:
+ for _, instr := range fr.block.Instrs {
+ if fr.i.mode&EnableTracing != 0 {
+ if v, ok := instr.(ssa.Value); ok {
+ fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr)
+ } else {
+ fmt.Fprintln(os.Stderr, "\t", instr)
+ }
+ }
+ switch visitInstr(fr, instr) {
+ case kReturn:
+ return
+ case kNext:
+ // no-op
+ case kJump:
+ break block
+ }
+ }
+ }
+}
+
+// doRecover implements the recover() built-in.
+func doRecover(caller *frame) value {
+ // recover() must be exactly one level beneath the deferred
+ // function (two levels beneath the panicking function) to
+ // have any effect. Thus we ignore both "defer recover()" and
+ // "defer f() -> g() -> recover()".
+ if caller.i.mode&DisableRecover == 0 &&
+ caller != nil && !caller.panicking &&
+ caller.caller != nil && caller.caller.panicking {
+ caller.caller.panicking = false
+ p := caller.caller.panic
+ caller.caller.panic = nil
+ switch p := p.(type) {
+ case targetPanic:
+ // The target program explicitly called panic().
+ return p.v
+ case runtime.Error:
+ // The interpreter encountered a runtime error.
+ return iface{caller.i.runtimeErrorString, p.Error()}
+ case string:
+ // The interpreter explicitly called panic().
+ return iface{caller.i.runtimeErrorString, p}
+ default:
+ panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p))
+ }
+ }
+ return iface{}
+}
+
+// setGlobal sets the value of a system-initialized global variable.
+func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) {
+ if g, ok := i.globals[pkg.Var(name)]; ok {
+ *g = v
+ return
+ }
+ panic("no global variable: " + pkg.Pkg.Path() + "." + name)
+}
+
+var environ []value
+
+func init() {
+ for _, s := range os.Environ() {
+ environ = append(environ, s)
+ }
+ environ = append(environ, "GOSSAINTERP=1")
+ environ = append(environ, "GOARCH="+runtime.GOARCH)
+}
+
+// deleteBodies delete the bodies of all standalone functions except the
+// specified ones. A missing intrinsic leads to a clear runtime error.
+func deleteBodies(pkg *ssa.Package, except ...string) {
+ keep := make(map[string]bool)
+ for _, e := range except {
+ keep[e] = true
+ }
+ for _, mem := range pkg.Members {
+ if fn, ok := mem.(*ssa.Function); ok && !keep[fn.Name()] {
+ fn.Blocks = nil
+ }
+ }
+}
+
+// Interpret interprets the Go program whose main package is mainpkg.
+// mode specifies various interpreter options. filename and args are
+// the initial values of os.Args for the target program. sizes is the
+// effective type-sizing function for this program.
+//
+// Interpret returns the exit code of the program: 2 for panic (like
+// gc does), or the argument to os.Exit for normal termination.
+//
+// The SSA program must include the "runtime" package.
+//
+func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) {
+ i := &interpreter{
+ prog: mainpkg.Prog,
+ globals: make(map[ssa.Value]*value),
+ mode: mode,
+ sizes: sizes,
+ }
+ runtimePkg := i.prog.ImportedPackage("runtime")
+ if runtimePkg == nil {
+ panic("ssa.Program doesn't include runtime package")
+ }
+ i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type()
+
+ initReflect(i)
+
+ i.osArgs = append(i.osArgs, filename)
+ for _, arg := range args {
+ i.osArgs = append(i.osArgs, arg)
+ }
+
+ for _, pkg := range i.prog.AllPackages() {
+ // Initialize global storage.
+ for _, m := range pkg.Members {
+ switch v := m.(type) {
+ case *ssa.Global:
+ cell := zero(deref(v.Type()))
+ i.globals[v] = &cell
+ }
+ }
+
+ // Ad-hoc initialization for magic system variables.
+ switch pkg.Pkg.Path() {
+ case "syscall":
+ setGlobal(i, pkg, "envs", environ)
+
+ case "reflect":
+ deleteBodies(pkg, "DeepEqual", "deepValueEqual")
+
+ case "runtime":
+ sz := sizes.Sizeof(pkg.Pkg.Scope().Lookup("MemStats").Type())
+ setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz))
+ deleteBodies(pkg, "GOROOT", "gogetenv")
+ }
+ }
+
+ // Top-level error handler.
+ exitCode = 2
+ defer func() {
+ if exitCode != 2 || i.mode&DisableRecover != 0 {
+ return
+ }
+ switch p := recover().(type) {
+ case exitPanic:
+ exitCode = int(p)
+ return
+ case targetPanic:
+ fmt.Fprintln(os.Stderr, "panic:", toString(p.v))
+ case runtime.Error:
+ fmt.Fprintln(os.Stderr, "panic:", p.Error())
+ case string:
+ fmt.Fprintln(os.Stderr, "panic:", p)
+ default:
+ fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p)
+ }
+
+ // TODO(adonovan): dump panicking interpreter goroutine?
+ // buf := make([]byte, 0x10000)
+ // runtime.Stack(buf, false)
+ // fmt.Fprintln(os.Stderr, string(buf))
+ // (Or dump panicking target goroutine?)
+ }()
+
+ // Run!
+ call(i, nil, token.NoPos, mainpkg.Func("init"), nil)
+ if mainFn := mainpkg.Func("main"); mainFn != nil {
+ call(i, nil, token.NoPos, mainFn, nil)
+ exitCode = 0
+ } else {
+ fmt.Fprintln(os.Stderr, "No main function.")
+ exitCode = 1
+ }
+ return
+}
+
+// deref returns a pointer's element type; otherwise it returns typ.
+// TODO(adonovan): Import from ssa?
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/interp14_test.go b/go/src/golang.org/x/tools/go/ssa/interp/interp14_test.go
new file mode 100644
index 0000000..63c3f53
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/interp14_test.go
@@ -0,0 +1,367 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// +build !android,!windows,!plan9
+
+package interp_test
+
+import (
+ "bytes"
+ "fmt"
+ "go/build"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/interp"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+// Each line contains a space-separated list of $GOROOT/test/
+// filenames comprising the main package of a program.
+// They are ordered quickest-first, roughly.
+//
+// TODO(adonovan): integrate into the $GOROOT/test driver scripts,
+// golden file checking, etc.
+var gorootTestTests = []string{
+ "235.go",
+ "alias1.go",
+ "chancap.go",
+ "func5.go",
+ "func6.go",
+ "func7.go",
+ "func8.go",
+ "helloworld.go",
+ "varinit.go",
+ "escape3.go",
+ "initcomma.go",
+ "cmp.go",
+ "compos.go",
+ "turing.go",
+ "indirect.go",
+ // "complit.go", // tests go1.5 features
+ "for.go",
+ "struct0.go",
+ "intcvt.go",
+ "printbig.go",
+ "deferprint.go",
+ "escape.go",
+ "range.go",
+ "const4.go",
+ "float_lit.go",
+ "bigalg.go",
+ "decl.go",
+ "if.go",
+ "named.go",
+ "bigmap.go",
+ "func.go",
+ "reorder2.go",
+ "closure.go",
+ "gc.go",
+ "simassign.go",
+ "iota.go",
+ "nilptr2.go",
+ "goprint.go", // doesn't actually assert anything (cmpout)
+ "utf.go",
+ "method.go",
+ "char_lit.go",
+ "env.go",
+ "int_lit.go",
+ "string_lit.go",
+ "defer.go",
+ "typeswitch.go",
+ "stringrange.go",
+ "reorder.go",
+ "method3.go",
+ "literal.go",
+ "nul1.go", // doesn't actually assert anything (errorcheckoutput)
+ "zerodivide.go",
+ "convert.go",
+ "convT2X.go",
+ "switch.go",
+ "initialize.go",
+ "ddd.go",
+ "blank.go", // partly disabled
+ "map.go",
+ "closedchan.go",
+ "divide.go",
+ "rename.go",
+ "const3.go",
+ "nil.go",
+ "recover.go", // reflection parts disabled
+ "recover1.go",
+ "recover2.go",
+ "recover3.go",
+ "typeswitch1.go",
+ "floatcmp.go",
+ "crlf.go", // doesn't actually assert anything (runoutput)
+ // Slow tests follow.
+ "bom.go", // ~1.7s
+ "gc1.go", // ~1.7s
+ "cmplxdivide.go cmplxdivide1.go", // ~2.4s
+
+ // Working, but not worth enabling:
+ // "append.go", // works, but slow (15s).
+ // "gc2.go", // works, but slow, and cheats on the memory check.
+ // "sigchld.go", // works, but only on POSIX.
+ // "peano.go", // works only up to n=9, and slow even then.
+ // "stack.go", // works, but too slow (~30s) by default.
+ // "solitaire.go", // works, but too slow (~30s).
+ // "const.go", // works but for but one bug: constant folder doesn't consider representations.
+ // "init1.go", // too slow (80s) and not that interesting. Cheats on ReadMemStats check too.
+ // "rotate.go rotate0.go", // emits source for a test
+ // "rotate.go rotate1.go", // emits source for a test
+ // "rotate.go rotate2.go", // emits source for a test
+ // "rotate.go rotate3.go", // emits source for a test
+ // "64bit.go", // emits source for a test
+ // "run.go", // test driver, not a test.
+
+ // Broken. TODO(adonovan): fix.
+ // copy.go // very slow; but with N=4 quickly crashes, slice index out of range.
+ // nilptr.go // interp: V > uintptr not implemented. Slow test, lots of mem
+ // args.go // works, but requires specific os.Args from the driver.
+ // index.go // a template, not a real test.
+ // mallocfin.go // SetFinalizer not implemented.
+
+ // TODO(adonovan): add tests from $GOROOT/test/* subtrees:
+ // bench chan bugs fixedbugs interface ken.
+}
+
+// These are files in go.tools/go/ssa/interp/testdata/.
+var testdataTests = []string{
+ "boundmeth.go",
+ // "complit.go", // requires go1.5
+ "coverage.go",
+ "defer.go",
+ "fieldprom.go",
+ "ifaceconv.go",
+ "ifaceprom.go",
+ "initorder.go",
+ "methprom.go",
+ "mrvchain.go",
+ "range.go",
+ "recover.go",
+ "reflect.go",
+ "static.go",
+ "callstack.go",
+}
+
+// These are files and packages in $GOROOT/src/.
+var gorootSrcTests = []string{
+ "encoding/ascii85",
+ "encoding/hex",
+ // "encoding/pem", // TODO(adonovan): implement (reflect.Value).SetString
+ // "testing", // TODO(adonovan): implement runtime.Goexit correctly
+ // "hash/crc32", // TODO(adonovan): implement hash/crc32.haveCLMUL
+ // "log", // TODO(adonovan): implement runtime.Callers correctly
+
+ // Too slow:
+ // "container/ring",
+ // "hash/adler32",
+
+ "unicode/utf8",
+ "path",
+ "flag",
+ "encoding/csv",
+ "text/scanner",
+ "unicode",
+}
+
+type successPredicate func(exitcode int, output string) error
+
+func run(t *testing.T, dir, input string, success successPredicate) bool {
+ fmt.Printf("Input: %s\n", input)
+
+ start := time.Now()
+
+ var inputs []string
+ for _, i := range strings.Split(input, " ") {
+ if strings.HasSuffix(i, ".go") {
+ i = dir + i
+ }
+ inputs = append(inputs, i)
+ }
+
+ var conf loader.Config
+ if _, err := conf.FromArgs(inputs, true); err != nil {
+ t.Errorf("FromArgs(%s) failed: %s", inputs, err)
+ return false
+ }
+
+ conf.Import("runtime")
+
+ // Print a helpful hint if we don't make it to the end.
+ var hint string
+ defer func() {
+ if hint != "" {
+ fmt.Println("FAIL")
+ fmt.Println(hint)
+ } else {
+ fmt.Println("PASS")
+ }
+
+ interp.CapturedOutput = nil
+ }()
+
+ hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input)
+
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Errorf("conf.Load(%s) failed: %s", inputs, err)
+ return false
+ }
+
+ prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
+ prog.Build()
+
+ var mainPkg *ssa.Package
+ var initialPkgs []*ssa.Package
+ for _, info := range iprog.InitialPackages() {
+ if info.Pkg.Path() == "runtime" {
+ continue // not an initial package
+ }
+ p := prog.Package(info.Pkg)
+ initialPkgs = append(initialPkgs, p)
+ if mainPkg == nil && p.Func("main") != nil {
+ mainPkg = p
+ }
+ }
+ if mainPkg == nil {
+ testmainPkg := prog.CreateTestMainPackage(initialPkgs...)
+ if testmainPkg == nil {
+ t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg)
+ return false
+ }
+ if testmainPkg.Func("main") == nil {
+ t.Errorf("synthetic testmain package has no main")
+ return false
+ }
+ mainPkg = testmainPkg
+ }
+
+ var out bytes.Buffer
+ interp.CapturedOutput = &out
+
+ hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input)
+ exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{})
+
+ // The definition of success varies with each file.
+ if err := success(exitCode, out.String()); err != nil {
+ t.Errorf("interp.Interpret(%s) failed: %s", inputs, err)
+ return false
+ }
+
+ hint = "" // call off the hounds
+
+ if false {
+ fmt.Println(input, time.Since(start)) // test profiling
+ }
+
+ return true
+}
+
+const slash = string(os.PathSeparator)
+
+func printFailures(failures []string) {
+ if failures != nil {
+ fmt.Println("The following tests failed:")
+ for _, f := range failures {
+ fmt.Printf("\t%s\n", f)
+ }
+ }
+}
+
+func success(exitcode int, output string) error {
+ if exitcode != 0 {
+ return fmt.Errorf("exit code was %d", exitcode)
+ }
+ if strings.Contains(output, "BUG") {
+ return fmt.Errorf("exited zero but output contained 'BUG'")
+ }
+ return nil
+}
+
+// TestTestdataFiles runs the interpreter on testdata/*.go.
+func TestTestdataFiles(t *testing.T) {
+ var failures []string
+ start := time.Now()
+ for _, input := range testdataTests {
+ if testing.Short() && time.Since(start) > 30*time.Second {
+ printFailures(failures)
+ t.Skipf("timeout - aborting test")
+ }
+ if !run(t, "testdata"+slash, input, success) {
+ failures = append(failures, input)
+ }
+ }
+ printFailures(failures)
+}
+
+// TestGorootTest runs the interpreter on $GOROOT/test/*.go.
+func TestGorootTest(t *testing.T) {
+ if testing.Short() {
+ t.Skip() // too slow (~30s)
+ }
+
+ var failures []string
+
+ for _, input := range gorootTestTests {
+ if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
+ failures = append(failures, input)
+ }
+ }
+ for _, input := range gorootSrcTests {
+ if !run(t, filepath.Join(build.Default.GOROOT, "src")+slash, input, success) {
+ failures = append(failures, input)
+ }
+ }
+ printFailures(failures)
+}
+
+// TestTestmainPackage runs the interpreter on a synthetic "testmain" package.
+func TestTestmainPackage(t *testing.T) {
+ if testing.Short() {
+ t.Skip() // too slow on some platforms
+ }
+
+ success := func(exitcode int, output string) error {
+ if exitcode == 0 {
+ return fmt.Errorf("unexpected success")
+ }
+ if !strings.Contains(output, "FAIL: TestFoo") {
+ return fmt.Errorf("missing failure log for TestFoo")
+ }
+ if !strings.Contains(output, "FAIL: TestBar") {
+ return fmt.Errorf("missing failure log for TestBar")
+ }
+ // TODO(adonovan): test benchmarks too
+ return nil
+ }
+ run(t, "testdata"+slash, "a_test.go", success)
+}
+
+// CreateTestMainPackage should return nil if there were no tests.
+func TestNullTestmainPackage(t *testing.T) {
+ var conf loader.Config
+ conf.CreateFromFilenames("", "testdata/b_test.go")
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Fatalf("CreatePackages failed: %s", err)
+ }
+ prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
+ mainPkg := prog.Package(iprog.Created[0].Pkg)
+ if mainPkg.Func("main") != nil {
+ t.Fatalf("unexpected main function")
+ }
+ if prog.CreateTestMainPackage(mainPkg) != nil {
+ t.Fatalf("CreateTestMainPackage returned non-nil")
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/interp_test.go b/go/src/golang.org/x/tools/go/ssa/interp/interp_test.go
index f36c23e..5163816 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/interp_test.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/interp_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// +build !android,!windows,!plan9
package interp_test
@@ -10,6 +12,7 @@
"bytes"
"fmt"
"go/build"
+ "go/types"
"os"
"path/filepath"
"strings"
@@ -20,7 +23,6 @@
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
// Each line contains a space-separated list of $GOROOT/test/
@@ -158,20 +160,19 @@
"encoding/hex",
// "encoding/pem", // TODO(adonovan): implement (reflect.Value).SetString
// "testing", // TODO(adonovan): implement runtime.Goexit correctly
- "unicode",
+ // "hash/crc32", // TODO(adonovan): implement hash/crc32.haveCLMUL
+ // "log", // TODO(adonovan): implement runtime.Callers correctly
// Too slow:
// "container/ring",
// "hash/adler32",
- // TODO(adonovan): packages with Examples require os.Pipe (unimplemented):
- // "hash/crc32",
- // "unicode/utf8",
- // "log",
- // "path",
- // "flag",
- // "encoding/csv"
- // "text/scanner"
+ "unicode/utf8",
+ "path",
+ "flag",
+ "encoding/csv",
+ "text/scanner",
+ "unicode",
}
type successPredicate func(exitcode int, output string) error
@@ -210,7 +211,7 @@
interp.CapturedOutput = nil
}()
- hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)
+ hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input)
iprog, err := conf.Load()
if err != nil {
@@ -219,7 +220,7 @@
}
prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
- prog.BuildAll()
+ prog.Build()
var mainPkg *ssa.Package
var initialPkgs []*ssa.Package
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/map.go b/go/src/golang.org/x/tools/go/ssa/interp/map.go
index 5dbaf0a..4c092b3 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/map.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/map.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package interp
// Custom hashtable atop map.
@@ -12,7 +14,7 @@
// concurrent map access.
import (
- "golang.org/x/tools/go/types"
+ "go/types"
)
type hashable interface {
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/map14.go b/go/src/golang.org/x/tools/go/ssa/interp/map14.go
new file mode 100644
index 0000000..a268c81
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/map14.go
@@ -0,0 +1,115 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package interp
+
+// Custom hashtable atop map.
+// For use when the key's equivalence relation is not consistent with ==.
+
+// The Go specification doesn't address the atomicity of map operations.
+// The FAQ states that an implementation is permitted to crash on
+// concurrent map access.
+
+import (
+ "golang.org/x/tools/go/types"
+)
+
+type hashable interface {
+ hash(t types.Type) int
+ eq(t types.Type, x interface{}) bool
+}
+
+type entry struct {
+ key hashable
+ value value
+ next *entry
+}
+
+// A hashtable atop the built-in map. Since each bucket contains
+// exactly one hash value, there's no need to perform hash-equality
+// tests when walking the linked list. Rehashing is done by the
+// underlying map.
+type hashmap struct {
+ keyType types.Type
+ table map[int]*entry
+ length int // number of entries in map
+}
+
+// makeMap returns an empty initialized map of key type kt,
+// preallocating space for reserve elements.
+func makeMap(kt types.Type, reserve int) value {
+ if usesBuiltinMap(kt) {
+ return make(map[value]value, reserve)
+ }
+ return &hashmap{keyType: kt, table: make(map[int]*entry, reserve)}
+}
+
+// delete removes the association for key k, if any.
+func (m *hashmap) delete(k hashable) {
+ if m != nil {
+ hash := k.hash(m.keyType)
+ head := m.table[hash]
+ if head != nil {
+ if k.eq(m.keyType, head.key) {
+ m.table[hash] = head.next
+ m.length--
+ return
+ }
+ prev := head
+ for e := head.next; e != nil; e = e.next {
+ if k.eq(m.keyType, e.key) {
+ prev.next = e.next
+ m.length--
+ return
+ }
+ prev = e
+ }
+ }
+ }
+}
+
+// lookup returns the value associated with key k, if present, or
+// value(nil) otherwise.
+func (m *hashmap) lookup(k hashable) value {
+ if m != nil {
+ hash := k.hash(m.keyType)
+ for e := m.table[hash]; e != nil; e = e.next {
+ if k.eq(m.keyType, e.key) {
+ return e.value
+ }
+ }
+ }
+ return nil
+}
+
+// insert updates the map to associate key k with value v. If there
+// was already an association for an eq() (though not necessarily ==)
+// k, the previous key remains in the map and its associated value is
+// updated.
+func (m *hashmap) insert(k hashable, v value) {
+ hash := k.hash(m.keyType)
+ head := m.table[hash]
+ for e := head; e != nil; e = e.next {
+ if k.eq(m.keyType, e.key) {
+ e.value = v
+ return
+ }
+ }
+ m.table[hash] = &entry{
+ key: k,
+ value: v,
+ next: head,
+ }
+ m.length++
+}
+
+// len returns the number of key/value associations in the map.
+func (m *hashmap) len() int {
+ if m != nil {
+ return m.length
+ }
+ return 0
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/ops.go b/go/src/golang.org/x/tools/go/ssa/interp/ops.go
index de89904..c7a0a40 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/ops.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/ops.go
@@ -2,19 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package interp
import (
"bytes"
"fmt"
+ exact "go/constant"
"go/token"
+ "go/types"
"strings"
"sync"
"unsafe"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// If the target program panics, the interpreter panics with this type.
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/ops14.go b/go/src/golang.org/x/tools/go/ssa/interp/ops14.go
new file mode 100644
index 0000000..2490dff
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/ops14.go
@@ -0,0 +1,1396 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package interp
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "strings"
+ "sync"
+ "unsafe"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+// If the target program panics, the interpreter panics with this type.
+type targetPanic struct {
+ v value
+}
+
+func (p targetPanic) String() string {
+ return toString(p.v)
+}
+
+// If the target program calls exit, the interpreter panics with this type.
+type exitPanic int
+
+// constValue returns the value of the constant with the
+// dynamic type tag appropriate for c.Type().
+func constValue(c *ssa.Const) value {
+ if c.IsNil() {
+ return zero(c.Type()) // typed nil
+ }
+
+ if t, ok := c.Type().Underlying().(*types.Basic); ok {
+ // TODO(adonovan): eliminate untyped constants from SSA form.
+ switch t.Kind() {
+ case types.Bool, types.UntypedBool:
+ return exact.BoolVal(c.Value)
+ case types.Int, types.UntypedInt:
+ // Assume sizeof(int) is same on host and target.
+ return int(c.Int64())
+ case types.Int8:
+ return int8(c.Int64())
+ case types.Int16:
+ return int16(c.Int64())
+ case types.Int32, types.UntypedRune:
+ return int32(c.Int64())
+ case types.Int64:
+ return c.Int64()
+ case types.Uint:
+ // Assume sizeof(uint) is same on host and target.
+ return uint(c.Uint64())
+ case types.Uint8:
+ return uint8(c.Uint64())
+ case types.Uint16:
+ return uint16(c.Uint64())
+ case types.Uint32:
+ return uint32(c.Uint64())
+ case types.Uint64:
+ return c.Uint64()
+ case types.Uintptr:
+ // Assume sizeof(uintptr) is same on host and target.
+ return uintptr(c.Uint64())
+ case types.Float32:
+ return float32(c.Float64())
+ case types.Float64, types.UntypedFloat:
+ return c.Float64()
+ case types.Complex64:
+ return complex64(c.Complex128())
+ case types.Complex128, types.UntypedComplex:
+ return c.Complex128()
+ case types.String, types.UntypedString:
+ if c.Value.Kind() == exact.String {
+ return exact.StringVal(c.Value)
+ }
+ return string(rune(c.Int64()))
+ }
+ }
+
+ panic(fmt.Sprintf("constValue: %s", c))
+}
+
+// asInt converts x, which must be an integer, to an int suitable for
+// use as a slice or array index or operand to make().
+func asInt(x value) int {
+ switch x := x.(type) {
+ case int:
+ return x
+ case int8:
+ return int(x)
+ case int16:
+ return int(x)
+ case int32:
+ return int(x)
+ case int64:
+ return int(x)
+ case uint:
+ return int(x)
+ case uint8:
+ return int(x)
+ case uint16:
+ return int(x)
+ case uint32:
+ return int(x)
+ case uint64:
+ return int(x)
+ case uintptr:
+ return int(x)
+ }
+ panic(fmt.Sprintf("cannot convert %T to int", x))
+}
+
+// asUint64 converts x, which must be an unsigned integer, to a uint64
+// suitable for use as a bitwise shift count.
+func asUint64(x value) uint64 {
+ switch x := x.(type) {
+ case uint:
+ return uint64(x)
+ case uint8:
+ return uint64(x)
+ case uint16:
+ return uint64(x)
+ case uint32:
+ return uint64(x)
+ case uint64:
+ return x
+ case uintptr:
+ return uint64(x)
+ }
+ panic(fmt.Sprintf("cannot convert %T to uint64", x))
+}
+
+// zero returns a new "zero" value of the specified type.
+func zero(t types.Type) value {
+ switch t := t.(type) {
+ case *types.Basic:
+ if t.Kind() == types.UntypedNil {
+ panic("untyped nil has no zero value")
+ }
+ if t.Info()&types.IsUntyped != 0 {
+ // TODO(adonovan): make it an invariant that
+ // this is unreachable. Currently some
+ // constants have 'untyped' types when they
+ // should be defaulted by the typechecker.
+ t = ssa.DefaultType(t).(*types.Basic)
+ }
+ switch t.Kind() {
+ case types.Bool:
+ return false
+ case types.Int:
+ return int(0)
+ case types.Int8:
+ return int8(0)
+ case types.Int16:
+ return int16(0)
+ case types.Int32:
+ return int32(0)
+ case types.Int64:
+ return int64(0)
+ case types.Uint:
+ return uint(0)
+ case types.Uint8:
+ return uint8(0)
+ case types.Uint16:
+ return uint16(0)
+ case types.Uint32:
+ return uint32(0)
+ case types.Uint64:
+ return uint64(0)
+ case types.Uintptr:
+ return uintptr(0)
+ case types.Float32:
+ return float32(0)
+ case types.Float64:
+ return float64(0)
+ case types.Complex64:
+ return complex64(0)
+ case types.Complex128:
+ return complex128(0)
+ case types.String:
+ return ""
+ case types.UnsafePointer:
+ return unsafe.Pointer(nil)
+ default:
+ panic(fmt.Sprint("zero for unexpected type:", t))
+ }
+ case *types.Pointer:
+ return (*value)(nil)
+ case *types.Array:
+ a := make(array, t.Len())
+ for i := range a {
+ a[i] = zero(t.Elem())
+ }
+ return a
+ case *types.Named:
+ return zero(t.Underlying())
+ case *types.Interface:
+ return iface{} // nil type, methodset and value
+ case *types.Slice:
+ return []value(nil)
+ case *types.Struct:
+ s := make(structure, t.NumFields())
+ for i := range s {
+ s[i] = zero(t.Field(i).Type())
+ }
+ return s
+ case *types.Tuple:
+ if t.Len() == 1 {
+ return zero(t.At(0).Type())
+ }
+ s := make(tuple, t.Len())
+ for i := range s {
+ s[i] = zero(t.At(i).Type())
+ }
+ return s
+ case *types.Chan:
+ return chan value(nil)
+ case *types.Map:
+ if usesBuiltinMap(t.Key()) {
+ return map[value]value(nil)
+ }
+ return (*hashmap)(nil)
+ case *types.Signature:
+ return (*ssa.Function)(nil)
+ }
+ panic(fmt.Sprint("zero: unexpected ", t))
+}
+
+// slice returns x[lo:hi:max]. Any of lo, hi and max may be nil.
+func slice(x, lo, hi, max value) value {
+ var Len, Cap int
+ switch x := x.(type) {
+ case string:
+ Len = len(x)
+ case []value:
+ Len = len(x)
+ Cap = cap(x)
+ case *value: // *array
+ a := (*x).(array)
+ Len = len(a)
+ Cap = cap(a)
+ }
+
+ l := 0
+ if lo != nil {
+ l = asInt(lo)
+ }
+
+ h := Len
+ if hi != nil {
+ h = asInt(hi)
+ }
+
+ m := Cap
+ if max != nil {
+ m = asInt(max)
+ }
+
+ switch x := x.(type) {
+ case string:
+ return x[l:h]
+ case []value:
+ return x[l:h:m]
+ case *value: // *array
+ a := (*x).(array)
+ return []value(a)[l:h:m]
+ }
+ panic(fmt.Sprintf("slice: unexpected X type: %T", x))
+}
+
+// lookup returns x[idx] where x is a map or string.
+func lookup(instr *ssa.Lookup, x, idx value) value {
+ switch x := x.(type) { // map or string
+ case map[value]value, *hashmap:
+ var v value
+ var ok bool
+ switch x := x.(type) {
+ case map[value]value:
+ v, ok = x[idx]
+ case *hashmap:
+ v = x.lookup(idx.(hashable))
+ ok = v != nil
+ }
+ if !ok {
+ v = zero(instr.X.Type().Underlying().(*types.Map).Elem())
+ }
+ if instr.CommaOk {
+ v = tuple{v, ok}
+ }
+ return v
+ case string:
+ return x[asInt(idx)]
+ }
+ panic(fmt.Sprintf("unexpected x type in Lookup: %T", x))
+}
+
+// binop implements all arithmetic and logical binary operators for
+// numeric datatypes and strings. Both operands must have identical
+// dynamic type.
+//
+func binop(op token.Token, t types.Type, x, y value) value {
+ switch op {
+ case token.ADD:
+ switch x.(type) {
+ case int:
+ return x.(int) + y.(int)
+ case int8:
+ return x.(int8) + y.(int8)
+ case int16:
+ return x.(int16) + y.(int16)
+ case int32:
+ return x.(int32) + y.(int32)
+ case int64:
+ return x.(int64) + y.(int64)
+ case uint:
+ return x.(uint) + y.(uint)
+ case uint8:
+ return x.(uint8) + y.(uint8)
+ case uint16:
+ return x.(uint16) + y.(uint16)
+ case uint32:
+ return x.(uint32) + y.(uint32)
+ case uint64:
+ return x.(uint64) + y.(uint64)
+ case uintptr:
+ return x.(uintptr) + y.(uintptr)
+ case float32:
+ return x.(float32) + y.(float32)
+ case float64:
+ return x.(float64) + y.(float64)
+ case complex64:
+ return x.(complex64) + y.(complex64)
+ case complex128:
+ return x.(complex128) + y.(complex128)
+ case string:
+ return x.(string) + y.(string)
+ }
+
+ case token.SUB:
+ switch x.(type) {
+ case int:
+ return x.(int) - y.(int)
+ case int8:
+ return x.(int8) - y.(int8)
+ case int16:
+ return x.(int16) - y.(int16)
+ case int32:
+ return x.(int32) - y.(int32)
+ case int64:
+ return x.(int64) - y.(int64)
+ case uint:
+ return x.(uint) - y.(uint)
+ case uint8:
+ return x.(uint8) - y.(uint8)
+ case uint16:
+ return x.(uint16) - y.(uint16)
+ case uint32:
+ return x.(uint32) - y.(uint32)
+ case uint64:
+ return x.(uint64) - y.(uint64)
+ case uintptr:
+ return x.(uintptr) - y.(uintptr)
+ case float32:
+ return x.(float32) - y.(float32)
+ case float64:
+ return x.(float64) - y.(float64)
+ case complex64:
+ return x.(complex64) - y.(complex64)
+ case complex128:
+ return x.(complex128) - y.(complex128)
+ }
+
+ case token.MUL:
+ switch x.(type) {
+ case int:
+ return x.(int) * y.(int)
+ case int8:
+ return x.(int8) * y.(int8)
+ case int16:
+ return x.(int16) * y.(int16)
+ case int32:
+ return x.(int32) * y.(int32)
+ case int64:
+ return x.(int64) * y.(int64)
+ case uint:
+ return x.(uint) * y.(uint)
+ case uint8:
+ return x.(uint8) * y.(uint8)
+ case uint16:
+ return x.(uint16) * y.(uint16)
+ case uint32:
+ return x.(uint32) * y.(uint32)
+ case uint64:
+ return x.(uint64) * y.(uint64)
+ case uintptr:
+ return x.(uintptr) * y.(uintptr)
+ case float32:
+ return x.(float32) * y.(float32)
+ case float64:
+ return x.(float64) * y.(float64)
+ case complex64:
+ return x.(complex64) * y.(complex64)
+ case complex128:
+ return x.(complex128) * y.(complex128)
+ }
+
+ case token.QUO:
+ switch x.(type) {
+ case int:
+ return x.(int) / y.(int)
+ case int8:
+ return x.(int8) / y.(int8)
+ case int16:
+ return x.(int16) / y.(int16)
+ case int32:
+ return x.(int32) / y.(int32)
+ case int64:
+ return x.(int64) / y.(int64)
+ case uint:
+ return x.(uint) / y.(uint)
+ case uint8:
+ return x.(uint8) / y.(uint8)
+ case uint16:
+ return x.(uint16) / y.(uint16)
+ case uint32:
+ return x.(uint32) / y.(uint32)
+ case uint64:
+ return x.(uint64) / y.(uint64)
+ case uintptr:
+ return x.(uintptr) / y.(uintptr)
+ case float32:
+ return x.(float32) / y.(float32)
+ case float64:
+ return x.(float64) / y.(float64)
+ case complex64:
+ return x.(complex64) / y.(complex64)
+ case complex128:
+ return x.(complex128) / y.(complex128)
+ }
+
+ case token.REM:
+ switch x.(type) {
+ case int:
+ return x.(int) % y.(int)
+ case int8:
+ return x.(int8) % y.(int8)
+ case int16:
+ return x.(int16) % y.(int16)
+ case int32:
+ return x.(int32) % y.(int32)
+ case int64:
+ return x.(int64) % y.(int64)
+ case uint:
+ return x.(uint) % y.(uint)
+ case uint8:
+ return x.(uint8) % y.(uint8)
+ case uint16:
+ return x.(uint16) % y.(uint16)
+ case uint32:
+ return x.(uint32) % y.(uint32)
+ case uint64:
+ return x.(uint64) % y.(uint64)
+ case uintptr:
+ return x.(uintptr) % y.(uintptr)
+ }
+
+ case token.AND:
+ switch x.(type) {
+ case int:
+ return x.(int) & y.(int)
+ case int8:
+ return x.(int8) & y.(int8)
+ case int16:
+ return x.(int16) & y.(int16)
+ case int32:
+ return x.(int32) & y.(int32)
+ case int64:
+ return x.(int64) & y.(int64)
+ case uint:
+ return x.(uint) & y.(uint)
+ case uint8:
+ return x.(uint8) & y.(uint8)
+ case uint16:
+ return x.(uint16) & y.(uint16)
+ case uint32:
+ return x.(uint32) & y.(uint32)
+ case uint64:
+ return x.(uint64) & y.(uint64)
+ case uintptr:
+ return x.(uintptr) & y.(uintptr)
+ }
+
+ case token.OR:
+ switch x.(type) {
+ case int:
+ return x.(int) | y.(int)
+ case int8:
+ return x.(int8) | y.(int8)
+ case int16:
+ return x.(int16) | y.(int16)
+ case int32:
+ return x.(int32) | y.(int32)
+ case int64:
+ return x.(int64) | y.(int64)
+ case uint:
+ return x.(uint) | y.(uint)
+ case uint8:
+ return x.(uint8) | y.(uint8)
+ case uint16:
+ return x.(uint16) | y.(uint16)
+ case uint32:
+ return x.(uint32) | y.(uint32)
+ case uint64:
+ return x.(uint64) | y.(uint64)
+ case uintptr:
+ return x.(uintptr) | y.(uintptr)
+ }
+
+ case token.XOR:
+ switch x.(type) {
+ case int:
+ return x.(int) ^ y.(int)
+ case int8:
+ return x.(int8) ^ y.(int8)
+ case int16:
+ return x.(int16) ^ y.(int16)
+ case int32:
+ return x.(int32) ^ y.(int32)
+ case int64:
+ return x.(int64) ^ y.(int64)
+ case uint:
+ return x.(uint) ^ y.(uint)
+ case uint8:
+ return x.(uint8) ^ y.(uint8)
+ case uint16:
+ return x.(uint16) ^ y.(uint16)
+ case uint32:
+ return x.(uint32) ^ y.(uint32)
+ case uint64:
+ return x.(uint64) ^ y.(uint64)
+ case uintptr:
+ return x.(uintptr) ^ y.(uintptr)
+ }
+
+ case token.AND_NOT:
+ switch x.(type) {
+ case int:
+ return x.(int) &^ y.(int)
+ case int8:
+ return x.(int8) &^ y.(int8)
+ case int16:
+ return x.(int16) &^ y.(int16)
+ case int32:
+ return x.(int32) &^ y.(int32)
+ case int64:
+ return x.(int64) &^ y.(int64)
+ case uint:
+ return x.(uint) &^ y.(uint)
+ case uint8:
+ return x.(uint8) &^ y.(uint8)
+ case uint16:
+ return x.(uint16) &^ y.(uint16)
+ case uint32:
+ return x.(uint32) &^ y.(uint32)
+ case uint64:
+ return x.(uint64) &^ y.(uint64)
+ case uintptr:
+ return x.(uintptr) &^ y.(uintptr)
+ }
+
+ case token.SHL:
+ y := asUint64(y)
+ switch x.(type) {
+ case int:
+ return x.(int) << y
+ case int8:
+ return x.(int8) << y
+ case int16:
+ return x.(int16) << y
+ case int32:
+ return x.(int32) << y
+ case int64:
+ return x.(int64) << y
+ case uint:
+ return x.(uint) << y
+ case uint8:
+ return x.(uint8) << y
+ case uint16:
+ return x.(uint16) << y
+ case uint32:
+ return x.(uint32) << y
+ case uint64:
+ return x.(uint64) << y
+ case uintptr:
+ return x.(uintptr) << y
+ }
+
+ case token.SHR:
+ y := asUint64(y)
+ switch x.(type) {
+ case int:
+ return x.(int) >> y
+ case int8:
+ return x.(int8) >> y
+ case int16:
+ return x.(int16) >> y
+ case int32:
+ return x.(int32) >> y
+ case int64:
+ return x.(int64) >> y
+ case uint:
+ return x.(uint) >> y
+ case uint8:
+ return x.(uint8) >> y
+ case uint16:
+ return x.(uint16) >> y
+ case uint32:
+ return x.(uint32) >> y
+ case uint64:
+ return x.(uint64) >> y
+ case uintptr:
+ return x.(uintptr) >> y
+ }
+
+ case token.LSS:
+ switch x.(type) {
+ case int:
+ return x.(int) < y.(int)
+ case int8:
+ return x.(int8) < y.(int8)
+ case int16:
+ return x.(int16) < y.(int16)
+ case int32:
+ return x.(int32) < y.(int32)
+ case int64:
+ return x.(int64) < y.(int64)
+ case uint:
+ return x.(uint) < y.(uint)
+ case uint8:
+ return x.(uint8) < y.(uint8)
+ case uint16:
+ return x.(uint16) < y.(uint16)
+ case uint32:
+ return x.(uint32) < y.(uint32)
+ case uint64:
+ return x.(uint64) < y.(uint64)
+ case uintptr:
+ return x.(uintptr) < y.(uintptr)
+ case float32:
+ return x.(float32) < y.(float32)
+ case float64:
+ return x.(float64) < y.(float64)
+ case string:
+ return x.(string) < y.(string)
+ }
+
+ case token.LEQ:
+ switch x.(type) {
+ case int:
+ return x.(int) <= y.(int)
+ case int8:
+ return x.(int8) <= y.(int8)
+ case int16:
+ return x.(int16) <= y.(int16)
+ case int32:
+ return x.(int32) <= y.(int32)
+ case int64:
+ return x.(int64) <= y.(int64)
+ case uint:
+ return x.(uint) <= y.(uint)
+ case uint8:
+ return x.(uint8) <= y.(uint8)
+ case uint16:
+ return x.(uint16) <= y.(uint16)
+ case uint32:
+ return x.(uint32) <= y.(uint32)
+ case uint64:
+ return x.(uint64) <= y.(uint64)
+ case uintptr:
+ return x.(uintptr) <= y.(uintptr)
+ case float32:
+ return x.(float32) <= y.(float32)
+ case float64:
+ return x.(float64) <= y.(float64)
+ case string:
+ return x.(string) <= y.(string)
+ }
+
+ case token.EQL:
+ return eqnil(t, x, y)
+
+ case token.NEQ:
+ return !eqnil(t, x, y)
+
+ case token.GTR:
+ switch x.(type) {
+ case int:
+ return x.(int) > y.(int)
+ case int8:
+ return x.(int8) > y.(int8)
+ case int16:
+ return x.(int16) > y.(int16)
+ case int32:
+ return x.(int32) > y.(int32)
+ case int64:
+ return x.(int64) > y.(int64)
+ case uint:
+ return x.(uint) > y.(uint)
+ case uint8:
+ return x.(uint8) > y.(uint8)
+ case uint16:
+ return x.(uint16) > y.(uint16)
+ case uint32:
+ return x.(uint32) > y.(uint32)
+ case uint64:
+ return x.(uint64) > y.(uint64)
+ case uintptr:
+ return x.(uintptr) > y.(uintptr)
+ case float32:
+ return x.(float32) > y.(float32)
+ case float64:
+ return x.(float64) > y.(float64)
+ case string:
+ return x.(string) > y.(string)
+ }
+
+ case token.GEQ:
+ switch x.(type) {
+ case int:
+ return x.(int) >= y.(int)
+ case int8:
+ return x.(int8) >= y.(int8)
+ case int16:
+ return x.(int16) >= y.(int16)
+ case int32:
+ return x.(int32) >= y.(int32)
+ case int64:
+ return x.(int64) >= y.(int64)
+ case uint:
+ return x.(uint) >= y.(uint)
+ case uint8:
+ return x.(uint8) >= y.(uint8)
+ case uint16:
+ return x.(uint16) >= y.(uint16)
+ case uint32:
+ return x.(uint32) >= y.(uint32)
+ case uint64:
+ return x.(uint64) >= y.(uint64)
+ case uintptr:
+ return x.(uintptr) >= y.(uintptr)
+ case float32:
+ return x.(float32) >= y.(float32)
+ case float64:
+ return x.(float64) >= y.(float64)
+ case string:
+ return x.(string) >= y.(string)
+ }
+ }
+ panic(fmt.Sprintf("invalid binary op: %T %s %T", x, op, y))
+}
+
+// eqnil returns the comparison x == y using the equivalence relation
+// appropriate for type t.
+// If t is a reference type, at most one of x or y may be a nil value
+// of that type.
+//
+func eqnil(t types.Type, x, y value) bool {
+ switch t.Underlying().(type) {
+ case *types.Map, *types.Signature, *types.Slice:
+ // Since these types don't support comparison,
+ // one of the operands must be a literal nil.
+ switch x := x.(type) {
+ case *hashmap:
+ return (x != nil) == (y.(*hashmap) != nil)
+ case map[value]value:
+ return (x != nil) == (y.(map[value]value) != nil)
+ case *ssa.Function:
+ switch y := y.(type) {
+ case *ssa.Function:
+ return (x != nil) == (y != nil)
+ case *closure:
+ return true
+ }
+ case *closure:
+ return (x != nil) == (y.(*ssa.Function) != nil)
+ case []value:
+ return (x != nil) == (y.([]value) != nil)
+ }
+ panic(fmt.Sprintf("eqnil(%s): illegal dynamic type: %T", t, x))
+ }
+
+ return equals(t, x, y)
+}
+
+func unop(instr *ssa.UnOp, x value) value {
+ switch instr.Op {
+ case token.ARROW: // receive
+ v, ok := <-x.(chan value)
+ if !ok {
+ v = zero(instr.X.Type().Underlying().(*types.Chan).Elem())
+ }
+ if instr.CommaOk {
+ v = tuple{v, ok}
+ }
+ return v
+ case token.SUB:
+ switch x := x.(type) {
+ case int:
+ return -x
+ case int8:
+ return -x
+ case int16:
+ return -x
+ case int32:
+ return -x
+ case int64:
+ return -x
+ case uint:
+ return -x
+ case uint8:
+ return -x
+ case uint16:
+ return -x
+ case uint32:
+ return -x
+ case uint64:
+ return -x
+ case uintptr:
+ return -x
+ case float32:
+ return -x
+ case float64:
+ return -x
+ case complex64:
+ return -x
+ case complex128:
+ return -x
+ }
+ case token.MUL:
+ return load(deref(instr.X.Type()), x.(*value))
+ case token.NOT:
+ return !x.(bool)
+ case token.XOR:
+ switch x := x.(type) {
+ case int:
+ return ^x
+ case int8:
+ return ^x
+ case int16:
+ return ^x
+ case int32:
+ return ^x
+ case int64:
+ return ^x
+ case uint:
+ return ^x
+ case uint8:
+ return ^x
+ case uint16:
+ return ^x
+ case uint32:
+ return ^x
+ case uint64:
+ return ^x
+ case uintptr:
+ return ^x
+ }
+ }
+ panic(fmt.Sprintf("invalid unary op %s %T", instr.Op, x))
+}
+
+// typeAssert checks whether dynamic type of itf is instr.AssertedType.
+// It returns the extracted value on success, and panics on failure,
+// unless instr.CommaOk, in which case it always returns a "value,ok" tuple.
+//
+func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
+ var v value
+ err := ""
+ if itf.t == nil {
+ err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType)
+
+ } else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
+ v = itf
+ err = checkInterface(i, idst, itf)
+
+ } else if types.Identical(itf.t, instr.AssertedType) {
+ v = itf.v // extract value
+
+ } else {
+ err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType)
+ }
+
+ if err != "" {
+ if !instr.CommaOk {
+ panic(err)
+ }
+ return tuple{zero(instr.AssertedType), false}
+ }
+ if instr.CommaOk {
+ return tuple{v, true}
+ }
+ return v
+}
+
+// If CapturedOutput is non-nil, all writes by the interpreted program
+// to file descriptors 1 and 2 will also be written to CapturedOutput.
+//
+// (The $GOROOT/test system requires that the test be considered a
+// failure if "BUG" appears in the combined stdout/stderr output, even
+// if it exits zero. This is a global variable shared by all
+// interpreters in the same process.)
+//
+var CapturedOutput *bytes.Buffer
+var capturedOutputMu sync.Mutex
+
+// write writes bytes b to the target program's file descriptor fd.
+// The print/println built-ins and the write() system call funnel
+// through here so they can be captured by the test driver.
+func write(fd int, b []byte) (int, error) {
+ // TODO(adonovan): fix: on Windows, std{out,err} are not 1, 2.
+ if CapturedOutput != nil && (fd == 1 || fd == 2) {
+ capturedOutputMu.Lock()
+ CapturedOutput.Write(b) // ignore errors
+ capturedOutputMu.Unlock()
+ }
+ return syswrite(fd, b)
+}
+
+// callBuiltin interprets a call to builtin fn with arguments args,
+// returning its result.
+func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value {
+ switch fn.Name() {
+ case "append":
+ if len(args) == 1 {
+ return args[0]
+ }
+ if s, ok := args[1].(string); ok {
+ // append([]byte, ...string) []byte
+ arg0 := args[0].([]value)
+ for i := 0; i < len(s); i++ {
+ arg0 = append(arg0, s[i])
+ }
+ return arg0
+ }
+ // append([]T, ...[]T) []T
+ return append(args[0].([]value), args[1].([]value)...)
+
+ case "copy": // copy([]T, []T) int or copy([]byte, string) int
+ src := args[1]
+ if _, ok := src.(string); ok {
+ params := fn.Type().(*types.Signature).Params()
+ src = conv(params.At(0).Type(), params.At(1).Type(), src)
+ }
+ return copy(args[0].([]value), src.([]value))
+
+ case "close": // close(chan T)
+ close(args[0].(chan value))
+ return nil
+
+ case "delete": // delete(map[K]value, K)
+ switch m := args[0].(type) {
+ case map[value]value:
+ delete(m, args[1])
+ case *hashmap:
+ m.delete(args[1].(hashable))
+ default:
+ panic(fmt.Sprintf("illegal map type: %T", m))
+ }
+ return nil
+
+ case "print", "println": // print(any, ...)
+ ln := fn.Name() == "println"
+ var buf bytes.Buffer
+ for i, arg := range args {
+ if i > 0 && ln {
+ buf.WriteRune(' ')
+ }
+ buf.WriteString(toString(arg))
+ }
+ if ln {
+ buf.WriteRune('\n')
+ }
+ write(1, buf.Bytes())
+ return nil
+
+ case "len":
+ switch x := args[0].(type) {
+ case string:
+ return len(x)
+ case array:
+ return len(x)
+ case *value:
+ return len((*x).(array))
+ case []value:
+ return len(x)
+ case map[value]value:
+ return len(x)
+ case *hashmap:
+ return x.len()
+ case chan value:
+ return len(x)
+ default:
+ panic(fmt.Sprintf("len: illegal operand: %T", x))
+ }
+
+ case "cap":
+ switch x := args[0].(type) {
+ case array:
+ return cap(x)
+ case *value:
+ return cap((*x).(array))
+ case []value:
+ return cap(x)
+ case chan value:
+ return cap(x)
+ default:
+ panic(fmt.Sprintf("cap: illegal operand: %T", x))
+ }
+
+ case "real":
+ switch c := args[0].(type) {
+ case complex64:
+ return real(c)
+ case complex128:
+ return real(c)
+ default:
+ panic(fmt.Sprintf("real: illegal operand: %T", c))
+ }
+
+ case "imag":
+ switch c := args[0].(type) {
+ case complex64:
+ return imag(c)
+ case complex128:
+ return imag(c)
+ default:
+ panic(fmt.Sprintf("imag: illegal operand: %T", c))
+ }
+
+ case "complex":
+ switch f := args[0].(type) {
+ case float32:
+ return complex(f, args[1].(float32))
+ case float64:
+ return complex(f, args[1].(float64))
+ default:
+ panic(fmt.Sprintf("complex: illegal operand: %T", f))
+ }
+
+ case "panic":
+ // ssa.Panic handles most cases; this is only for "go
+ // panic" or "defer panic".
+ panic(targetPanic{args[0]})
+
+ case "recover":
+ return doRecover(caller)
+
+ case "ssa:wrapnilchk":
+ recv := args[0]
+ if recv.(*value) == nil {
+ recvType := args[1]
+ methodName := args[2]
+ panic(fmt.Sprintf("value method (%s).%s called using nil *%s pointer",
+ recvType, methodName, recvType))
+ }
+ return recv
+ }
+
+ panic("unknown built-in: " + fn.Name())
+}
+
+func rangeIter(x value, t types.Type) iter {
+ switch x := x.(type) {
+ case map[value]value:
+ // TODO(adonovan): fix: leaks goroutines and channels
+ // on each incomplete map iteration. We need to open
+ // up an iteration interface using the
+ // reflect.(Value).MapKeys machinery.
+ it := make(mapIter)
+ go func() {
+ for k, v := range x {
+ it <- [2]value{k, v}
+ }
+ close(it)
+ }()
+ return it
+ case *hashmap:
+ // TODO(adonovan): fix: leaks goroutines and channels
+ // on each incomplete map iteration. We need to open
+ // up an iteration interface using the
+ // reflect.(Value).MapKeys machinery.
+ it := make(mapIter)
+ go func() {
+ for _, e := range x.table {
+ for e != nil {
+ it <- [2]value{e.key, e.value}
+ e = e.next
+ }
+ }
+ close(it)
+ }()
+ return it
+ case string:
+ return &stringIter{Reader: strings.NewReader(x)}
+ }
+ panic(fmt.Sprintf("cannot range over %T", x))
+}
+
+// widen widens a basic typed value x to the widest type of its
+// category, one of:
+// bool, int64, uint64, float64, complex128, string.
+// This is inefficient but reduces the size of the cross-product of
+// cases we have to consider.
+//
+func widen(x value) value {
+ switch y := x.(type) {
+ case bool, int64, uint64, float64, complex128, string, unsafe.Pointer:
+ return x
+ case int:
+ return int64(y)
+ case int8:
+ return int64(y)
+ case int16:
+ return int64(y)
+ case int32:
+ return int64(y)
+ case uint:
+ return uint64(y)
+ case uint8:
+ return uint64(y)
+ case uint16:
+ return uint64(y)
+ case uint32:
+ return uint64(y)
+ case uintptr:
+ return uint64(y)
+ case float32:
+ return float64(y)
+ case complex64:
+ return complex128(y)
+ }
+ panic(fmt.Sprintf("cannot widen %T", x))
+}
+
+// conv converts the value x of type t_src to type t_dst and returns
+// the result.
+// Possible cases are described with the ssa.Convert operator.
+//
+func conv(t_dst, t_src types.Type, x value) value {
+ ut_src := t_src.Underlying()
+ ut_dst := t_dst.Underlying()
+
+ // Destination type is not an "untyped" type.
+ if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
+ panic("oops: conversion to 'untyped' type: " + b.String())
+ }
+
+ // Nor is it an interface type.
+ if _, ok := ut_dst.(*types.Interface); ok {
+ if _, ok := ut_src.(*types.Interface); ok {
+ panic("oops: Convert should be ChangeInterface")
+ } else {
+ panic("oops: Convert should be MakeInterface")
+ }
+ }
+
+ // Remaining conversions:
+ // + untyped string/number/bool constant to a specific
+ // representation.
+ // + conversions between non-complex numeric types.
+ // + conversions between complex numeric types.
+ // + integer/[]byte/[]rune -> string.
+ // + string -> []byte/[]rune.
+ //
+ // All are treated the same: first we extract the value to the
+ // widest representation (int64, uint64, float64, complex128,
+ // or string), then we convert it to the desired type.
+
+ switch ut_src := ut_src.(type) {
+ case *types.Pointer:
+ switch ut_dst := ut_dst.(type) {
+ case *types.Basic:
+ // *value to unsafe.Pointer?
+ if ut_dst.Kind() == types.UnsafePointer {
+ return unsafe.Pointer(x.(*value))
+ }
+ }
+
+ case *types.Slice:
+ // []byte or []rune -> string
+ // TODO(adonovan): fix: type B byte; conv([]B -> string).
+ switch ut_src.Elem().(*types.Basic).Kind() {
+ case types.Byte:
+ x := x.([]value)
+ b := make([]byte, 0, len(x))
+ for i := range x {
+ b = append(b, x[i].(byte))
+ }
+ return string(b)
+
+ case types.Rune:
+ x := x.([]value)
+ r := make([]rune, 0, len(x))
+ for i := range x {
+ r = append(r, x[i].(rune))
+ }
+ return string(r)
+ }
+
+ case *types.Basic:
+ x = widen(x)
+
+ // integer -> string?
+ // TODO(adonovan): fix: test integer -> named alias of string.
+ if ut_src.Info()&types.IsInteger != 0 {
+ if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String {
+ return string(asInt(x))
+ }
+ }
+
+ // string -> []rune, []byte or string?
+ if s, ok := x.(string); ok {
+ switch ut_dst := ut_dst.(type) {
+ case *types.Slice:
+ var res []value
+ // TODO(adonovan): fix: test named alias of rune, byte.
+ switch ut_dst.Elem().(*types.Basic).Kind() {
+ case types.Rune:
+ for _, r := range []rune(s) {
+ res = append(res, r)
+ }
+ return res
+ case types.Byte:
+ for _, b := range []byte(s) {
+ res = append(res, b)
+ }
+ return res
+ }
+ case *types.Basic:
+ if ut_dst.Kind() == types.String {
+ return x.(string)
+ }
+ }
+ break // fail: no other conversions for string
+ }
+
+ // unsafe.Pointer -> *value
+ if ut_src.Kind() == types.UnsafePointer {
+ // TODO(adonovan): this is wrong and cannot
+ // really be fixed with the current design.
+ //
+ // return (*value)(x.(unsafe.Pointer))
+ // creates a new pointer of a different
+ // type but the underlying interface value
+ // knows its "true" type and so cannot be
+ // meaningfully used through the new pointer.
+ //
+ // To make this work, the interpreter needs to
+ // simulate the memory layout of a real
+ // compiled implementation.
+ //
+ // To at least preserve type-safety, we'll
+ // just return the zero value of the
+ // destination type.
+ return zero(t_dst)
+ }
+
+ // Conversions between complex numeric types?
+ if ut_src.Info()&types.IsComplex != 0 {
+ switch ut_dst.(*types.Basic).Kind() {
+ case types.Complex64:
+ return complex64(x.(complex128))
+ case types.Complex128:
+ return x.(complex128)
+ }
+ break // fail: no other conversions for complex
+ }
+
+ // Conversions between non-complex numeric types?
+ if ut_src.Info()&types.IsNumeric != 0 {
+ kind := ut_dst.(*types.Basic).Kind()
+ switch x := x.(type) {
+ case int64: // signed integer -> numeric?
+ switch kind {
+ case types.Int:
+ return int(x)
+ case types.Int8:
+ return int8(x)
+ case types.Int16:
+ return int16(x)
+ case types.Int32:
+ return int32(x)
+ case types.Int64:
+ return int64(x)
+ case types.Uint:
+ return uint(x)
+ case types.Uint8:
+ return uint8(x)
+ case types.Uint16:
+ return uint16(x)
+ case types.Uint32:
+ return uint32(x)
+ case types.Uint64:
+ return uint64(x)
+ case types.Uintptr:
+ return uintptr(x)
+ case types.Float32:
+ return float32(x)
+ case types.Float64:
+ return float64(x)
+ }
+
+ case uint64: // unsigned integer -> numeric?
+ switch kind {
+ case types.Int:
+ return int(x)
+ case types.Int8:
+ return int8(x)
+ case types.Int16:
+ return int16(x)
+ case types.Int32:
+ return int32(x)
+ case types.Int64:
+ return int64(x)
+ case types.Uint:
+ return uint(x)
+ case types.Uint8:
+ return uint8(x)
+ case types.Uint16:
+ return uint16(x)
+ case types.Uint32:
+ return uint32(x)
+ case types.Uint64:
+ return uint64(x)
+ case types.Uintptr:
+ return uintptr(x)
+ case types.Float32:
+ return float32(x)
+ case types.Float64:
+ return float64(x)
+ }
+
+ case float64: // floating point -> numeric?
+ switch kind {
+ case types.Int:
+ return int(x)
+ case types.Int8:
+ return int8(x)
+ case types.Int16:
+ return int16(x)
+ case types.Int32:
+ return int32(x)
+ case types.Int64:
+ return int64(x)
+ case types.Uint:
+ return uint(x)
+ case types.Uint8:
+ return uint8(x)
+ case types.Uint16:
+ return uint16(x)
+ case types.Uint32:
+ return uint32(x)
+ case types.Uint64:
+ return uint64(x)
+ case types.Uintptr:
+ return uintptr(x)
+ case types.Float32:
+ return float32(x)
+ case types.Float64:
+ return float64(x)
+ }
+ }
+ }
+ }
+
+ panic(fmt.Sprintf("unsupported conversion: %s -> %s, dynamic type %T", t_src, t_dst, x))
+}
+
+// checkInterface checks that the method set of x implements the
+// interface itype.
+// On success it returns "", on failure, an error message.
+//
+func checkInterface(i *interpreter, itype *types.Interface, x iface) string {
+ if meth, _ := types.MissingMethod(x.t, itype, true); meth != nil {
+ return fmt.Sprintf("interface conversion: %v is not %v: missing method %s",
+ x.t, itype, meth.Name())
+ }
+ return "" // ok
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/reflect.go b/go/src/golang.org/x/tools/go/ssa/interp/reflect.go
index 468771b..48bb911 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/reflect.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/reflect.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package interp
// Emulated "reflect" package.
@@ -13,11 +15,11 @@
import (
"fmt"
"go/token"
+ "go/types"
"reflect"
"unsafe"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
type opaqueType struct {
@@ -520,7 +522,7 @@
func initReflect(i *interpreter) {
i.reflectPackage = &ssa.Package{
Prog: i.prog,
- Object: reflectTypesPackage,
+ Pkg: reflectTypesPackage,
Members: make(map[string]ssa.Member),
}
@@ -539,18 +541,18 @@
// provide fake source files. This would guarantee that no bad
// information leaks into other packages.
if r := i.prog.ImportedPackage("reflect"); r != nil {
- rV := r.Object.Scope().Lookup("Value").Type().(*types.Named)
+ rV := r.Pkg.Scope().Lookup("Value").Type().(*types.Named)
// delete bodies of the old methods
mset := i.prog.MethodSets.MethodSet(rV)
for j := 0; j < mset.Len(); j++ {
- i.prog.Method(mset.At(j)).Blocks = nil
+ i.prog.MethodValue(mset.At(j)).Blocks = nil
}
tEface := types.NewInterface(nil, nil).Complete()
rV.SetUnderlying(types.NewStruct([]*types.Var{
- types.NewField(token.NoPos, r.Object, "t", tEface, false), // a lie
- types.NewField(token.NoPos, r.Object, "v", tEface, false),
+ types.NewField(token.NoPos, r.Pkg, "t", tEface, false), // a lie
+ types.NewField(token.NoPos, r.Pkg, "v", tEface, false),
}, nil))
}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/reflect14.go b/go/src/golang.org/x/tools/go/ssa/interp/reflect14.go
new file mode 100644
index 0000000..9f42327
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/reflect14.go
@@ -0,0 +1,576 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package interp
+
+// Emulated "reflect" package.
+//
+// We completely replace the built-in "reflect" package.
+// The only thing clients can depend upon are that reflect.Type is an
+// interface and reflect.Value is an (opaque) struct.
+
+import (
+ "fmt"
+ "go/token"
+ "reflect"
+ "unsafe"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+type opaqueType struct {
+ types.Type
+ name string
+}
+
+func (t *opaqueType) String() string { return t.name }
+
+// A bogus "reflect" type-checker package. Shared across interpreters.
+var reflectTypesPackage = types.NewPackage("reflect", "reflect")
+
+// rtype is the concrete type the interpreter uses to implement the
+// reflect.Type interface.
+//
+// type rtype <opaque>
+var rtypeType = makeNamedType("rtype", &opaqueType{nil, "rtype"})
+
+// error is an (interpreted) named type whose underlying type is string.
+// The interpreter uses it for all implementations of the built-in error
+// interface that it creates.
+// We put it in the "reflect" package for expedience.
+//
+// type error string
+var errorType = makeNamedType("error", &opaqueType{nil, "error"})
+
+func makeNamedType(name string, underlying types.Type) *types.Named {
+ obj := types.NewTypeName(token.NoPos, reflectTypesPackage, name, nil)
+ return types.NewNamed(obj, underlying, nil)
+}
+
+func makeReflectValue(t types.Type, v value) value {
+ return structure{rtype{t}, v}
+}
+
+// Given a reflect.Value, returns its rtype.
+func rV2T(v value) rtype {
+ return v.(structure)[0].(rtype)
+}
+
+// Given a reflect.Value, returns the underlying interpreter value.
+func rV2V(v value) value {
+ return v.(structure)[1]
+}
+
+// makeReflectType boxes up an rtype in a reflect.Type interface.
+func makeReflectType(rt rtype) value {
+ return iface{rtypeType, rt}
+}
+
+func extÛ°reflectÛ°Init(fr *frame, args []value) value {
+ // Signature: func()
+ return nil
+}
+
+func extÛ°reflectÛ°rtypeÛ°Bits(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) int
+ rt := args[0].(rtype).t
+ basic, ok := rt.Underlying().(*types.Basic)
+ if !ok {
+ panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt))
+ }
+ return int(fr.i.sizes.Sizeof(basic)) * 8
+}
+
+func extÛ°reflectÛ°rtypeÛ°Elem(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) reflect.Type
+ return makeReflectType(rtype{args[0].(rtype).t.Underlying().(interface {
+ Elem() types.Type
+ }).Elem()})
+}
+
+func extÛ°reflectÛ°rtypeÛ°Field(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype, i int) reflect.StructField
+ st := args[0].(rtype).t.Underlying().(*types.Struct)
+ i := args[1].(int)
+ f := st.Field(i)
+ return structure{
+ f.Name(),
+ f.Pkg().Path(),
+ makeReflectType(rtype{f.Type()}),
+ st.Tag(i),
+ 0, // TODO(adonovan): offset
+ []value{}, // TODO(adonovan): indices
+ f.Anonymous(),
+ }
+}
+
+func extÛ°reflectÛ°rtypeÛ°In(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype, i int) int
+ i := args[1].(int)
+ return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Params().At(i).Type()})
+}
+
+func extÛ°reflectÛ°rtypeÛ°Kind(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) uint
+ return uint(reflectKind(args[0].(rtype).t))
+}
+
+func extÛ°reflectÛ°rtypeÛ°NumField(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) int
+ return args[0].(rtype).t.Underlying().(*types.Struct).NumFields()
+}
+
+func extÛ°reflectÛ°rtypeÛ°NumIn(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) int
+ return args[0].(rtype).t.(*types.Signature).Params().Len()
+}
+
+func extÛ°reflectÛ°rtypeÛ°NumMethod(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) int
+ return fr.i.prog.MethodSets.MethodSet(args[0].(rtype).t).Len()
+}
+
+func extÛ°reflectÛ°rtypeÛ°NumOut(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) int
+ return args[0].(rtype).t.(*types.Signature).Results().Len()
+}
+
+func extÛ°reflectÛ°rtypeÛ°Out(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype, i int) int
+ i := args[1].(int)
+ return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results().At(i).Type()})
+}
+
+func extÛ°reflectÛ°rtypeÛ°Size(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) uintptr
+ return uintptr(fr.i.sizes.Sizeof(args[0].(rtype).t))
+}
+
+func extÛ°reflectÛ°rtypeÛ°String(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) string
+ return args[0].(rtype).t.String()
+}
+
+func extÛ°reflectÛ°New(fr *frame, args []value) value {
+ // Signature: func (t reflect.Type) reflect.Value
+ t := args[0].(iface).v.(rtype).t
+ alloc := zero(t)
+ return makeReflectValue(types.NewPointer(t), &alloc)
+}
+
+func extÛ°reflectÛ°SliceOf(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) Type
+ return makeReflectType(rtype{types.NewSlice(args[0].(iface).v.(rtype).t)})
+}
+
+func extÛ°reflectÛ°TypeOf(fr *frame, args []value) value {
+ // Signature: func (t reflect.rtype) Type
+ return makeReflectType(rtype{args[0].(iface).t})
+}
+
+func extÛ°reflectÛ°ValueOf(fr *frame, args []value) value {
+ // Signature: func (interface{}) reflect.Value
+ itf := args[0].(iface)
+ return makeReflectValue(itf.t, itf.v)
+}
+
+func extÛ°reflectÛ°Zero(fr *frame, args []value) value {
+ // Signature: func (t reflect.Type) reflect.Value
+ t := args[0].(iface).v.(rtype).t
+ return makeReflectValue(t, zero(t))
+}
+
+func reflectKind(t types.Type) reflect.Kind {
+ switch t := t.(type) {
+ case *types.Named:
+ return reflectKind(t.Underlying())
+ case *types.Basic:
+ switch t.Kind() {
+ case types.Bool:
+ return reflect.Bool
+ case types.Int:
+ return reflect.Int
+ case types.Int8:
+ return reflect.Int8
+ case types.Int16:
+ return reflect.Int16
+ case types.Int32:
+ return reflect.Int32
+ case types.Int64:
+ return reflect.Int64
+ case types.Uint:
+ return reflect.Uint
+ case types.Uint8:
+ return reflect.Uint8
+ case types.Uint16:
+ return reflect.Uint16
+ case types.Uint32:
+ return reflect.Uint32
+ case types.Uint64:
+ return reflect.Uint64
+ case types.Uintptr:
+ return reflect.Uintptr
+ case types.Float32:
+ return reflect.Float32
+ case types.Float64:
+ return reflect.Float64
+ case types.Complex64:
+ return reflect.Complex64
+ case types.Complex128:
+ return reflect.Complex128
+ case types.String:
+ return reflect.String
+ case types.UnsafePointer:
+ return reflect.UnsafePointer
+ }
+ case *types.Array:
+ return reflect.Array
+ case *types.Chan:
+ return reflect.Chan
+ case *types.Signature:
+ return reflect.Func
+ case *types.Interface:
+ return reflect.Interface
+ case *types.Map:
+ return reflect.Map
+ case *types.Pointer:
+ return reflect.Ptr
+ case *types.Slice:
+ return reflect.Slice
+ case *types.Struct:
+ return reflect.Struct
+ }
+ panic(fmt.Sprint("unexpected type: ", t))
+}
+
+func extÛ°reflectÛ°ValueÛ°Kind(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) uint
+ return uint(reflectKind(rV2T(args[0]).t))
+}
+
+func extÛ°reflectÛ°ValueÛ°String(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) string
+ return toString(rV2V(args[0]))
+}
+
+func extÛ°reflectÛ°ValueÛ°Type(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) reflect.Type
+ return makeReflectType(rV2T(args[0]))
+}
+
+func extÛ°reflectÛ°ValueÛ°Uint(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) uint64
+ switch v := rV2V(args[0]).(type) {
+ case uint:
+ return uint64(v)
+ case uint8:
+ return uint64(v)
+ case uint16:
+ return uint64(v)
+ case uint32:
+ return uint64(v)
+ case uint64:
+ return uint64(v)
+ case uintptr:
+ return uint64(v)
+ }
+ panic("reflect.Value.Uint")
+}
+
+func extÛ°reflectÛ°ValueÛ°Len(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) int
+ switch v := rV2V(args[0]).(type) {
+ case string:
+ return len(v)
+ case array:
+ return len(v)
+ case chan value:
+ return cap(v)
+ case []value:
+ return len(v)
+ case *hashmap:
+ return v.len()
+ case map[value]value:
+ return len(v)
+ default:
+ panic(fmt.Sprintf("reflect.(Value).Len(%v)", v))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°MapIndex(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) Value
+ tValue := rV2T(args[0]).t.Underlying().(*types.Map).Key()
+ k := rV2V(args[1])
+ switch m := rV2V(args[0]).(type) {
+ case map[value]value:
+ if v, ok := m[k]; ok {
+ return makeReflectValue(tValue, v)
+ }
+
+ case *hashmap:
+ if v := m.lookup(k.(hashable)); v != nil {
+ return makeReflectValue(tValue, v)
+ }
+
+ default:
+ panic(fmt.Sprintf("(reflect.Value).MapIndex(%T, %T)", m, k))
+ }
+ return makeReflectValue(nil, nil)
+}
+
+func extÛ°reflectÛ°ValueÛ°MapKeys(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) []Value
+ var keys []value
+ tKey := rV2T(args[0]).t.Underlying().(*types.Map).Key()
+ switch v := rV2V(args[0]).(type) {
+ case map[value]value:
+ for k := range v {
+ keys = append(keys, makeReflectValue(tKey, k))
+ }
+
+ case *hashmap:
+ for _, e := range v.table {
+ for ; e != nil; e = e.next {
+ keys = append(keys, makeReflectValue(tKey, e.key))
+ }
+ }
+
+ default:
+ panic(fmt.Sprintf("(reflect.Value).MapKeys(%T)", v))
+ }
+ return keys
+}
+
+func extÛ°reflectÛ°ValueÛ°NumField(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) int
+ return len(rV2V(args[0]).(structure))
+}
+
+func extÛ°reflectÛ°ValueÛ°NumMethod(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) int
+ return fr.i.prog.MethodSets.MethodSet(rV2T(args[0]).t).Len()
+}
+
+func extÛ°reflectÛ°ValueÛ°Pointer(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value) uintptr
+ switch v := rV2V(args[0]).(type) {
+ case *value:
+ return uintptr(unsafe.Pointer(v))
+ case chan value:
+ return reflect.ValueOf(v).Pointer()
+ case []value:
+ return reflect.ValueOf(v).Pointer()
+ case *hashmap:
+ return reflect.ValueOf(v.table).Pointer()
+ case map[value]value:
+ return reflect.ValueOf(v).Pointer()
+ case *ssa.Function:
+ return uintptr(unsafe.Pointer(v))
+ case *closure:
+ return uintptr(unsafe.Pointer(v))
+ default:
+ panic(fmt.Sprintf("reflect.(Value).Pointer(%T)", v))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Index(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value, i int) Value
+ i := args[1].(int)
+ t := rV2T(args[0]).t.Underlying()
+ switch v := rV2V(args[0]).(type) {
+ case array:
+ return makeReflectValue(t.(*types.Array).Elem(), v[i])
+ case []value:
+ return makeReflectValue(t.(*types.Slice).Elem(), v[i])
+ default:
+ panic(fmt.Sprintf("reflect.(Value).Index(%T)", v))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Bool(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) bool
+ return rV2V(args[0]).(bool)
+}
+
+func extÛ°reflectÛ°ValueÛ°CanAddr(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value) bool
+ // Always false for our representation.
+ return false
+}
+
+func extÛ°reflectÛ°ValueÛ°CanInterface(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value) bool
+ // Always true for our representation.
+ return true
+}
+
+func extÛ°reflectÛ°ValueÛ°Elem(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value) reflect.Value
+ switch x := rV2V(args[0]).(type) {
+ case iface:
+ return makeReflectValue(x.t, x.v)
+ case *value:
+ return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x)
+ default:
+ panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°Field(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value, i int) reflect.Value
+ v := args[0]
+ i := args[1].(int)
+ return makeReflectValue(rV2T(v).t.Underlying().(*types.Struct).Field(i).Type(), rV2V(v).(structure)[i])
+}
+
+func extÛ°reflectÛ°ValueÛ°Float(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) float64
+ switch v := rV2V(args[0]).(type) {
+ case float32:
+ return float64(v)
+ case float64:
+ return float64(v)
+ }
+ panic("reflect.Value.Float")
+}
+
+func extÛ°reflectÛ°ValueÛ°Interface(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value) interface{}
+ return extÛ°reflectÛ°valueInterface(fr, args)
+}
+
+func extÛ°reflectÛ°ValueÛ°Int(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) int64
+ switch x := rV2V(args[0]).(type) {
+ case int:
+ return int64(x)
+ case int8:
+ return int64(x)
+ case int16:
+ return int64(x)
+ case int32:
+ return int64(x)
+ case int64:
+ return x
+ default:
+ panic(fmt.Sprintf("reflect.(Value).Int(%T)", x))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°IsNil(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) bool
+ switch x := rV2V(args[0]).(type) {
+ case *value:
+ return x == nil
+ case chan value:
+ return x == nil
+ case map[value]value:
+ return x == nil
+ case *hashmap:
+ return x == nil
+ case iface:
+ return x.t == nil
+ case []value:
+ return x == nil
+ case *ssa.Function:
+ return x == nil
+ case *ssa.Builtin:
+ return x == nil
+ case *closure:
+ return x == nil
+ default:
+ panic(fmt.Sprintf("reflect.(Value).IsNil(%T)", x))
+ }
+}
+
+func extÛ°reflectÛ°ValueÛ°IsValid(fr *frame, args []value) value {
+ // Signature: func (reflect.Value) bool
+ return rV2V(args[0]) != nil
+}
+
+func extÛ°reflectÛ°ValueÛ°Set(fr *frame, args []value) value {
+ // TODO(adonovan): implement.
+ return nil
+}
+
+func extÛ°reflectÛ°valueInterface(fr *frame, args []value) value {
+ // Signature: func (v reflect.Value, safe bool) interface{}
+ v := args[0].(structure)
+ return iface{rV2T(v).t, rV2V(v)}
+}
+
+func extÛ°reflectÛ°errorÛ°Error(fr *frame, args []value) value {
+ return args[0]
+}
+
+// newMethod creates a new method of the specified name, package and receiver type.
+func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function {
+ // TODO(adonovan): fix: hack: currently the only part of Signature
+ // that is needed is the "pointerness" of Recv.Type, and for
+ // now, we'll set it to always be false since we're only
+ // concerned with rtype. Encapsulate this better.
+ sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false)
+ fn := pkg.Prog.NewFunction(name, sig, "fake reflect method")
+ fn.Pkg = pkg
+ return fn
+}
+
+func initReflect(i *interpreter) {
+ i.reflectPackage = &ssa.Package{
+ Prog: i.prog,
+ Pkg: reflectTypesPackage,
+ Members: make(map[string]ssa.Member),
+ }
+
+ // Clobber the type-checker's notion of reflect.Value's
+ // underlying type so that it more closely matches the fake one
+ // (at least in the number of fields---we lie about the type of
+ // the rtype field).
+ //
+ // We must ensure that calls to (ssa.Value).Type() return the
+ // fake type so that correct "shape" is used when allocating
+ // variables, making zero values, loading, and storing.
+ //
+ // TODO(adonovan): obviously this is a hack. We need a cleaner
+ // way to fake the reflect package (almost---DeepEqual is fine).
+ // One approach would be not to even load its source code, but
+ // provide fake source files. This would guarantee that no bad
+ // information leaks into other packages.
+ if r := i.prog.ImportedPackage("reflect"); r != nil {
+ rV := r.Pkg.Scope().Lookup("Value").Type().(*types.Named)
+
+ // delete bodies of the old methods
+ mset := i.prog.MethodSets.MethodSet(rV)
+ for j := 0; j < mset.Len(); j++ {
+ i.prog.MethodValue(mset.At(j)).Blocks = nil
+ }
+
+ tEface := types.NewInterface(nil, nil).Complete()
+ rV.SetUnderlying(types.NewStruct([]*types.Var{
+ types.NewField(token.NoPos, r.Pkg, "t", tEface, false), // a lie
+ types.NewField(token.NoPos, r.Pkg, "v", tEface, false),
+ }, nil))
+ }
+
+ i.rtypeMethods = methodSet{
+ "Bits": newMethod(i.reflectPackage, rtypeType, "Bits"),
+ "Elem": newMethod(i.reflectPackage, rtypeType, "Elem"),
+ "Field": newMethod(i.reflectPackage, rtypeType, "Field"),
+ "In": newMethod(i.reflectPackage, rtypeType, "In"),
+ "Kind": newMethod(i.reflectPackage, rtypeType, "Kind"),
+ "NumField": newMethod(i.reflectPackage, rtypeType, "NumField"),
+ "NumIn": newMethod(i.reflectPackage, rtypeType, "NumIn"),
+ "NumMethod": newMethod(i.reflectPackage, rtypeType, "NumMethod"),
+ "NumOut": newMethod(i.reflectPackage, rtypeType, "NumOut"),
+ "Out": newMethod(i.reflectPackage, rtypeType, "Out"),
+ "Size": newMethod(i.reflectPackage, rtypeType, "Size"),
+ "String": newMethod(i.reflectPackage, rtypeType, "String"),
+ }
+ i.errorMethods = methodSet{
+ "Error": newMethod(i.reflectPackage, errorType, "Error"),
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/testdata/complit.go b/go/src/golang.org/x/tools/go/ssa/interp/testdata/complit.go
index 02f9916..7bec523 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/testdata/complit.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/testdata/complit.go
@@ -164,5 +164,21 @@
}
}
+// Regression test for https://github.com/golang/go/issues/13341:
+// within a map literal, if a key expression is a composite literal,
+// Go 1.5 allows its type to be omitted. An & operation may be implied.
+func init() {
+ type S struct{ x int }
+ // same as map[*S]bool{&S{x: 1}: true}
+ m := map[*S]bool{{x: 1}: true}
+ for s := range m {
+ if s.x != 1 {
+ panic(s) // wrong key
+ }
+ return
+ }
+ panic("map is empty")
+}
+
func main() {
}
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/value.go b/go/src/golang.org/x/tools/go/ssa/interp/value.go
index 2ab0c04..2194b01 100644
--- a/go/src/golang.org/x/tools/go/ssa/interp/value.go
+++ b/go/src/golang.org/x/tools/go/ssa/interp/value.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package interp
// Values
@@ -36,6 +38,7 @@
import (
"bytes"
"fmt"
+ "go/types"
"io"
"reflect"
"strings"
@@ -43,7 +46,6 @@
"unsafe"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/go/ssa/interp/value14.go b/go/src/golang.org/x/tools/go/ssa/interp/value14.go
new file mode 100644
index 0000000..f8fedd3
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/interp/value14.go
@@ -0,0 +1,499 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package interp
+
+// Values
+//
+// All interpreter values are "boxed" in the empty interface, value.
+// The range of possible dynamic types within value are:
+//
+// - bool
+// - numbers (all built-in int/float/complex types are distinguished)
+// - string
+// - map[value]value --- maps for which usesBuiltinMap(keyType)
+// *hashmap --- maps for which !usesBuiltinMap(keyType)
+// - chan value
+// - []value --- slices
+// - iface --- interfaces.
+// - structure --- structs. Fields are ordered and accessed by numeric indices.
+// - array --- arrays.
+// - *value --- pointers. Careful: *value is a distinct type from *array etc.
+// - *ssa.Function \
+// *ssa.Builtin } --- functions. A nil 'func' is always of type *ssa.Function.
+// *closure /
+// - tuple --- as returned by Return, Next, "value,ok" modes, etc.
+// - iter --- iterators from 'range' over map or string.
+// - bad --- a poison pill for locals that have gone out of scope.
+// - rtype -- the interpreter's concrete implementation of reflect.Type
+//
+// Note that nil is not on this list.
+//
+// Pay close attention to whether or not the dynamic type is a pointer.
+// The compiler cannot help you since value is an empty interface.
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+ "sync"
+ "unsafe"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+type value interface{}
+
+type tuple []value
+
+type array []value
+
+type iface struct {
+ t types.Type // never an "untyped" type
+ v value
+}
+
+type structure []value
+
+// For map, array, *array, slice, string or channel.
+type iter interface {
+ // next returns a Tuple (key, value, ok).
+ // key and value are unaliased, e.g. copies of the sequence element.
+ next() tuple
+}
+
+type closure struct {
+ Fn *ssa.Function
+ Env []value
+}
+
+type bad struct{}
+
+type rtype struct {
+ t types.Type
+}
+
+// Hash functions and equivalence relation:
+
+// hashString computes the FNV hash of s.
+func hashString(s string) int {
+ var h uint32
+ for i := 0; i < len(s); i++ {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return int(h)
+}
+
+var (
+ mu sync.Mutex
+ hasher = typeutil.MakeHasher()
+)
+
+// hashType returns a hash for t such that
+// types.Identical(x, y) => hashType(x) == hashType(y).
+func hashType(t types.Type) int {
+ mu.Lock()
+ h := int(hasher.Hash(t))
+ mu.Unlock()
+ return h
+}
+
+// usesBuiltinMap returns true if the built-in hash function and
+// equivalence relation for type t are consistent with those of the
+// interpreter's representation of type t. Such types are: all basic
+// types (bool, numbers, string), pointers and channels.
+//
+// usesBuiltinMap returns false for types that require a custom map
+// implementation: interfaces, arrays and structs.
+//
+// Panic ensues if t is an invalid map key type: function, map or slice.
+func usesBuiltinMap(t types.Type) bool {
+ switch t := t.(type) {
+ case *types.Basic, *types.Chan, *types.Pointer:
+ return true
+ case *types.Named:
+ return usesBuiltinMap(t.Underlying())
+ case *types.Interface, *types.Array, *types.Struct:
+ return false
+ }
+ panic(fmt.Sprintf("invalid map key type: %T", t))
+}
+
+func (x array) eq(t types.Type, _y interface{}) bool {
+ y := _y.(array)
+ tElt := t.Underlying().(*types.Array).Elem()
+ for i, xi := range x {
+ if !equals(tElt, xi, y[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func (x array) hash(t types.Type) int {
+ h := 0
+ tElt := t.Underlying().(*types.Array).Elem()
+ for _, xi := range x {
+ h += hash(tElt, xi)
+ }
+ return h
+}
+
+func (x structure) eq(t types.Type, _y interface{}) bool {
+ y := _y.(structure)
+ tStruct := t.Underlying().(*types.Struct)
+ for i, n := 0, tStruct.NumFields(); i < n; i++ {
+ if f := tStruct.Field(i); !f.Anonymous() {
+ if !equals(f.Type(), x[i], y[i]) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+func (x structure) hash(t types.Type) int {
+ tStruct := t.Underlying().(*types.Struct)
+ h := 0
+ for i, n := 0, tStruct.NumFields(); i < n; i++ {
+ if f := tStruct.Field(i); !f.Anonymous() {
+ h += hash(f.Type(), x[i])
+ }
+ }
+ return h
+}
+
+// nil-tolerant variant of types.Identical.
+func sameType(x, y types.Type) bool {
+ if x == nil {
+ return y == nil
+ }
+ return y != nil && types.Identical(x, y)
+}
+
+func (x iface) eq(t types.Type, _y interface{}) bool {
+ y := _y.(iface)
+ return sameType(x.t, y.t) && (x.t == nil || equals(x.t, x.v, y.v))
+}
+
+func (x iface) hash(_ types.Type) int {
+ return hashType(x.t)*8581 + hash(x.t, x.v)
+}
+
+func (x rtype) hash(_ types.Type) int {
+ return hashType(x.t)
+}
+
+func (x rtype) eq(_ types.Type, y interface{}) bool {
+ return types.Identical(x.t, y.(rtype).t)
+}
+
+// equals returns true iff x and y are equal according to Go's
+// linguistic equivalence relation for type t.
+// In a well-typed program, the dynamic types of x and y are
+// guaranteed equal.
+func equals(t types.Type, x, y value) bool {
+ switch x := x.(type) {
+ case bool:
+ return x == y.(bool)
+ case int:
+ return x == y.(int)
+ case int8:
+ return x == y.(int8)
+ case int16:
+ return x == y.(int16)
+ case int32:
+ return x == y.(int32)
+ case int64:
+ return x == y.(int64)
+ case uint:
+ return x == y.(uint)
+ case uint8:
+ return x == y.(uint8)
+ case uint16:
+ return x == y.(uint16)
+ case uint32:
+ return x == y.(uint32)
+ case uint64:
+ return x == y.(uint64)
+ case uintptr:
+ return x == y.(uintptr)
+ case float32:
+ return x == y.(float32)
+ case float64:
+ return x == y.(float64)
+ case complex64:
+ return x == y.(complex64)
+ case complex128:
+ return x == y.(complex128)
+ case string:
+ return x == y.(string)
+ case *value:
+ return x == y.(*value)
+ case chan value:
+ return x == y.(chan value)
+ case structure:
+ return x.eq(t, y)
+ case array:
+ return x.eq(t, y)
+ case iface:
+ return x.eq(t, y)
+ case rtype:
+ return x.eq(t, y)
+ }
+
+ // Since map, func and slice don't support comparison, this
+ // case is only reachable if one of x or y is literally nil
+ // (handled in eqnil) or via interface{} values.
+ panic(fmt.Sprintf("comparing uncomparable type %s", t))
+}
+
+// Returns an integer hash of x such that equals(x, y) => hash(x) == hash(y).
+func hash(t types.Type, x value) int {
+ switch x := x.(type) {
+ case bool:
+ if x {
+ return 1
+ }
+ return 0
+ case int:
+ return x
+ case int8:
+ return int(x)
+ case int16:
+ return int(x)
+ case int32:
+ return int(x)
+ case int64:
+ return int(x)
+ case uint:
+ return int(x)
+ case uint8:
+ return int(x)
+ case uint16:
+ return int(x)
+ case uint32:
+ return int(x)
+ case uint64:
+ return int(x)
+ case uintptr:
+ return int(x)
+ case float32:
+ return int(x)
+ case float64:
+ return int(x)
+ case complex64:
+ return int(real(x))
+ case complex128:
+ return int(real(x))
+ case string:
+ return hashString(x)
+ case *value:
+ return int(uintptr(unsafe.Pointer(x)))
+ case chan value:
+ return int(uintptr(reflect.ValueOf(x).Pointer()))
+ case structure:
+ return x.hash(t)
+ case array:
+ return x.hash(t)
+ case iface:
+ return x.hash(t)
+ case rtype:
+ return x.hash(t)
+ }
+ panic(fmt.Sprintf("%T is unhashable", x))
+}
+
+// reflect.Value struct values don't have a fixed shape, since the
+// payload can be a scalar or an aggregate depending on the instance.
+// So store (and load) can't simply use recursion over the shape of the
+// rhs value, or the lhs, to copy the value; we need the static type
+// information. (We can't make reflect.Value a new basic data type
+// because its "structness" is exposed to Go programs.)
+
+// load returns the value of type T in *addr.
+func load(T types.Type, addr *value) value {
+ switch T := T.Underlying().(type) {
+ case *types.Struct:
+ v := (*addr).(structure)
+ a := make(structure, len(v))
+ for i := range a {
+ a[i] = load(T.Field(i).Type(), &v[i])
+ }
+ return a
+ case *types.Array:
+ v := (*addr).(array)
+ a := make(array, len(v))
+ for i := range a {
+ a[i] = load(T.Elem(), &v[i])
+ }
+ return a
+ default:
+ return *addr
+ }
+}
+
+// store stores value v of type T into *addr.
+func store(T types.Type, addr *value, v value) {
+ switch T := T.Underlying().(type) {
+ case *types.Struct:
+ lhs := (*addr).(structure)
+ rhs := v.(structure)
+ for i := range lhs {
+ store(T.Field(i).Type(), &lhs[i], rhs[i])
+ }
+ case *types.Array:
+ lhs := (*addr).(array)
+ rhs := v.(array)
+ for i := range lhs {
+ store(T.Elem(), &lhs[i], rhs[i])
+ }
+ default:
+ *addr = v
+ }
+}
+
+// Prints in the style of built-in println.
+// (More or less; in gc println is actually a compiler intrinsic and
+// can distinguish println(1) from println(interface{}(1)).)
+func writeValue(buf *bytes.Buffer, v value) {
+ switch v := v.(type) {
+ case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string:
+ fmt.Fprintf(buf, "%v", v)
+
+ case map[value]value:
+ buf.WriteString("map[")
+ sep := ""
+ for k, e := range v {
+ buf.WriteString(sep)
+ sep = " "
+ writeValue(buf, k)
+ buf.WriteString(":")
+ writeValue(buf, e)
+ }
+ buf.WriteString("]")
+
+ case *hashmap:
+ buf.WriteString("map[")
+ sep := " "
+ for _, e := range v.table {
+ for e != nil {
+ buf.WriteString(sep)
+ sep = " "
+ writeValue(buf, e.key)
+ buf.WriteString(":")
+ writeValue(buf, e.value)
+ e = e.next
+ }
+ }
+ buf.WriteString("]")
+
+ case chan value:
+ fmt.Fprintf(buf, "%v", v) // (an address)
+
+ case *value:
+ if v == nil {
+ buf.WriteString("<nil>")
+ } else {
+ fmt.Fprintf(buf, "%p", v)
+ }
+
+ case iface:
+ fmt.Fprintf(buf, "(%s, ", v.t)
+ writeValue(buf, v.v)
+ buf.WriteString(")")
+
+ case structure:
+ buf.WriteString("{")
+ for i, e := range v {
+ if i > 0 {
+ buf.WriteString(" ")
+ }
+ writeValue(buf, e)
+ }
+ buf.WriteString("}")
+
+ case array:
+ buf.WriteString("[")
+ for i, e := range v {
+ if i > 0 {
+ buf.WriteString(" ")
+ }
+ writeValue(buf, e)
+ }
+ buf.WriteString("]")
+
+ case []value:
+ buf.WriteString("[")
+ for i, e := range v {
+ if i > 0 {
+ buf.WriteString(" ")
+ }
+ writeValue(buf, e)
+ }
+ buf.WriteString("]")
+
+ case *ssa.Function, *ssa.Builtin, *closure:
+ fmt.Fprintf(buf, "%p", v) // (an address)
+
+ case rtype:
+ buf.WriteString(v.t.String())
+
+ case tuple:
+ // Unreachable in well-formed Go programs
+ buf.WriteString("(")
+ for i, e := range v {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ writeValue(buf, e)
+ }
+ buf.WriteString(")")
+
+ default:
+ fmt.Fprintf(buf, "<%T>", v)
+ }
+}
+
+// Implements printing of Go values in the style of built-in println.
+func toString(v value) string {
+ var b bytes.Buffer
+ writeValue(&b, v)
+ return b.String()
+}
+
+// ------------------------------------------------------------------------
+// Iterators
+
+type stringIter struct {
+ *strings.Reader
+ i int
+}
+
+func (it *stringIter) next() tuple {
+ okv := make(tuple, 3)
+ ch, n, err := it.ReadRune()
+ ok := err != io.EOF
+ okv[0] = ok
+ if ok {
+ okv[1] = it.i
+ okv[2] = ch
+ }
+ it.i += n
+ return okv
+}
+
+type mapIter chan [2]value
+
+func (it mapIter) next() tuple {
+ kv, ok := <-it
+ return tuple{ok, kv[0], kv[1]}
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/lift.go b/go/src/golang.org/x/tools/go/ssa/lift.go
index 3771f61..722d086 100644
--- a/go/src/golang.org/x/tools/go/ssa/lift.go
+++ b/go/src/golang.org/x/tools/go/ssa/lift.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file defines the lifting pass which tries to "lift" Alloc
@@ -44,10 +46,9 @@
import (
"fmt"
"go/token"
+ "go/types"
"math/big"
"os"
-
- "golang.org/x/tools/go/types"
)
// If true, perform sanity checking and show diagnostic information at
diff --git a/go/src/golang.org/x/tools/go/ssa/lift14.go b/go/src/golang.org/x/tools/go/ssa/lift14.go
new file mode 100644
index 0000000..d57a85c
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/lift14.go
@@ -0,0 +1,601 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file defines the lifting pass which tries to "lift" Alloc
+// cells (new/local variables) into SSA registers, replacing loads
+// with the dominating stored value, eliminating loads and stores, and
+// inserting φ-nodes as needed.
+
+// Cited papers and resources:
+//
+// Ron Cytron et al. 1991. Efficiently computing SSA form...
+// http://doi.acm.org/10.1145/115372.115320
+//
+// Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm.
+// Software Practice and Experience 2001, 4:1-10.
+// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
+//
+// Daniel Berlin, llvmdev mailing list, 2012.
+// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
+// (Be sure to expand the whole thread.)
+
+// TODO(adonovan): opt: there are many optimizations worth evaluating, and
+// the conventional wisdom for SSA construction is that a simple
+// algorithm well engineered often beats those of better asymptotic
+// complexity on all but the most egregious inputs.
+//
+// Danny Berlin suggests that the Cooper et al. algorithm for
+// computing the dominance frontier is superior to Cytron et al.
+// Furthermore he recommends that rather than computing the DF for the
+// whole function then renaming all alloc cells, it may be cheaper to
+// compute the DF for each alloc cell separately and throw it away.
+//
+// Consider exploiting liveness information to avoid creating dead
+// φ-nodes which we then immediately remove.
+//
+// Integrate lifting with scalar replacement of aggregates (SRA) since
+// the two are synergistic.
+//
+// Also see many other "TODO: opt" suggestions in the code.
+
+import (
+ "fmt"
+ "go/token"
+ "math/big"
+ "os"
+
+ "golang.org/x/tools/go/types"
+)
+
+// If true, perform sanity checking and show diagnostic information at
+// each step of lifting. Very verbose.
+const debugLifting = false
+
+// domFrontier maps each block to the set of blocks in its dominance
+// frontier. The outer slice is conceptually a map keyed by
+// Block.Index. The inner slice is conceptually a set, possibly
+// containing duplicates.
+//
+// TODO(adonovan): opt: measure impact of dups; consider a packed bit
+// representation, e.g. big.Int, and bitwise parallel operations for
+// the union step in the Children loop.
+//
+// domFrontier's methods mutate the slice's elements but not its
+// length, so their receivers needn't be pointers.
+//
+type domFrontier [][]*BasicBlock
+
+func (df domFrontier) add(u, v *BasicBlock) {
+ p := &df[u.Index]
+ *p = append(*p, v)
+}
+
+// build builds the dominance frontier df for the dominator (sub)tree
+// rooted at u, using the Cytron et al. algorithm.
+//
+// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
+// by pruning the entire IDF computation, rather than merely pruning
+// the DF -> IDF step.
+func (df domFrontier) build(u *BasicBlock) {
+ // Encounter each node u in postorder of dom tree.
+ for _, child := range u.dom.children {
+ df.build(child)
+ }
+ for _, vb := range u.Succs {
+ if v := vb.dom; v.idom != u {
+ df.add(u, vb)
+ }
+ }
+ for _, w := range u.dom.children {
+ for _, vb := range df[w.Index] {
+ // TODO(adonovan): opt: use word-parallel bitwise union.
+ if v := vb.dom; v.idom != u {
+ df.add(u, vb)
+ }
+ }
+ }
+}
+
+func buildDomFrontier(fn *Function) domFrontier {
+ df := make(domFrontier, len(fn.Blocks))
+ df.build(fn.Blocks[0])
+ if fn.Recover != nil {
+ df.build(fn.Recover)
+ }
+ return df
+}
+
+func removeInstr(refs []Instruction, instr Instruction) []Instruction {
+ i := 0
+ for _, ref := range refs {
+ if ref == instr {
+ continue
+ }
+ refs[i] = ref
+ i++
+ }
+ for j := i; j != len(refs); j++ {
+ refs[j] = nil // aid GC
+ }
+ return refs[:i]
+}
+
+// lift attempts to replace local and new Allocs accessed only with
+// load/store by SSA registers, inserting φ-nodes where necessary.
+// The result is a program in classical pruned SSA form.
+//
+// Preconditions:
+// - fn has no dead blocks (blockopt has run).
+// - Def/use info (Operands and Referrers) is up-to-date.
+// - The dominator tree is up-to-date.
+//
+func lift(fn *Function) {
+ // TODO(adonovan): opt: lots of little optimizations may be
+ // worthwhile here, especially if they cause us to avoid
+ // buildDomFrontier. For example:
+ //
+ // - Alloc never loaded? Eliminate.
+ // - Alloc never stored? Replace all loads with a zero constant.
+ // - Alloc stored once? Replace loads with dominating store;
+ // don't forget that an Alloc is itself an effective store
+ // of zero.
+ // - Alloc used only within a single block?
+ // Use degenerate algorithm avoiding φ-nodes.
+ // - Consider synergy with scalar replacement of aggregates (SRA).
+ // e.g. *(&x.f) where x is an Alloc.
+ // Perhaps we'd get better results if we generated this as x.f
+ // i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
+ // Unclear.
+ //
+ // But we will start with the simplest correct code.
+ df := buildDomFrontier(fn)
+
+ if debugLifting {
+ title := false
+ for i, blocks := range df {
+ if blocks != nil {
+ if !title {
+ fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
+ title = true
+ }
+ fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
+ }
+ }
+ }
+
+ newPhis := make(newPhiMap)
+
+ // During this pass we will replace some BasicBlock.Instrs
+ // (allocs, loads and stores) with nil, keeping a count in
+ // BasicBlock.gaps. At the end we will reset Instrs to the
+ // concatenation of all non-dead newPhis and non-nil Instrs
+ // for the block, reusing the original array if space permits.
+
+ // While we're here, we also eliminate 'rundefers'
+ // instructions in functions that contain no 'defer'
+ // instructions.
+ usesDefer := false
+
+ // Determine which allocs we can lift and number them densely.
+ // The renaming phase uses this numbering for compact maps.
+ numAllocs := 0
+ for _, b := range fn.Blocks {
+ b.gaps = 0
+ b.rundefers = 0
+ for _, instr := range b.Instrs {
+ switch instr := instr.(type) {
+ case *Alloc:
+ index := -1
+ if liftAlloc(df, instr, newPhis) {
+ index = numAllocs
+ numAllocs++
+ }
+ instr.index = index
+ case *Defer:
+ usesDefer = true
+ case *RunDefers:
+ b.rundefers++
+ }
+ }
+ }
+
+ // renaming maps an alloc (keyed by index) to its replacement
+ // value. Initially the renaming contains nil, signifying the
+ // zero constant of the appropriate type; we construct the
+ // Const lazily at most once on each path through the domtree.
+ // TODO(adonovan): opt: cache per-function not per subtree.
+ renaming := make([]Value, numAllocs)
+
+ // Renaming.
+ rename(fn.Blocks[0], renaming, newPhis)
+
+ // Eliminate dead new phis, then prepend the live ones to each block.
+ for _, b := range fn.Blocks {
+
+ // Compress the newPhis slice to eliminate unused phis.
+ // TODO(adonovan): opt: compute liveness to avoid
+ // placing phis in blocks for which the alloc cell is
+ // not live.
+ nps := newPhis[b]
+ j := 0
+ for _, np := range nps {
+ if !phiIsLive(np.phi) {
+ // discard it, first removing it from referrers
+ for _, newval := range np.phi.Edges {
+ if refs := newval.Referrers(); refs != nil {
+ *refs = removeInstr(*refs, np.phi)
+ }
+ }
+ continue
+ }
+ nps[j] = np
+ j++
+ }
+ nps = nps[:j]
+
+ rundefersToKill := b.rundefers
+ if usesDefer {
+ rundefersToKill = 0
+ }
+
+ if j+b.gaps+rundefersToKill == 0 {
+ continue // fast path: no new phis or gaps
+ }
+
+ // Compact nps + non-nil Instrs into a new slice.
+ // TODO(adonovan): opt: compact in situ if there is
+ // sufficient space or slack in the slice.
+ dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
+ for i, np := range nps {
+ dst[i] = np.phi
+ }
+ for _, instr := range b.Instrs {
+ if instr == nil {
+ continue
+ }
+ if !usesDefer {
+ if _, ok := instr.(*RunDefers); ok {
+ continue
+ }
+ }
+ dst[j] = instr
+ j++
+ }
+ for i, np := range nps {
+ dst[i] = np.phi
+ }
+ b.Instrs = dst
+ }
+
+ // Remove any fn.Locals that were lifted.
+ j := 0
+ for _, l := range fn.Locals {
+ if l.index < 0 {
+ fn.Locals[j] = l
+ j++
+ }
+ }
+ // Nil out fn.Locals[j:] to aid GC.
+ for i := j; i < len(fn.Locals); i++ {
+ fn.Locals[i] = nil
+ }
+ fn.Locals = fn.Locals[:j]
+}
+
+func phiIsLive(phi *Phi) bool {
+ for _, instr := range *phi.Referrers() {
+ if instr == phi {
+ continue // self-refs don't count
+ }
+ if _, ok := instr.(*DebugRef); ok {
+ continue // debug refs don't count
+ }
+ return true
+ }
+ return false
+}
+
+type blockSet struct{ big.Int } // (inherit methods from Int)
+
+// add adds b to the set and returns true if the set changed.
+func (s *blockSet) add(b *BasicBlock) bool {
+ i := b.Index
+ if s.Bit(i) != 0 {
+ return false
+ }
+ s.SetBit(&s.Int, i, 1)
+ return true
+}
+
+// take removes an arbitrary element from a set s and
+// returns its index, or returns -1 if empty.
+func (s *blockSet) take() int {
+ l := s.BitLen()
+ for i := 0; i < l; i++ {
+ if s.Bit(i) == 1 {
+ s.SetBit(&s.Int, i, 0)
+ return i
+ }
+ }
+ return -1
+}
+
+// newPhi is a pair of a newly introduced φ-node and the lifted Alloc
+// it replaces.
+type newPhi struct {
+ phi *Phi
+ alloc *Alloc
+}
+
+// newPhiMap records for each basic block, the set of newPhis that
+// must be prepended to the block.
+type newPhiMap map[*BasicBlock][]newPhi
+
+// liftAlloc determines whether alloc can be lifted into registers,
+// and if so, it populates newPhis with all the φ-nodes it may require
+// and returns true.
+//
+func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
+ // Don't lift aggregates into registers, because we don't have
+ // a way to express their zero-constants.
+ switch deref(alloc.Type()).Underlying().(type) {
+ case *types.Array, *types.Struct:
+ return false
+ }
+
+ // Don't lift named return values in functions that defer
+ // calls that may recover from panic.
+ if fn := alloc.Parent(); fn.Recover != nil {
+ for _, nr := range fn.namedResults {
+ if nr == alloc {
+ return false
+ }
+ }
+ }
+
+ // Compute defblocks, the set of blocks containing a
+ // definition of the alloc cell.
+ var defblocks blockSet
+ for _, instr := range *alloc.Referrers() {
+ // Bail out if we discover the alloc is not liftable;
+ // the only operations permitted to use the alloc are
+ // loads/stores into the cell, and DebugRef.
+ switch instr := instr.(type) {
+ case *Store:
+ if instr.Val == alloc {
+ return false // address used as value
+ }
+ if instr.Addr != alloc {
+ panic("Alloc.Referrers is inconsistent")
+ }
+ defblocks.add(instr.Block())
+ case *UnOp:
+ if instr.Op != token.MUL {
+ return false // not a load
+ }
+ if instr.X != alloc {
+ panic("Alloc.Referrers is inconsistent")
+ }
+ case *DebugRef:
+ // ok
+ default:
+ return false // some other instruction
+ }
+ }
+ // The Alloc itself counts as a (zero) definition of the cell.
+ defblocks.add(alloc.Block())
+
+ if debugLifting {
+ fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
+ }
+
+ fn := alloc.Parent()
+
+ // Φ-insertion.
+ //
+ // What follows is the body of the main loop of the insert-φ
+ // function described by Cytron et al, but instead of using
+ // counter tricks, we just reset the 'hasAlready' and 'work'
+ // sets each iteration. These are bitmaps so it's pretty cheap.
+ //
+ // TODO(adonovan): opt: recycle slice storage for W,
+ // hasAlready, defBlocks across liftAlloc calls.
+ var hasAlready blockSet
+
+ // Initialize W and work to defblocks.
+ var work blockSet = defblocks // blocks seen
+ var W blockSet // blocks to do
+ W.Set(&defblocks.Int)
+
+ // Traverse iterated dominance frontier, inserting φ-nodes.
+ for i := W.take(); i != -1; i = W.take() {
+ u := fn.Blocks[i]
+ for _, v := range df[u.Index] {
+ if hasAlready.add(v) {
+ // Create φ-node.
+ // It will be prepended to v.Instrs later, if needed.
+ phi := &Phi{
+ Edges: make([]Value, len(v.Preds)),
+ Comment: alloc.Comment,
+ }
+ phi.pos = alloc.Pos()
+ phi.setType(deref(alloc.Type()))
+ phi.block = v
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
+ }
+ newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
+
+ if work.add(v) {
+ W.add(v)
+ }
+ }
+ }
+ }
+
+ return true
+}
+
+// replaceAll replaces all intraprocedural uses of x with y,
+// updating x.Referrers and y.Referrers.
+// Precondition: x.Referrers() != nil, i.e. x must be local to some function.
+//
+func replaceAll(x, y Value) {
+ var rands []*Value
+ pxrefs := x.Referrers()
+ pyrefs := y.Referrers()
+ for _, instr := range *pxrefs {
+ rands = instr.Operands(rands[:0]) // recycle storage
+ for _, rand := range rands {
+ if *rand != nil {
+ if *rand == x {
+ *rand = y
+ }
+ }
+ }
+ if pyrefs != nil {
+ *pyrefs = append(*pyrefs, instr) // dups ok
+ }
+ }
+ *pxrefs = nil // x is now unreferenced
+}
+
+// renamed returns the value to which alloc is being renamed,
+// constructing it lazily if it's the implicit zero initialization.
+//
+func renamed(renaming []Value, alloc *Alloc) Value {
+ v := renaming[alloc.index]
+ if v == nil {
+ v = zeroConst(deref(alloc.Type()))
+ renaming[alloc.index] = v
+ }
+ return v
+}
+
+// rename implements the (Cytron et al) SSA renaming algorithm, a
+// preorder traversal of the dominator tree replacing all loads of
+// Alloc cells with the value stored to that cell by the dominating
+// store instruction. For lifting, we need only consider loads,
+// stores and φ-nodes.
+//
+// renaming is a map from *Alloc (keyed by index number) to its
+// dominating stored value; newPhis[x] is the set of new φ-nodes to be
+// prepended to block x.
+//
+func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
+ // Each φ-node becomes the new name for its associated Alloc.
+ for _, np := range newPhis[u] {
+ phi := np.phi
+ alloc := np.alloc
+ renaming[alloc.index] = phi
+ }
+
+ // Rename loads and stores of allocs.
+ for i, instr := range u.Instrs {
+ switch instr := instr.(type) {
+ case *Alloc:
+ if instr.index >= 0 { // store of zero to Alloc cell
+ // Replace dominated loads by the zero value.
+ renaming[instr.index] = nil
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
+ }
+ // Delete the Alloc.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+
+ case *Store:
+ if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
+ // Replace dominated loads by the stored value.
+ renaming[alloc.index] = instr.Val
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
+ instr, instr.Val.Name())
+ }
+ // Remove the store from the referrer list of the stored value.
+ if refs := instr.Val.Referrers(); refs != nil {
+ *refs = removeInstr(*refs, instr)
+ }
+ // Delete the Store.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+
+ case *UnOp:
+ if instr.Op == token.MUL {
+ if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
+ newval := renamed(renaming, alloc)
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
+ instr.Name(), instr, newval.Name())
+ }
+ // Replace all references to
+ // the loaded value by the
+ // dominating stored value.
+ replaceAll(instr, newval)
+ // Delete the Load.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+ }
+
+ case *DebugRef:
+ if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell
+ if instr.IsAddr {
+ instr.X = renamed(renaming, alloc)
+ instr.IsAddr = false
+
+ // Add DebugRef to instr.X's referrers.
+ if refs := instr.X.Referrers(); refs != nil {
+ *refs = append(*refs, instr)
+ }
+ } else {
+ // A source expression denotes the address
+ // of an Alloc that was optimized away.
+ instr.X = nil
+
+ // Delete the DebugRef.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+ }
+ }
+ }
+
+ // For each φ-node in a CFG successor, rename the edge.
+ for _, v := range u.Succs {
+ phis := newPhis[v]
+ if len(phis) == 0 {
+ continue
+ }
+ i := v.predIndex(u)
+ for _, np := range phis {
+ phi := np.phi
+ alloc := np.alloc
+ newval := renamed(renaming, alloc)
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
+ phi.Name(), u, v, i, alloc.Name(), newval.Name())
+ }
+ phi.Edges[i] = newval
+ if prefs := newval.Referrers(); prefs != nil {
+ *prefs = append(*prefs, phi)
+ }
+ }
+ }
+
+ // Continue depth-first recursion over domtree, pushing a
+ // fresh copy of the renaming map for each subtree.
+ for _, v := range u.dom.children {
+ // TODO(adonovan): opt: avoid copy on final iteration; use destructive update.
+ r := make([]Value, len(renaming))
+ copy(r, renaming)
+ rename(v, r, newPhis)
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/lvalue.go b/go/src/golang.org/x/tools/go/ssa/lvalue.go
index 4284b1c..85e090f 100644
--- a/go/src/golang.org/x/tools/go/ssa/lvalue.go
+++ b/go/src/golang.org/x/tools/go/ssa/lvalue.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// lvalues are the union of addressable expressions and map-index
@@ -10,8 +12,7 @@
import (
"go/ast"
"go/token"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// An lvalue represents an assignable location that may appear on the
diff --git a/go/src/golang.org/x/tools/go/ssa/lvalue14.go b/go/src/golang.org/x/tools/go/ssa/lvalue14.go
new file mode 100644
index 0000000..597761b
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/lvalue14.go
@@ -0,0 +1,123 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// lvalues are the union of addressable expressions and map-index
+// expressions.
+
+import (
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/types"
+)
+
+// An lvalue represents an assignable location that may appear on the
+// left-hand side of an assignment. This is a generalization of a
+// pointer to permit updates to elements of maps.
+//
+type lvalue interface {
+ store(fn *Function, v Value) // stores v into the location
+ load(fn *Function) Value // loads the contents of the location
+ address(fn *Function) Value // address of the location
+ typ() types.Type // returns the type of the location
+}
+
+// An address is an lvalue represented by a true pointer.
+type address struct {
+ addr Value
+ pos token.Pos // source position
+ expr ast.Expr // source syntax of the value (not address) [debug mode]
+}
+
+func (a *address) load(fn *Function) Value {
+ load := emitLoad(fn, a.addr)
+ load.pos = a.pos
+ return load
+}
+
+func (a *address) store(fn *Function, v Value) {
+ store := emitStore(fn, a.addr, v, a.pos)
+ if a.expr != nil {
+ // store.Val is v, converted for assignability.
+ emitDebugRef(fn, a.expr, store.Val, false)
+ }
+}
+
+func (a *address) address(fn *Function) Value {
+ if a.expr != nil {
+ emitDebugRef(fn, a.expr, a.addr, true)
+ }
+ return a.addr
+}
+
+func (a *address) typ() types.Type {
+ return deref(a.addr.Type())
+}
+
+// An element is an lvalue represented by m[k], the location of an
+// element of a map or string. These locations are not addressable
+// since pointers cannot be formed from them, but they do support
+// load(), and in the case of maps, store().
+//
+type element struct {
+ m, k Value // map or string
+ t types.Type // map element type or string byte type
+ pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
+}
+
+func (e *element) load(fn *Function) Value {
+ l := &Lookup{
+ X: e.m,
+ Index: e.k,
+ }
+ l.setPos(e.pos)
+ l.setType(e.t)
+ return fn.emit(l)
+}
+
+func (e *element) store(fn *Function, v Value) {
+ up := &MapUpdate{
+ Map: e.m,
+ Key: e.k,
+ Value: emitConv(fn, v, e.t),
+ }
+ up.pos = e.pos
+ fn.emit(up)
+}
+
+func (e *element) address(fn *Function) Value {
+ panic("map/string elements are not addressable")
+}
+
+func (e *element) typ() types.Type {
+ return e.t
+}
+
+// A blank is a dummy variable whose name is "_".
+// It is not reified: loads are illegal and stores are ignored.
+//
+type blank struct{}
+
+func (bl blank) load(fn *Function) Value {
+ panic("blank.load is illegal")
+}
+
+func (bl blank) store(fn *Function, v Value) {
+ // no-op
+}
+
+func (bl blank) address(fn *Function) Value {
+ panic("blank var is not addressable")
+}
+
+func (bl blank) typ() types.Type {
+ // This should be the type of the blank Ident; the typechecker
+ // doesn't provide this yet, but fortunately, we don't need it
+ // yet either.
+ panic("blank.typ is unimplemented")
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/methods.go b/go/src/golang.org/x/tools/go/ssa/methods.go
index 12534de..7d1fb42 100644
--- a/go/src/golang.org/x/tools/go/ssa/methods.go
+++ b/go/src/golang.org/x/tools/go/ssa/methods.go
@@ -2,30 +2,28 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file defines utilities for population of method sets.
import (
"fmt"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
-// Method returns the Function implementing method sel, building
+// MethodValue returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
// abstract (interface) method.
//
// Precondition: sel.Kind() == MethodVal.
//
-// TODO(adonovan): rename this to MethodValue because of the
-// precondition, and for consistency with functions in source.go.
-//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
-func (prog *Program) Method(sel *types.Selection) *Function {
+func (prog *Program) MethodValue(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel))
}
@@ -52,7 +50,7 @@
if sel == nil {
panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
}
- return prog.Method(sel)
+ return prog.MethodValue(sel)
}
// methodSet contains the (concrete) methods of a non-interface type.
diff --git a/go/src/golang.org/x/tools/go/ssa/methods14.go b/go/src/golang.org/x/tools/go/ssa/methods14.go
new file mode 100644
index 0000000..7c2a40d
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/methods14.go
@@ -0,0 +1,242 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file defines utilities for population of method sets.
+
+import (
+ "fmt"
+
+ "golang.org/x/tools/go/types"
+)
+
+// MethodValue returns the Function implementing method sel, building
+// wrapper methods on demand. It returns nil if sel denotes an
+// abstract (interface) method.
+//
+// Precondition: sel.Kind() == MethodVal.
+//
+// Thread-safe.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
+//
+func (prog *Program) MethodValue(sel *types.Selection) *Function {
+ if sel.Kind() != types.MethodVal {
+ panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel))
+ }
+ T := sel.Recv()
+ if isInterface(T) {
+ return nil // abstract method
+ }
+ if prog.mode&LogSource != 0 {
+ defer logStack("Method %s %v", T, sel)()
+ }
+
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ return prog.addMethod(prog.createMethodSet(T), sel)
+}
+
+// LookupMethod returns the implementation of the method of type T
+// identified by (pkg, name). It returns nil if the method exists but
+// is abstract, and panics if T has no such method.
+//
+func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
+ sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
+ if sel == nil {
+ panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
+ }
+ return prog.MethodValue(sel)
+}
+
+// methodSet contains the (concrete) methods of a non-interface type.
+type methodSet struct {
+ mapping map[string]*Function // populated lazily
+ complete bool // mapping contains all methods
+}
+
+// Precondition: !isInterface(T).
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+func (prog *Program) createMethodSet(T types.Type) *methodSet {
+ mset, ok := prog.methodSets.At(T).(*methodSet)
+ if !ok {
+ mset = &methodSet{mapping: make(map[string]*Function)}
+ prog.methodSets.Set(T, mset)
+ }
+ return mset
+}
+
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
+ if sel.Kind() == types.MethodExpr {
+ panic(sel)
+ }
+ id := sel.Obj().Id()
+ fn := mset.mapping[id]
+ if fn == nil {
+ obj := sel.Obj().(*types.Func)
+
+ needsPromotion := len(sel.Index()) > 1
+ needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
+ if needsPromotion || needsIndirection {
+ fn = makeWrapper(prog, sel)
+ } else {
+ fn = prog.declaredFunc(obj)
+ }
+ if fn.Signature.Recv() == nil {
+ panic(fn) // missing receiver
+ }
+ mset.mapping[id] = fn
+ }
+ return fn
+}
+
+// RuntimeTypes returns a new unordered slice containing all
+// concrete types in the program for which a complete (non-empty)
+// method set is required at run-time.
+//
+// Thread-safe.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
+//
+func (prog *Program) RuntimeTypes() []types.Type {
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ var res []types.Type
+ prog.methodSets.Iterate(func(T types.Type, v interface{}) {
+ if v.(*methodSet).complete {
+ res = append(res, T)
+ }
+ })
+ return res
+}
+
+// declaredFunc returns the concrete function/method denoted by obj.
+// Panic ensues if there is none.
+//
+func (prog *Program) declaredFunc(obj *types.Func) *Function {
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Function)
+ }
+ panic("no concrete method: " + obj.String())
+}
+
+// needMethodsOf ensures that runtime type information (including the
+// complete method set) is available for the specified type T and all
+// its subcomponents.
+//
+// needMethodsOf must be called for at least every type that is an
+// operand of some MakeInterface instruction, and for the type of
+// every exported package member.
+//
+// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+//
+// Thread-safe. (Called via emitConv from multiple builder goroutines.)
+//
+// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
+//
+func (prog *Program) needMethodsOf(T types.Type) {
+ prog.methodsMu.Lock()
+ prog.needMethods(T, false)
+ prog.methodsMu.Unlock()
+}
+
+// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+// Recursive case: skip => don't create methods for T.
+//
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+//
+func (prog *Program) needMethods(T types.Type, skip bool) {
+ // Each package maintains its own set of types it has visited.
+ if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
+ // needMethods(T) was previously called
+ if !prevSkip || skip {
+ return // already seen, with same or false 'skip' value
+ }
+ }
+ prog.runtimeTypes.Set(T, skip)
+
+ tmset := prog.MethodSets.MethodSet(T)
+
+ if !skip && !isInterface(T) && tmset.Len() > 0 {
+ // Create methods of T.
+ mset := prog.createMethodSet(T)
+ if !mset.complete {
+ mset.complete = true
+ n := tmset.Len()
+ for i := 0; i < n; i++ {
+ prog.addMethod(mset, tmset.At(i))
+ }
+ }
+ }
+
+ // Recursion over signatures of each method.
+ for i := 0; i < tmset.Len(); i++ {
+ sig := tmset.At(i).Type().(*types.Signature)
+ prog.needMethods(sig.Params(), false)
+ prog.needMethods(sig.Results(), false)
+ }
+
+ switch t := T.(type) {
+ case *types.Basic:
+ // nop
+
+ case *types.Interface:
+ // nop---handled by recursion over method set.
+
+ case *types.Pointer:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Slice:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Chan:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Map:
+ prog.needMethods(t.Key(), false)
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Signature:
+ if t.Recv() != nil {
+ panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
+ }
+ prog.needMethods(t.Params(), false)
+ prog.needMethods(t.Results(), false)
+
+ case *types.Named:
+ // A pointer-to-named type can be derived from a named
+ // type via reflection. It may have methods too.
+ prog.needMethods(types.NewPointer(T), false)
+
+ // Consider 'type T struct{S}' where S has methods.
+ // Reflection provides no way to get from T to struct{S},
+ // only to S, so the method set of struct{S} is unwanted,
+ // so set 'skip' flag during recursion.
+ prog.needMethods(t.Underlying(), true)
+
+ case *types.Array:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Struct:
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ prog.needMethods(t.Field(i).Type(), false)
+ }
+
+ case *types.Tuple:
+ for i, n := 0, t.Len(); i < n; i++ {
+ prog.needMethods(t.At(i).Type(), false)
+ }
+
+ default:
+ panic(T)
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/print.go b/go/src/golang.org/x/tools/go/ssa/print.go
index 88c31f6..55c9266 100644
--- a/go/src/golang.org/x/tools/go/ssa/print.go
+++ b/go/src/golang.org/x/tools/go/ssa/print.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file implements the String() methods for all Value and
@@ -10,11 +12,11 @@
import (
"bytes"
"fmt"
+ "go/types"
"io"
"reflect"
"sort"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -27,7 +29,7 @@
func relName(v Value, i Instruction) string {
var from *types.Package
if i != nil {
- from = i.Parent().pkgobj()
+ from = i.Parent().pkg()
}
switch v := v.(type) {
case Member: // *Function or *Global
@@ -45,8 +47,8 @@
func relString(m Member, from *types.Package) string {
// NB: not all globals have an Object (e.g. init$guard),
// so use Package().Object not Object.Package().
- if obj := m.Package().Object; obj != nil && obj != from {
- return fmt.Sprintf("%s.%s", obj.Path(), m.Name())
+ if pkg := m.Package().Pkg; pkg != nil && pkg != from {
+ return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
}
return m.Name()
}
@@ -57,12 +59,12 @@
// It never appears in disassembly, which uses Value.Name().
func (v *Parameter) String() string {
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *FreeVar) String() string {
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
}
@@ -77,7 +79,7 @@
if v.Heap {
op = "new"
}
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment)
}
@@ -147,7 +149,7 @@
}
func printConv(prefix string, v, x Value) string {
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("%s %s <- %s (%s)",
prefix,
relType(v.Type(), from),
@@ -177,7 +179,7 @@
}
func (v *MakeSlice) String() string {
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("make %s %s %s",
relType(v.Type(), from),
relName(v.Len, v),
@@ -209,12 +211,12 @@
if v.Reserve != nil {
res = relName(v.Reserve, v)
}
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
}
func (v *MakeChan) String() string {
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
}
@@ -259,7 +261,7 @@
}
func (v *TypeAssert) String() string {
- from := v.Parent().pkgobj()
+ from := v.Parent().pkg()
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
}
@@ -366,7 +368,7 @@
}
func (p *Package) String() string {
- return "package " + p.Object.Path()
+ return "package " + p.Pkg.Path()
}
var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
@@ -391,7 +393,7 @@
names = append(names, name)
}
- from := p.Object
+ from := p.Pkg
sort.Strings(names)
for _, name := range names {
switch mem := p.Members[name].(type) {
diff --git a/go/src/golang.org/x/tools/go/ssa/print14.go b/go/src/golang.org/x/tools/go/ssa/print14.go
new file mode 100644
index 0000000..155d5ec
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/print14.go
@@ -0,0 +1,429 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file implements the String() methods for all Value and
+// Instruction types.
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// relName returns the name of v relative to i.
+// In most cases, this is identical to v.Name(), but references to
+// Functions (including methods) and Globals use RelString and
+// all types are displayed with relType, so that only cross-package
+// references are package-qualified.
+//
+func relName(v Value, i Instruction) string {
+ var from *types.Package
+ if i != nil {
+ from = i.Parent().pkg()
+ }
+ switch v := v.(type) {
+ case Member: // *Function or *Global
+ return v.RelString(from)
+ case *Const:
+ return v.RelString(from)
+ }
+ return v.Name()
+}
+
+func relType(t types.Type, from *types.Package) string {
+ return types.TypeString(t, types.RelativeTo(from))
+}
+
+func relString(m Member, from *types.Package) string {
+ // NB: not all globals have an Object (e.g. init$guard),
+ // so use Package().Object not Object.Package().
+ if pkg := m.Package().Pkg; pkg != nil && pkg != from {
+ return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
+ }
+ return m.Name()
+}
+
+// Value.String()
+//
+// This method is provided only for debugging.
+// It never appears in disassembly, which uses Value.Name().
+
+func (v *Parameter) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
+}
+
+func (v *FreeVar) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
+}
+
+func (v *Builtin) String() string {
+ return fmt.Sprintf("builtin %s", v.Name())
+}
+
+// Instruction.String()
+
+func (v *Alloc) String() string {
+ op := "local"
+ if v.Heap {
+ op = "new"
+ }
+ from := v.Parent().pkg()
+ return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment)
+}
+
+func (v *Phi) String() string {
+ var b bytes.Buffer
+ b.WriteString("phi [")
+ for i, edge := range v.Edges {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ // Be robust against malformed CFG.
+ block := -1
+ if v.block != nil && i < len(v.block.Preds) {
+ block = v.block.Preds[i].Index
+ }
+ fmt.Fprintf(&b, "%d: ", block)
+ edgeVal := "<nil>" // be robust
+ if edge != nil {
+ edgeVal = relName(edge, v)
+ }
+ b.WriteString(edgeVal)
+ }
+ b.WriteString("]")
+ if v.Comment != "" {
+ b.WriteString(" #")
+ b.WriteString(v.Comment)
+ }
+ return b.String()
+}
+
+func printCall(v *CallCommon, prefix string, instr Instruction) string {
+ var b bytes.Buffer
+ b.WriteString(prefix)
+ if !v.IsInvoke() {
+ b.WriteString(relName(v.Value, instr))
+ } else {
+ fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
+ }
+ b.WriteString("(")
+ for i, arg := range v.Args {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(relName(arg, instr))
+ }
+ if v.Signature().Variadic() {
+ b.WriteString("...")
+ }
+ b.WriteString(")")
+ return b.String()
+}
+
+func (c *CallCommon) String() string {
+ return printCall(c, "", nil)
+}
+
+func (v *Call) String() string {
+ return printCall(&v.Call, "", v)
+}
+
+func (v *BinOp) String() string {
+ return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
+}
+
+func (v *UnOp) String() string {
+ return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
+}
+
+func printConv(prefix string, v, x Value) string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("%s %s <- %s (%s)",
+ prefix,
+ relType(v.Type(), from),
+ relType(x.Type(), from),
+ relName(x, v.(Instruction)))
+}
+
+func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
+func (v *Convert) String() string { return printConv("convert", v, v.X) }
+func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
+func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
+
+func (v *MakeClosure) String() string {
+ var b bytes.Buffer
+ fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
+ if v.Bindings != nil {
+ b.WriteString(" [")
+ for i, c := range v.Bindings {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(relName(c, v))
+ }
+ b.WriteString("]")
+ }
+ return b.String()
+}
+
+func (v *MakeSlice) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("make %s %s %s",
+ relType(v.Type(), from),
+ relName(v.Len, v),
+ relName(v.Cap, v))
+}
+
+func (v *Slice) String() string {
+ var b bytes.Buffer
+ b.WriteString("slice ")
+ b.WriteString(relName(v.X, v))
+ b.WriteString("[")
+ if v.Low != nil {
+ b.WriteString(relName(v.Low, v))
+ }
+ b.WriteString(":")
+ if v.High != nil {
+ b.WriteString(relName(v.High, v))
+ }
+ if v.Max != nil {
+ b.WriteString(":")
+ b.WriteString(relName(v.Max, v))
+ }
+ b.WriteString("]")
+ return b.String()
+}
+
+func (v *MakeMap) String() string {
+ res := ""
+ if v.Reserve != nil {
+ res = relName(v.Reserve, v)
+ }
+ from := v.Parent().pkg()
+ return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
+}
+
+func (v *MakeChan) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
+}
+
+func (v *FieldAddr) String() string {
+ st := deref(v.X.Type()).Underlying().(*types.Struct)
+ // Be robust against a bad index.
+ name := "?"
+ if 0 <= v.Field && v.Field < st.NumFields() {
+ name = st.Field(v.Field).Name()
+ }
+ return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
+}
+
+func (v *Field) String() string {
+ st := v.X.Type().Underlying().(*types.Struct)
+ // Be robust against a bad index.
+ name := "?"
+ if 0 <= v.Field && v.Field < st.NumFields() {
+ name = st.Field(v.Field).Name()
+ }
+ return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
+}
+
+func (v *IndexAddr) String() string {
+ return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
+}
+
+func (v *Index) String() string {
+ return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
+}
+
+func (v *Lookup) String() string {
+ return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
+}
+
+func (v *Range) String() string {
+ return "range " + relName(v.X, v)
+}
+
+func (v *Next) String() string {
+ return "next " + relName(v.Iter, v)
+}
+
+func (v *TypeAssert) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
+}
+
+func (v *Extract) String() string {
+ return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
+}
+
+func (s *Jump) String() string {
+ // Be robust against malformed CFG.
+ block := -1
+ if s.block != nil && len(s.block.Succs) == 1 {
+ block = s.block.Succs[0].Index
+ }
+ return fmt.Sprintf("jump %d", block)
+}
+
+func (s *If) String() string {
+ // Be robust against malformed CFG.
+ tblock, fblock := -1, -1
+ if s.block != nil && len(s.block.Succs) == 2 {
+ tblock = s.block.Succs[0].Index
+ fblock = s.block.Succs[1].Index
+ }
+ return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
+}
+
+func (s *Go) String() string {
+ return printCall(&s.Call, "go ", s)
+}
+
+func (s *Panic) String() string {
+ return "panic " + relName(s.X, s)
+}
+
+func (s *Return) String() string {
+ var b bytes.Buffer
+ b.WriteString("return")
+ for i, r := range s.Results {
+ if i == 0 {
+ b.WriteString(" ")
+ } else {
+ b.WriteString(", ")
+ }
+ b.WriteString(relName(r, s))
+ }
+ return b.String()
+}
+
+func (*RunDefers) String() string {
+ return "rundefers"
+}
+
+func (s *Send) String() string {
+ return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
+}
+
+func (s *Defer) String() string {
+ return printCall(&s.Call, "defer ", s)
+}
+
+func (s *Select) String() string {
+ var b bytes.Buffer
+ for i, st := range s.States {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ if st.Dir == types.RecvOnly {
+ b.WriteString("<-")
+ b.WriteString(relName(st.Chan, s))
+ } else {
+ b.WriteString(relName(st.Chan, s))
+ b.WriteString("<-")
+ b.WriteString(relName(st.Send, s))
+ }
+ }
+ non := ""
+ if !s.Blocking {
+ non = "non"
+ }
+ return fmt.Sprintf("select %sblocking [%s]", non, b.String())
+}
+
+func (s *Store) String() string {
+ return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
+}
+
+func (s *MapUpdate) String() string {
+ return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
+}
+
+func (s *DebugRef) String() string {
+ p := s.Parent().Prog.Fset.Position(s.Pos())
+ var descr interface{}
+ if s.object != nil {
+ descr = s.object // e.g. "var x int"
+ } else {
+ descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
+ }
+ var addr string
+ if s.IsAddr {
+ addr = "address of "
+ }
+ return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
+}
+
+func (p *Package) String() string {
+ return "package " + p.Pkg.Path()
+}
+
+var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
+
+func (p *Package) WriteTo(w io.Writer) (int64, error) {
+ var buf bytes.Buffer
+ WritePackage(&buf, p)
+ n, err := w.Write(buf.Bytes())
+ return int64(n), err
+}
+
+// WritePackage writes to buf a human-readable summary of p.
+func WritePackage(buf *bytes.Buffer, p *Package) {
+ fmt.Fprintf(buf, "%s:\n", p)
+
+ var names []string
+ maxname := 0
+ for name := range p.Members {
+ if l := len(name); l > maxname {
+ maxname = l
+ }
+ names = append(names, name)
+ }
+
+ from := p.Pkg
+ sort.Strings(names)
+ for _, name := range names {
+ switch mem := p.Members[name].(type) {
+ case *NamedConst:
+ fmt.Fprintf(buf, " const %-*s %s = %s\n",
+ maxname, name, mem.Name(), mem.Value.RelString(from))
+
+ case *Function:
+ fmt.Fprintf(buf, " func %-*s %s\n",
+ maxname, name, relType(mem.Type(), from))
+
+ case *Type:
+ fmt.Fprintf(buf, " type %-*s %s\n",
+ maxname, name, relType(mem.Type().Underlying(), from))
+ for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
+ fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
+ }
+
+ case *Global:
+ fmt.Fprintf(buf, " var %-*s %s\n",
+ maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
+ }
+ }
+
+ fmt.Fprintf(buf, "\n")
+}
+
+func commaOk(x bool) string {
+ if x {
+ return ",ok"
+ }
+ return ""
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/sanity.go b/go/src/golang.org/x/tools/go/ssa/sanity.go
index b0593d0..4babb37 100644
--- a/go/src/golang.org/x/tools/go/ssa/sanity.go
+++ b/go/src/golang.org/x/tools/go/ssa/sanity.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// An optional pass for sanity-checking invariants of the SSA representation.
@@ -9,11 +11,10 @@
import (
"fmt"
+ "go/types"
"io"
"os"
"strings"
-
- "golang.org/x/tools/go/types"
)
type sanity struct {
@@ -407,8 +408,8 @@
s.errorf("nil Prog")
}
- fn.String() // must not crash
- fn.RelString(fn.pkgobj()) // must not crash
+ fn.String() // must not crash
+ fn.RelString(fn.pkg()) // must not crash
// All functions have a package, except delegates (which are
// shared across packages, or duplicated as weak symbols in a
@@ -484,7 +485,7 @@
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
- if pkg.Object == nil {
+ if pkg.Pkg == nil {
panic(fmt.Sprintf("Package %s has no Object", pkg))
}
pkg.String() // must not crash
@@ -492,7 +493,7 @@
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
- pkg.Object.Path(), mem, mem.Name(), name))
+ pkg.Pkg.Path(), mem, mem.Name(), name))
}
obj := mem.Object()
if obj == nil {
@@ -510,7 +511,7 @@
// its types.Func ("init") and its ssa.Function ("init#%d").
} else {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
- pkg.Object.Path(), mem, obj.Name(), name))
+ pkg.Pkg.Path(), mem, obj.Name(), name))
}
}
if obj.Pos() != mem.Pos() {
diff --git a/go/src/golang.org/x/tools/go/ssa/sanity14.go b/go/src/golang.org/x/tools/go/ssa/sanity14.go
new file mode 100644
index 0000000..fe4d4ed
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/sanity14.go
@@ -0,0 +1,522 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// An optional pass for sanity-checking invariants of the SSA representation.
+// Currently it checks CFG invariants but little at the instruction level.
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+
+ "golang.org/x/tools/go/types"
+)
+
+type sanity struct {
+ reporter io.Writer
+ fn *Function
+ block *BasicBlock
+ instrs map[Instruction]struct{}
+ insane bool
+}
+
+// sanityCheck performs integrity checking of the SSA representation
+// of the function fn and returns true if it was valid. Diagnostics
+// are written to reporter if non-nil, os.Stderr otherwise. Some
+// diagnostics are only warnings and do not imply a negative result.
+//
+// Sanity-checking is intended to facilitate the debugging of code
+// transformation passes.
+//
+func sanityCheck(fn *Function, reporter io.Writer) bool {
+ if reporter == nil {
+ reporter = os.Stderr
+ }
+ return (&sanity{reporter: reporter}).checkFunction(fn)
+}
+
+// mustSanityCheck is like sanityCheck but panics instead of returning
+// a negative result.
+//
+func mustSanityCheck(fn *Function, reporter io.Writer) {
+ if !sanityCheck(fn, reporter) {
+ fn.WriteTo(os.Stderr)
+ panic("SanityCheck failed")
+ }
+}
+
+func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
+ fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
+ if s.block != nil {
+ fmt.Fprintf(s.reporter, ", block %s", s.block)
+ }
+ io.WriteString(s.reporter, ": ")
+ fmt.Fprintf(s.reporter, format, args...)
+ io.WriteString(s.reporter, "\n")
+}
+
+func (s *sanity) errorf(format string, args ...interface{}) {
+ s.insane = true
+ s.diagnostic("Error", format, args...)
+}
+
+func (s *sanity) warnf(format string, args ...interface{}) {
+ s.diagnostic("Warning", format, args...)
+}
+
+// findDuplicate returns an arbitrary basic block that appeared more
+// than once in blocks, or nil if all were unique.
+func findDuplicate(blocks []*BasicBlock) *BasicBlock {
+ if len(blocks) < 2 {
+ return nil
+ }
+ if blocks[0] == blocks[1] {
+ return blocks[0]
+ }
+ // Slow path:
+ m := make(map[*BasicBlock]bool)
+ for _, b := range blocks {
+ if m[b] {
+ return b
+ }
+ m[b] = true
+ }
+ return nil
+}
+
+func (s *sanity) checkInstr(idx int, instr Instruction) {
+ switch instr := instr.(type) {
+ case *If, *Jump, *Return, *Panic:
+ s.errorf("control flow instruction not at end of block")
+ case *Phi:
+ if idx == 0 {
+ // It suffices to apply this check to just the first phi node.
+ if dup := findDuplicate(s.block.Preds); dup != nil {
+ s.errorf("phi node in block with duplicate predecessor %s", dup)
+ }
+ } else {
+ prev := s.block.Instrs[idx-1]
+ if _, ok := prev.(*Phi); !ok {
+ s.errorf("Phi instruction follows a non-Phi: %T", prev)
+ }
+ }
+ if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
+ s.errorf("phi node has %d edges but %d predecessors", ne, np)
+
+ } else {
+ for i, e := range instr.Edges {
+ if e == nil {
+ s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i])
+ }
+ }
+ }
+
+ case *Alloc:
+ if !instr.Heap {
+ found := false
+ for _, l := range s.fn.Locals {
+ if l == instr {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr)
+ }
+ }
+
+ case *BinOp:
+ case *Call:
+ case *ChangeInterface:
+ case *ChangeType:
+ case *Convert:
+ if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok {
+ if _, ok := instr.Type().Underlying().(*types.Basic); !ok {
+ s.errorf("convert %s -> %s: at least one type must be basic", instr.X.Type(), instr.Type())
+ }
+ }
+
+ case *Defer:
+ case *Extract:
+ case *Field:
+ case *FieldAddr:
+ case *Go:
+ case *Index:
+ case *IndexAddr:
+ case *Lookup:
+ case *MakeChan:
+ case *MakeClosure:
+ numFree := len(instr.Fn.(*Function).FreeVars)
+ numBind := len(instr.Bindings)
+ if numFree != numBind {
+ s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
+ numBind, instr.Fn, numFree)
+
+ }
+ if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
+ s.errorf("MakeClosure's type includes receiver %s", recv.Type())
+ }
+
+ case *MakeInterface:
+ case *MakeMap:
+ case *MakeSlice:
+ case *MapUpdate:
+ case *Next:
+ case *Range:
+ case *RunDefers:
+ case *Select:
+ case *Send:
+ case *Slice:
+ case *Store:
+ case *TypeAssert:
+ case *UnOp:
+ case *DebugRef:
+ // TODO(adonovan): implement checks.
+ default:
+ panic(fmt.Sprintf("Unknown instruction type: %T", instr))
+ }
+
+ if call, ok := instr.(CallInstruction); ok {
+ if call.Common().Signature() == nil {
+ s.errorf("nil signature: %s", call)
+ }
+ }
+
+ // Check that value-defining instructions have valid types
+ // and a valid referrer list.
+ if v, ok := instr.(Value); ok {
+ t := v.Type()
+ if t == nil {
+ s.errorf("no type: %s = %s", v.Name(), v)
+ } else if t == tRangeIter {
+ // not a proper type; ignore.
+ } else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
+ s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
+ }
+ s.checkReferrerList(v)
+ }
+
+ // Untyped constants are legal as instruction Operands(),
+ // for example:
+ // _ = "foo"[0]
+ // or:
+ // if wordsize==64 {...}
+
+ // All other non-Instruction Values can be found via their
+ // enclosing Function or Package.
+}
+
+func (s *sanity) checkFinalInstr(idx int, instr Instruction) {
+ switch instr := instr.(type) {
+ case *If:
+ if nsuccs := len(s.block.Succs); nsuccs != 2 {
+ s.errorf("If-terminated block has %d successors; expected 2", nsuccs)
+ return
+ }
+ if s.block.Succs[0] == s.block.Succs[1] {
+ s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0])
+ return
+ }
+
+ case *Jump:
+ if nsuccs := len(s.block.Succs); nsuccs != 1 {
+ s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs)
+ return
+ }
+
+ case *Return:
+ if nsuccs := len(s.block.Succs); nsuccs != 0 {
+ s.errorf("Return-terminated block has %d successors; expected none", nsuccs)
+ return
+ }
+ if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
+ s.errorf("%d-ary return in %d-ary function", na, nf)
+ }
+
+ case *Panic:
+ if nsuccs := len(s.block.Succs); nsuccs != 0 {
+ s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
+ return
+ }
+
+ default:
+ s.errorf("non-control flow instruction at end of block")
+ }
+}
+
+func (s *sanity) checkBlock(b *BasicBlock, index int) {
+ s.block = b
+
+ if b.Index != index {
+ s.errorf("block has incorrect Index %d", b.Index)
+ }
+ if b.parent != s.fn {
+ s.errorf("block has incorrect parent %s", b.parent)
+ }
+
+ // Check all blocks are reachable.
+ // (The entry block is always implicitly reachable,
+ // as is the Recover block, if any.)
+ if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
+ s.warnf("unreachable block")
+ if b.Instrs == nil {
+ // Since this block is about to be pruned,
+ // tolerating transient problems in it
+ // simplifies other optimizations.
+ return
+ }
+ }
+
+ // Check predecessor and successor relations are dual,
+ // and that all blocks in CFG belong to same function.
+ for _, a := range b.Preds {
+ found := false
+ for _, bb := range a.Succs {
+ if bb == b {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
+ }
+ if a.parent != s.fn {
+ s.errorf("predecessor %s belongs to different function %s", a, a.parent)
+ }
+ }
+ for _, c := range b.Succs {
+ found := false
+ for _, bb := range c.Preds {
+ if bb == b {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
+ }
+ if c.parent != s.fn {
+ s.errorf("successor %s belongs to different function %s", c, c.parent)
+ }
+ }
+
+ // Check each instruction is sane.
+ n := len(b.Instrs)
+ if n == 0 {
+ s.errorf("basic block contains no instructions")
+ }
+ var rands [10]*Value // reuse storage
+ for j, instr := range b.Instrs {
+ if instr == nil {
+ s.errorf("nil instruction at index %d", j)
+ continue
+ }
+ if b2 := instr.Block(); b2 == nil {
+ s.errorf("nil Block() for instruction at index %d", j)
+ continue
+ } else if b2 != b {
+ s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j)
+ continue
+ }
+ if j < n-1 {
+ s.checkInstr(j, instr)
+ } else {
+ s.checkFinalInstr(j, instr)
+ }
+
+ // Check Instruction.Operands.
+ operands:
+ for i, op := range instr.Operands(rands[:0]) {
+ if op == nil {
+ s.errorf("nil operand pointer %d of %s", i, instr)
+ continue
+ }
+ val := *op
+ if val == nil {
+ continue // a nil operand is ok
+ }
+
+ // Check that "untyped" types only appear on constant operands.
+ if _, ok := (*op).(*Const); !ok {
+ if basic, ok := (*op).Type().(*types.Basic); ok {
+ if basic.Info()&types.IsUntyped != 0 {
+ s.errorf("operand #%d of %s is untyped: %s", i, instr, basic)
+ }
+ }
+ }
+
+ // Check that Operands that are also Instructions belong to same function.
+ // TODO(adonovan): also check their block dominates block b.
+ if val, ok := val.(Instruction); ok {
+ if val.Parent() != s.fn {
+ s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
+ }
+ }
+
+ // Check that each function-local operand of
+ // instr refers back to instr. (NB: quadratic)
+ switch val := val.(type) {
+ case *Const, *Global, *Builtin:
+ continue // not local
+ case *Function:
+ if val.parent == nil {
+ continue // only anon functions are local
+ }
+ }
+
+ // TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
+
+ if refs := val.Referrers(); refs != nil {
+ for _, ref := range *refs {
+ if ref == instr {
+ continue operands
+ }
+ }
+ s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
+ } else {
+ s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
+ }
+ }
+ }
+}
+
+func (s *sanity) checkReferrerList(v Value) {
+ refs := v.Referrers()
+ if refs == nil {
+ s.errorf("%s has missing referrer list", v.Name())
+ return
+ }
+ for i, ref := range *refs {
+ if _, ok := s.instrs[ref]; !ok {
+ s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
+ }
+ }
+}
+
+func (s *sanity) checkFunction(fn *Function) bool {
+ // TODO(adonovan): check Function invariants:
+ // - check params match signature
+ // - check transient fields are nil
+ // - warn if any fn.Locals do not appear among block instructions.
+ s.fn = fn
+ if fn.Prog == nil {
+ s.errorf("nil Prog")
+ }
+
+ fn.String() // must not crash
+ fn.RelString(fn.pkg()) // must not crash
+
+ // All functions have a package, except delegates (which are
+ // shared across packages, or duplicated as weak symbols in a
+ // separate-compilation model), and error.Error.
+ if fn.Pkg == nil {
+ if strings.HasPrefix(fn.Synthetic, "wrapper ") ||
+ strings.HasPrefix(fn.Synthetic, "bound ") ||
+ strings.HasPrefix(fn.Synthetic, "thunk ") ||
+ strings.HasSuffix(fn.name, "Error") {
+ // ok
+ } else {
+ s.errorf("nil Pkg")
+ }
+ }
+ if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
+ s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
+ }
+ for i, l := range fn.Locals {
+ if l.Parent() != fn {
+ s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
+ }
+ if l.Heap {
+ s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
+ }
+ }
+ // Build the set of valid referrers.
+ s.instrs = make(map[Instruction]struct{})
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ s.instrs[instr] = struct{}{}
+ }
+ }
+ for i, p := range fn.Params {
+ if p.Parent() != fn {
+ s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
+ }
+ s.checkReferrerList(p)
+ }
+ for i, fv := range fn.FreeVars {
+ if fv.Parent() != fn {
+ s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
+ }
+ s.checkReferrerList(fv)
+ }
+
+ if fn.Blocks != nil && len(fn.Blocks) == 0 {
+ // Function _had_ blocks (so it's not external) but
+ // they were "optimized" away, even the entry block.
+ s.errorf("Blocks slice is non-nil but empty")
+ }
+ for i, b := range fn.Blocks {
+ if b == nil {
+ s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
+ continue
+ }
+ s.checkBlock(b, i)
+ }
+ if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
+ s.errorf("Recover block is not in Blocks slice")
+ }
+
+ s.block = nil
+ for i, anon := range fn.AnonFuncs {
+ if anon.Parent() != fn {
+ s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
+ }
+ }
+ s.fn = nil
+ return !s.insane
+}
+
+// sanityCheckPackage checks invariants of packages upon creation.
+// It does not require that the package is built.
+// Unlike sanityCheck (for functions), it just panics at the first error.
+func sanityCheckPackage(pkg *Package) {
+ if pkg.Pkg == nil {
+ panic(fmt.Sprintf("Package %s has no Object", pkg))
+ }
+ pkg.String() // must not crash
+
+ for name, mem := range pkg.Members {
+ if name != mem.Name() {
+ panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
+ pkg.Pkg.Path(), mem, mem.Name(), name))
+ }
+ obj := mem.Object()
+ if obj == nil {
+ // This check is sound because fields
+ // {Global,Function}.object have type
+ // types.Object. (If they were declared as
+ // *types.{Var,Func}, we'd have a non-empty
+ // interface containing a nil pointer.)
+
+ continue // not all members have typechecker objects
+ }
+ if obj.Name() != name {
+ if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
+ // Ok. The name of a declared init function varies between
+ // its types.Func ("init") and its ssa.Function ("init#%d").
+ } else {
+ panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
+ pkg.Pkg.Path(), mem, obj.Name(), name))
+ }
+ }
+ if obj.Pos() != mem.Pos() {
+ panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/source.go b/go/src/golang.org/x/tools/go/ssa/source.go
index 84e6f1d..3a6f039 100644
--- a/go/src/golang.org/x/tools/go/ssa/source.go
+++ b/go/src/golang.org/x/tools/go/ssa/source.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file defines utilities for working with source positions
@@ -13,8 +15,7 @@
import (
"go/ast"
"go/token"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// EnclosingFunction returns the function that contains the syntax
diff --git a/go/src/golang.org/x/tools/go/ssa/source14.go b/go/src/golang.org/x/tools/go/ssa/source14.go
new file mode 100644
index 0000000..af93136
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/source14.go
@@ -0,0 +1,296 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file defines utilities for working with source positions
+// or source-level named entities ("objects").
+
+// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
+// the originating syntax, as specified.
+
+import (
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/types"
+)
+
+// EnclosingFunction returns the function that contains the syntax
+// node denoted by path.
+//
+// Syntax associated with package-level variable specifications is
+// enclosed by the package's init() function.
+//
+// Returns nil if not found; reasons might include:
+// - the node is not enclosed by any function.
+// - the node is within an anonymous function (FuncLit) and
+// its SSA function has not been created yet
+// (pkg.Build() has not yet been called).
+//
+func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
+ // Start with package-level function...
+ fn := findEnclosingPackageLevelFunction(pkg, path)
+ if fn == nil {
+ return nil // not in any function
+ }
+
+ // ...then walk down the nested anonymous functions.
+ n := len(path)
+outer:
+ for i := range path {
+ if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
+ for _, anon := range fn.AnonFuncs {
+ if anon.Pos() == lit.Type.Func {
+ fn = anon
+ continue outer
+ }
+ }
+ // SSA function not found:
+ // - package not yet built, or maybe
+ // - builder skipped FuncLit in dead block
+ // (in principle; but currently the Builder
+ // generates even dead FuncLits).
+ return nil
+ }
+ }
+ return fn
+}
+
+// HasEnclosingFunction returns true if the AST node denoted by path
+// is contained within the declaration of some function or
+// package-level variable.
+//
+// Unlike EnclosingFunction, the behaviour of this function does not
+// depend on whether SSA code for pkg has been built, so it can be
+// used to quickly reject check inputs that will cause
+// EnclosingFunction to fail, prior to SSA building.
+//
+func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
+ return findEnclosingPackageLevelFunction(pkg, path) != nil
+}
+
+// findEnclosingPackageLevelFunction returns the Function
+// corresponding to the package-level function enclosing path.
+//
+func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
+ if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
+ switch decl := path[n-2].(type) {
+ case *ast.GenDecl:
+ if decl.Tok == token.VAR && n >= 3 {
+ // Package-level 'var' initializer.
+ return pkg.init
+ }
+
+ case *ast.FuncDecl:
+ if decl.Recv == nil && decl.Name.Name == "init" {
+ // Explicit init() function.
+ for _, b := range pkg.init.Blocks {
+ for _, instr := range b.Instrs {
+ if instr, ok := instr.(*Call); ok {
+ if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
+ return callee
+ }
+ }
+ }
+ }
+ // Hack: return non-nil when SSA is not yet
+ // built so that HasEnclosingFunction works.
+ return pkg.init
+ }
+ // Declared function/method.
+ return findNamedFunc(pkg, decl.Name.NamePos)
+ }
+ }
+ return nil // not in any function
+}
+
+// findNamedFunc returns the named function whose FuncDecl.Ident is at
+// position pos.
+//
+func findNamedFunc(pkg *Package, pos token.Pos) *Function {
+ // Look at all package members and method sets of named types.
+ // Not very efficient.
+ for _, mem := range pkg.Members {
+ switch mem := mem.(type) {
+ case *Function:
+ if mem.Pos() == pos {
+ return mem
+ }
+ case *Type:
+ mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
+ for i, n := 0, mset.Len(); i < n; i++ {
+ // Don't call Program.Method: avoid creating wrappers.
+ obj := mset.At(i).Obj().(*types.Func)
+ if obj.Pos() == pos {
+ return pkg.values[obj].(*Function)
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// ValueForExpr returns the SSA Value that corresponds to non-constant
+// expression e.
+//
+// It returns nil if no value was found, e.g.
+// - the expression is not lexically contained within f;
+// - f was not built with debug information; or
+// - e is a constant expression. (For efficiency, no debug
+// information is stored for constants. Use
+// go/types.Info.Types[e].Value instead.)
+// - e is a reference to nil or a built-in function.
+// - the value was optimised away.
+//
+// If e is an addressable expression used in an lvalue context,
+// value is the address denoted by e, and isAddr is true.
+//
+// The types of e (or &e, if isAddr) and the result are equal
+// (modulo "untyped" bools resulting from comparisons).
+//
+// (Tip: to find the ssa.Value given a source position, use
+// importer.PathEnclosingInterval to locate the ast.Node, then
+// EnclosingFunction to locate the Function, then ValueForExpr to find
+// the ssa.Value.)
+//
+func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
+ if f.debugInfo() { // (opt)
+ e = unparen(e)
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ if ref, ok := instr.(*DebugRef); ok {
+ if ref.Expr == e {
+ return ref.X, ref.IsAddr
+ }
+ }
+ }
+ }
+ }
+ return
+}
+
+// --- Lookup functions for source-level named entities (types.Objects) ---
+
+// Package returns the SSA Package corresponding to the specified
+// type-checker package object.
+// It returns nil if no such SSA package has been created.
+//
+func (prog *Program) Package(obj *types.Package) *Package {
+ return prog.packages[obj]
+}
+
+// packageLevelValue returns the package-level value corresponding to
+// the specified named object, which may be a package-level const
+// (*Const), var (*Global) or func (*Function) of some package in
+// prog. It returns nil if the object is not found.
+//
+func (prog *Program) packageLevelValue(obj types.Object) Value {
+ if pkg, ok := prog.packages[obj.Pkg()]; ok {
+ return pkg.values[obj]
+ }
+ return nil
+}
+
+// FuncValue returns the concrete Function denoted by the source-level
+// named function obj, or nil if obj denotes an interface method.
+//
+// TODO(adonovan): check the invariant that obj.Type() matches the
+// result's Signature, both in the params/results and in the receiver.
+//
+func (prog *Program) FuncValue(obj *types.Func) *Function {
+ fn, _ := prog.packageLevelValue(obj).(*Function)
+ return fn
+}
+
+// ConstValue returns the SSA Value denoted by the source-level named
+// constant obj.
+//
+func (prog *Program) ConstValue(obj *types.Const) *Const {
+ // TODO(adonovan): opt: share (don't reallocate)
+ // Consts for const objects and constant ast.Exprs.
+
+ // Universal constant? {true,false,nil}
+ if obj.Parent() == types.Universe {
+ return NewConst(obj.Val(), obj.Type())
+ }
+ // Package-level named constant?
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Const)
+ }
+ return NewConst(obj.Val(), obj.Type())
+}
+
+// VarValue returns the SSA Value that corresponds to a specific
+// identifier denoting the source-level named variable obj.
+//
+// VarValue returns nil if a local variable was not found, perhaps
+// because its package was not built, the debug information was not
+// requested during SSA construction, or the value was optimized away.
+//
+// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
+// and that ident must resolve to obj.
+//
+// pkg is the package enclosing the reference. (A reference to a var
+// always occurs within a function, so we need to know where to find it.)
+//
+// If the identifier is a field selector and its base expression is
+// non-addressable, then VarValue returns the value of that field.
+// For example:
+// func f() struct {x int}
+// f().x // VarValue(x) returns a *Field instruction of type int
+//
+// All other identifiers denote addressable locations (variables).
+// For them, VarValue may return either the variable's address or its
+// value, even when the expression is evaluated only for its value; the
+// situation is reported by isAddr, the second component of the result.
+//
+// If !isAddr, the returned value is the one associated with the
+// specific identifier. For example,
+// var x int // VarValue(x) returns Const 0 here
+// x = 1 // VarValue(x) returns Const 1 here
+//
+// It is not specified whether the value or the address is returned in
+// any particular case, as it may depend upon optimizations performed
+// during SSA code generation, such as registerization, constant
+// folding, avoidance of materialization of subexpressions, etc.
+//
+func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
+ // All references to a var are local to some function, possibly init.
+ fn := EnclosingFunction(pkg, ref)
+ if fn == nil {
+ return // e.g. def of struct field; SSA not built?
+ }
+
+ id := ref[0].(*ast.Ident)
+
+ // Defining ident of a parameter?
+ if id.Pos() == obj.Pos() {
+ for _, param := range fn.Params {
+ if param.Object() == obj {
+ return param, false
+ }
+ }
+ }
+
+ // Other ident?
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ if dr, ok := instr.(*DebugRef); ok {
+ if dr.Pos() == id.Pos() {
+ return dr.X, dr.IsAddr
+ }
+ }
+ }
+ }
+
+ // Defining ident of package-level var?
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Global), true
+ }
+
+ return // e.g. debug info not requested, or var optimized away
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/source14_test.go b/go/src/golang.org/x/tools/go/ssa/source14_test.go
new file mode 100644
index 0000000..cd7b268
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/source14_test.go
@@ -0,0 +1,395 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa_test
+
+// This file defines tests of source-level debugging utilities.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "os"
+ "regexp"
+ "runtime"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+func TestObjValueLookup(t *testing.T) {
+ if runtime.GOOS == "android" {
+ t.Skipf("no testdata directory on %s", runtime.GOOS)
+ }
+
+ conf := loader.Config{ParserMode: parser.ParseComments}
+ f, err := conf.ParseFile("testdata/objlookup.go", nil)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ conf.CreateFromFiles("main", f)
+
+ // Maps each var Ident (represented "name:linenum") to the
+ // kind of ssa.Value we expect (represented "Constant", "&Alloc").
+ expectations := make(map[string]string)
+
+ // Find all annotations of form x::BinOp, &y::Alloc, etc.
+ re := regexp.MustCompile(`(\b|&)?(\w*)::(\w*)\b`)
+ for _, c := range f.Comments {
+ text := c.Text()
+ pos := conf.Fset.Position(c.Pos())
+ for _, m := range re.FindAllStringSubmatch(text, -1) {
+ key := fmt.Sprintf("%s:%d", m[2], pos.Line)
+ value := m[1] + m[3]
+ expectations[key] = value
+ }
+ }
+
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ prog := ssautil.CreateProgram(iprog, 0 /*|ssa.PrintFunctions*/)
+ mainInfo := iprog.Created[0]
+ mainPkg := prog.Package(mainInfo.Pkg)
+ mainPkg.SetDebugMode(true)
+ mainPkg.Build()
+
+ var varIds []*ast.Ident
+ var varObjs []*types.Var
+ for id, obj := range mainInfo.Defs {
+ // Check invariants for func and const objects.
+ switch obj := obj.(type) {
+ case *types.Func:
+ checkFuncValue(t, prog, obj)
+
+ case *types.Const:
+ checkConstValue(t, prog, obj)
+
+ case *types.Var:
+ if id.Name == "_" {
+ continue
+ }
+ varIds = append(varIds, id)
+ varObjs = append(varObjs, obj)
+ }
+ }
+ for id, obj := range mainInfo.Uses {
+ if obj, ok := obj.(*types.Var); ok {
+ varIds = append(varIds, id)
+ varObjs = append(varObjs, obj)
+ }
+ }
+
+ // Check invariants for var objects.
+ // The result varies based on the specific Ident.
+ for i, id := range varIds {
+ obj := varObjs[i]
+ ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())
+ pos := prog.Fset.Position(id.Pos())
+ exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
+ if exp == "" {
+ t.Errorf("%s: no expectation for var ident %s ", pos, id.Name)
+ continue
+ }
+ wantAddr := false
+ if exp[0] == '&' {
+ wantAddr = true
+ exp = exp[1:]
+ }
+ checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)
+ }
+}
+
+func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) {
+ fn := prog.FuncValue(obj)
+ // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging
+ if fn == nil {
+ if obj.Name() != "interfaceMethod" {
+ t.Errorf("FuncValue(%s) == nil", obj)
+ }
+ return
+ }
+ if fnobj := fn.Object(); fnobj != obj {
+ t.Errorf("FuncValue(%s).Object() == %s; value was %s",
+ obj, fnobj, fn.Name())
+ return
+ }
+ if !types.Identical(fn.Type(), obj.Type()) {
+ t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
+ return
+ }
+}
+
+func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
+ c := prog.ConstValue(obj)
+ // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
+ if c == nil {
+ t.Errorf("ConstValue(%s) == nil", obj)
+ return
+ }
+ if !types.Identical(c.Type(), obj.Type()) {
+ t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
+ return
+ }
+ if obj.Name() != "nil" {
+ if !exact.Compare(c.Value, token.EQL, obj.Val()) {
+ t.Errorf("ConstValue(%s).Value (%s) != %s",
+ obj, c.Value, obj.Val())
+ return
+ }
+ }
+}
+
+func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) {
+ // The prefix of all assertions messages.
+ prefix := fmt.Sprintf("VarValue(%s @ L%d)",
+ obj, prog.Fset.Position(ref[0].Pos()).Line)
+
+ v, gotAddr := prog.VarValue(obj, pkg, ref)
+
+ // Kind is the concrete type of the ssa Value.
+ gotKind := "nil"
+ if v != nil {
+ gotKind = fmt.Sprintf("%T", v)[len("*ssa."):]
+ }
+
+ // fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging
+
+ // Check the kinds match.
+ // "nil" indicates expected failure (e.g. optimized away).
+ if expKind != gotKind {
+ t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind)
+ }
+
+ // Check the types match.
+ // If wantAddr, the expected type is the object's address.
+ if v != nil {
+ expType := obj.Type()
+ if wantAddr {
+ expType = types.NewPointer(expType)
+ if !gotAddr {
+ t.Errorf("%s: got value, want address", prefix)
+ }
+ } else if gotAddr {
+ t.Errorf("%s: got address, want value", prefix)
+ }
+ if !types.Identical(v.Type(), expType) {
+ t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
+ }
+ }
+}
+
+// Ensure that, in debug mode, we can determine the ssa.Value
+// corresponding to every ast.Expr.
+func TestValueForExpr(t *testing.T) {
+ if runtime.GOOS == "android" {
+ t.Skipf("no testdata dir on %s", runtime.GOOS)
+ }
+
+ conf := loader.Config{ParserMode: parser.ParseComments}
+ f, err := conf.ParseFile("testdata/valueforexpr.go", nil)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ conf.CreateFromFiles("main", f)
+
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ mainInfo := iprog.Created[0]
+
+ prog := ssautil.CreateProgram(iprog, 0)
+ mainPkg := prog.Package(mainInfo.Pkg)
+ mainPkg.SetDebugMode(true)
+ mainPkg.Build()
+
+ if false {
+ // debugging
+ for _, mem := range mainPkg.Members {
+ if fn, ok := mem.(*ssa.Function); ok {
+ fn.WriteTo(os.Stderr)
+ }
+ }
+ }
+
+ // Find the actual AST node for each canonical position.
+ parenExprByPos := make(map[token.Pos]*ast.ParenExpr)
+ ast.Inspect(f, func(n ast.Node) bool {
+ if n != nil {
+ if e, ok := n.(*ast.ParenExpr); ok {
+ parenExprByPos[e.Pos()] = e
+ }
+ }
+ return true
+ })
+
+ // Find all annotations of form /*@kind*/.
+ for _, c := range f.Comments {
+ text := strings.TrimSpace(c.Text())
+ if text == "" || text[0] != '@' {
+ continue
+ }
+ text = text[1:]
+ pos := c.End() + 1
+ position := prog.Fset.Position(pos)
+ var e ast.Expr
+ if target := parenExprByPos[pos]; target == nil {
+ t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text)
+ continue
+ } else {
+ e = target.X
+ }
+
+ path, _ := astutil.PathEnclosingInterval(f, pos, pos)
+ if path == nil {
+ t.Errorf("%s: can't find AST path from root to comment: %s", position, text)
+ continue
+ }
+
+ fn := ssa.EnclosingFunction(mainPkg, path)
+ if fn == nil {
+ t.Errorf("%s: can't find enclosing function", position)
+ continue
+ }
+
+ v, gotAddr := fn.ValueForExpr(e) // (may be nil)
+ got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")
+ if want := text; got != want {
+ t.Errorf("%s: got value %q, want %q", position, got, want)
+ }
+ if v != nil {
+ T := v.Type()
+ if gotAddr {
+ T = T.Underlying().(*types.Pointer).Elem() // deref
+ }
+ if !types.Identical(T, mainInfo.TypeOf(e)) {
+ t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
+ }
+ }
+ }
+}
+
+// findInterval parses input and returns the [start, end) positions of
+// the first occurrence of substr in input. f==nil indicates failure;
+// an error has already been reported in that case.
+//
+func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
+ f, err := parser.ParseFile(fset, "<input>", input, 0)
+ if err != nil {
+ t.Errorf("parse error: %s", err)
+ return
+ }
+
+ i := strings.Index(input, substr)
+ if i < 0 {
+ t.Errorf("%q is not a substring of input", substr)
+ f = nil
+ return
+ }
+
+ filePos := fset.File(f.Package)
+ return f, filePos.Pos(i), filePos.Pos(i + len(substr))
+}
+
+func TestEnclosingFunction(t *testing.T) {
+ tests := []struct {
+ input string // the input file
+ substr string // first occurrence of this string denotes interval
+ fn string // name of expected containing function
+ }{
+ // We use distinctive numbers as syntactic landmarks.
+
+ // Ordinary function:
+ {`package main
+ func f() { println(1003) }`,
+ "100", "main.f"},
+ // Methods:
+ {`package main
+ type T int
+ func (t T) f() { println(200) }`,
+ "200", "(main.T).f"},
+ // Function literal:
+ {`package main
+ func f() { println(func() { print(300) }) }`,
+ "300", "main.f$1"},
+ // Doubly nested
+ {`package main
+ func f() { println(func() { print(func() { print(350) })})}`,
+ "350", "main.f$1$1"},
+ // Implicit init for package-level var initializer.
+ {"package main; var a = 400", "400", "main.init"},
+ // No code for constants:
+ {"package main; const a = 500", "500", "(none)"},
+ // Explicit init()
+ {"package main; func init() { println(600) }", "600", "main.init#1"},
+ // Multiple explicit init functions:
+ {`package main
+ func init() { println("foo") }
+ func init() { println(800) }`,
+ "800", "main.init#2"},
+ // init() containing FuncLit.
+ {`package main
+ func init() { println(func(){print(900)}) }`,
+ "900", "main.init#1$1"},
+ }
+ for _, test := range tests {
+ conf := loader.Config{Fset: token.NewFileSet()}
+ f, start, end := findInterval(t, conf.Fset, test.input, test.substr)
+ if f == nil {
+ continue
+ }
+ path, exact := astutil.PathEnclosingInterval(f, start, end)
+ if !exact {
+ t.Errorf("EnclosingFunction(%q) not exact", test.substr)
+ continue
+ }
+
+ conf.CreateFromFiles("main", f)
+
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ prog := ssautil.CreateProgram(iprog, 0)
+ pkg := prog.Package(iprog.Created[0].Pkg)
+ pkg.Build()
+
+ name := "(none)"
+ fn := ssa.EnclosingFunction(pkg, path)
+ if fn != nil {
+ name = fn.String()
+ }
+
+ if name != test.fn {
+ t.Errorf("EnclosingFunction(%q in %q) got %s, want %s",
+ test.substr, test.input, name, test.fn)
+ continue
+ }
+
+ // While we're here: test HasEnclosingFunction.
+ if has := ssa.HasEnclosingFunction(pkg, path); has != (fn != nil) {
+ t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v",
+ test.substr, test.input, has, fn != nil)
+ continue
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/source_test.go b/go/src/golang.org/x/tools/go/ssa/source_test.go
index 75669c1..3fd7ac4 100644
--- a/go/src/golang.org/x/tools/go/ssa/source_test.go
+++ b/go/src/golang.org/x/tools/go/ssa/source_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa_test
// This file defines tests of source-level debugging utilities.
@@ -9,8 +11,10 @@
import (
"fmt"
"go/ast"
+ exact "go/constant"
"go/parser"
"go/token"
+ "go/types"
"os"
"regexp"
"runtime"
@@ -18,11 +22,9 @@
"testing"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
func TestObjValueLookup(t *testing.T) {
diff --git a/go/src/golang.org/x/tools/go/ssa/ssa.go b/go/src/golang.org/x/tools/go/ssa/ssa.go
index f4c64bb..c547af8 100644
--- a/go/src/golang.org/x/tools/go/ssa/ssa.go
+++ b/go/src/golang.org/x/tools/go/ssa/ssa.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This package defines a high-level intermediate representation for
@@ -10,11 +12,11 @@
import (
"fmt"
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"sync"
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -45,7 +47,7 @@
//
type Package struct {
Prog *Program // the owning program
- Object *types.Package // the type checker's package object for this package
+ Pkg *types.Package // the corresponding go/types.Package
Members map[string]Member // all package members keyed by name (incl. init and init#%d)
values map[types.Object]Value // package members (incl. types and methods), keyed by object
init *Function // Func("init"); the package's init function
@@ -53,10 +55,10 @@
// The following fields are set transiently, then cleared
// after building.
- started int32 // atomically tested and set at start of build phase
- ninit int32 // number of init functions
- info *types.Info // package type information
- files []*ast.File // package ASTs
+ buildOnce sync.Once // ensures package building occurs once
+ ninit int32 // number of init functions
+ info *types.Info // package type information
+ files []*ast.File // package ASTs
}
// A Member is a member of a Go package, implemented by *NamedConst,
diff --git a/go/src/golang.org/x/tools/go/ssa/ssa14.go b/go/src/golang.org/x/tools/go/ssa/ssa14.go
new file mode 100644
index 0000000..dcc62da
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/ssa14.go
@@ -0,0 +1,1702 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This package defines a high-level intermediate representation for
+// Go programs using static single-assignment (SSA) form.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "sync"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// A Program is a partial or complete Go program converted to SSA form.
+type Program struct {
+ Fset *token.FileSet // position information for the files of this Program
+ imported map[string]*Package // all importable Packages, keyed by import path
+ packages map[*types.Package]*Package // all loaded Packages, keyed by object
+ mode BuilderMode // set of mode bits for SSA construction
+ MethodSets typeutil.MethodSetCache // cache of type-checker's method-sets
+
+ methodsMu sync.Mutex // guards the following maps:
+ methodSets typeutil.Map // maps type to its concrete methodSet
+ runtimeTypes typeutil.Map // types for which rtypes are needed
+ canon typeutil.Map // type canonicalization map
+ bounds map[*types.Func]*Function // bounds for curried x.Method closures
+ thunks map[selectionKey]*Function // thunks for T.Method expressions
+}
+
+// A Package is a single analyzed Go package containing Members for
+// all package-level functions, variables, constants and types it
+// declares. These may be accessed directly via Members, or via the
+// type-specific accessor methods Func, Type, Var and Const.
+//
+// Members also contains entries for "init" (the synthetic package
+// initializer) and "init#%d", the nth declared init function,
+// and unspecified other things too.
+//
+type Package struct {
+ Prog *Program // the owning program
+ Pkg *types.Package // the corresponding go/types.Package
+ Members map[string]Member // all package members keyed by name (incl. init and init#%d)
+ values map[types.Object]Value // package members (incl. types and methods), keyed by object
+ init *Function // Func("init"); the package's init function
+ debug bool // include full debug info in this package
+
+ // The following fields are set transiently, then cleared
+ // after building.
+ buildOnce sync.Once // ensures package building occurs once
+ ninit int32 // number of init functions
+ info *types.Info // package type information
+ files []*ast.File // package ASTs
+}
+
+// A Member is a member of a Go package, implemented by *NamedConst,
+// *Global, *Function, or *Type; they are created by package-level
+// const, var, func and type declarations respectively.
+//
+type Member interface {
+ Name() string // declared name of the package member
+ String() string // package-qualified name of the package member
+ RelString(*types.Package) string // like String, but relative refs are unqualified
+ Object() types.Object // typechecker's object for this member, if any
+ Pos() token.Pos // position of member's declaration, if known
+ Type() types.Type // type of the package member
+ Token() token.Token // token.{VAR,FUNC,CONST,TYPE}
+ Package() *Package // the containing package
+}
+
+// A Type is a Member of a Package representing a package-level named type.
+//
+// Type() returns a *types.Named.
+//
+type Type struct {
+ object *types.TypeName
+ pkg *Package
+}
+
+// A NamedConst is a Member of a Package representing a package-level
+// named constant.
+//
+// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
+// identifier.
+//
+// NB: a NamedConst is not a Value; it contains a constant Value, which
+// it augments with the name and position of its 'const' declaration.
+//
+type NamedConst struct {
+ object *types.Const
+ Value *Const
+ pos token.Pos
+ pkg *Package
+}
+
+// A Value is an SSA value that can be referenced by an instruction.
+type Value interface {
+ // Name returns the name of this value, and determines how
+ // this Value appears when used as an operand of an
+ // Instruction.
+ //
+ // This is the same as the source name for Parameters,
+ // Builtins, Functions, FreeVars, Globals.
+ // For constants, it is a representation of the constant's value
+ // and type. For all other Values this is the name of the
+ // virtual register defined by the instruction.
+ //
+ // The name of an SSA Value is not semantically significant,
+ // and may not even be unique within a function.
+ Name() string
+
+ // If this value is an Instruction, String returns its
+ // disassembled form; otherwise it returns unspecified
+ // human-readable information about the Value, such as its
+ // kind, name and type.
+ String() string
+
+ // Type returns the type of this value. Many instructions
+ // (e.g. IndexAddr) change their behaviour depending on the
+ // types of their operands.
+ Type() types.Type
+
+ // Parent returns the function to which this Value belongs.
+ // It returns nil for named Functions, Builtin, Const and Global.
+ Parent() *Function
+
+ // Referrers returns the list of instructions that have this
+ // value as one of their operands; it may contain duplicates
+ // if an instruction has a repeated operand.
+ //
+ // Referrers actually returns a pointer through which the
+ // caller may perform mutations to the object's state.
+ //
+ // Referrers is currently only defined if Parent()!=nil,
+ // i.e. for the function-local values FreeVar, Parameter,
+ // Functions (iff anonymous) and all value-defining instructions.
+ // It returns nil for named Functions, Builtin, Const and Global.
+ //
+ // Instruction.Operands contains the inverse of this relation.
+ Referrers() *[]Instruction
+
+ // Pos returns the location of the AST token most closely
+ // associated with the operation that gave rise to this value,
+ // or token.NoPos if it was not explicit in the source.
+ //
+ // For each ast.Node type, a particular token is designated as
+ // the closest location for the expression, e.g. the Lparen
+ // for an *ast.CallExpr. This permits a compact but
+ // approximate mapping from Values to source positions for use
+ // in diagnostic messages, for example.
+ //
+ // (Do not use this position to determine which Value
+ // corresponds to an ast.Expr; use Function.ValueForExpr
+ // instead. NB: it requires that the function was built with
+ // debug information.)
+ Pos() token.Pos
+}
+
+// An Instruction is an SSA instruction that computes a new Value or
+// has some effect.
+//
+// An Instruction that defines a value (e.g. BinOp) also implements
+// the Value interface; an Instruction that only has an effect (e.g. Store)
+// does not.
+//
+type Instruction interface {
+ // String returns the disassembled form of this value.
+ //
+ // Examples of Instructions that are Values:
+ // "x + y" (BinOp)
+ // "len([])" (Call)
+ // Note that the name of the Value is not printed.
+ //
+ // Examples of Instructions that are not Values:
+ // "return x" (Return)
+ // "*y = x" (Store)
+ //
+ // (The separation Value.Name() from Value.String() is useful
+ // for some analyses which distinguish the operation from the
+ // value it defines, e.g., 'y = local int' is both an allocation
+ // of memory 'local int' and a definition of a pointer y.)
+ String() string
+
+ // Parent returns the function to which this instruction
+ // belongs.
+ Parent() *Function
+
+ // Block returns the basic block to which this instruction
+ // belongs.
+ Block() *BasicBlock
+
+ // setBlock sets the basic block to which this instruction belongs.
+ setBlock(*BasicBlock)
+
+ // Operands returns the operands of this instruction: the
+ // set of Values it references.
+ //
+ // Specifically, it appends their addresses to rands, a
+ // user-provided slice, and returns the resulting slice,
+ // permitting avoidance of memory allocation.
+ //
+ // The operands are appended in undefined order, but the order
+ // is consistent for a given Instruction; the addresses are
+ // always non-nil but may point to a nil Value. Clients may
+ // store through the pointers, e.g. to effect a value
+ // renaming.
+ //
+ // Value.Referrers is a subset of the inverse of this
+ // relation. (Referrers are not tracked for all types of
+ // Values.)
+ Operands(rands []*Value) []*Value
+
+ // Pos returns the location of the AST token most closely
+ // associated with the operation that gave rise to this
+ // instruction, or token.NoPos if it was not explicit in the
+ // source.
+ //
+ // For each ast.Node type, a particular token is designated as
+ // the closest location for the expression, e.g. the Go token
+ // for an *ast.GoStmt. This permits a compact but approximate
+ // mapping from Instructions to source positions for use in
+ // diagnostic messages, for example.
+ //
+ // (Do not use this position to determine which Instruction
+ // corresponds to an ast.Expr; see the notes for Value.Pos.
+ // This position may be used to determine which non-Value
+ // Instruction corresponds to some ast.Stmts, but not all: If
+ // and Jump instructions have no Pos(), for example.)
+ Pos() token.Pos
+}
+
+// A Node is a node in the SSA value graph. Every concrete type that
+// implements Node is also either a Value, an Instruction, or both.
+//
+// Node contains the methods common to Value and Instruction, plus the
+// Operands and Referrers methods generalized to return nil for
+// non-Instructions and non-Values, respectively.
+//
+// Node is provided to simplify SSA graph algorithms. Clients should
+// use the more specific and informative Value or Instruction
+// interfaces where appropriate.
+//
+type Node interface {
+ // Common methods:
+ String() string
+ Pos() token.Pos
+ Parent() *Function
+
+ // Partial methods:
+ Operands(rands []*Value) []*Value // nil for non-Instructions
+ Referrers() *[]Instruction // nil for non-Values
+}
+
+// Function represents the parameters, results, and code of a function
+// or method.
+//
+// If Blocks is nil, this indicates an external function for which no
+// Go source code is available. In this case, FreeVars and Locals
+// are nil too. Clients performing whole-program analysis must
+// handle external functions specially.
+//
+// Blocks contains the function's control-flow graph (CFG).
+// Blocks[0] is the function entry point; block order is not otherwise
+// semantically significant, though it may affect the readability of
+// the disassembly.
+// To iterate over the blocks in dominance order, use DomPreorder().
+//
+// Recover is an optional second entry point to which control resumes
+// after a recovered panic. The Recover block may contain only a return
+// statement, preceded by a load of the function's named return
+// parameters, if any.
+//
+// A nested function (Parent()!=nil) that refers to one or more
+// lexically enclosing local variables ("free variables") has FreeVars.
+// Such functions cannot be called directly but require a
+// value created by MakeClosure which, via its Bindings, supplies
+// values for these parameters.
+//
+// If the function is a method (Signature.Recv() != nil) then the first
+// element of Params is the receiver parameter.
+//
+// A Go package may declare many functions called "init".
+// For each one, Object().Name() returns "init" but Name() returns
+// "init#1", etc, in declaration order.
+//
+// Pos() returns the declaring ast.FuncLit.Type.Func or the position
+// of the ast.FuncDecl.Name, if the function was explicit in the
+// source. Synthetic wrappers, for which Synthetic != "", may share
+// the same position as the function they wrap.
+// Syntax.Pos() always returns the position of the declaring "func" token.
+//
+// Type() returns the function's Signature.
+//
+type Function struct {
+ name string
+ object types.Object // a declared *types.Func or one of its wrappers
+ method *types.Selection // info about provenance of synthetic methods
+ Signature *types.Signature
+ pos token.Pos
+
+ Synthetic string // provenance of synthetic function; "" for true source functions
+ syntax ast.Node // *ast.Func{Decl,Lit}; replaced with simple ast.Node after build, unless debug mode
+ parent *Function // enclosing function if anon; nil if global
+ Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error)
+ Prog *Program // enclosing program
+ Params []*Parameter // function parameters; for methods, includes receiver
+ FreeVars []*FreeVar // free variables whose values must be supplied by closure
+ Locals []*Alloc // local variables of this function
+ Blocks []*BasicBlock // basic blocks of the function; nil => external
+ Recover *BasicBlock // optional; control transfers here after recovered panic
+ AnonFuncs []*Function // anonymous functions directly beneath this one
+ referrers []Instruction // referring instructions (iff Parent() != nil)
+
+ // The following fields are set transiently during building,
+ // then cleared.
+ currentBlock *BasicBlock // where to emit code
+ objects map[types.Object]Value // addresses of local variables
+ namedResults []*Alloc // tuple of named results
+ targets *targets // linked stack of branch targets
+ lblocks map[*ast.Object]*lblock // labelled blocks
+}
+
+// BasicBlock represents an SSA basic block.
+//
+// The final element of Instrs is always an explicit transfer of
+// control (If, Jump, Return, or Panic).
+//
+// A block may contain no Instructions only if it is unreachable,
+// i.e., Preds is nil. Empty blocks are typically pruned.
+//
+// BasicBlocks and their Preds/Succs relation form a (possibly cyclic)
+// graph independent of the SSA Value graph: the control-flow graph or
+// CFG. It is illegal for multiple edges to exist between the same
+// pair of blocks.
+//
+// Each BasicBlock is also a node in the dominator tree of the CFG.
+// The tree may be navigated using Idom()/Dominees() and queried using
+// Dominates().
+//
+// The order of Preds and Succs is significant (to Phi and If
+// instructions, respectively).
+//
+type BasicBlock struct {
+ Index int // index of this block within Parent().Blocks
+ Comment string // optional label; no semantic significance
+ parent *Function // parent function
+ Instrs []Instruction // instructions in order
+ Preds, Succs []*BasicBlock // predecessors and successors
+ succs2 [2]*BasicBlock // initial space for Succs
+ dom domInfo // dominator tree info
+ gaps int // number of nil Instrs (transient)
+ rundefers int // number of rundefers (transient)
+}
+
+// Pure values ----------------------------------------
+
+// A FreeVar represents a free variable of the function to which it
+// belongs.
+//
+// FreeVars are used to implement anonymous functions, whose free
+// variables are lexically captured in a closure formed by
+// MakeClosure. The value of such a free var is an Alloc or another
+// FreeVar and is considered a potentially escaping heap address, with
+// pointer type.
+//
+// FreeVars are also used to implement bound method closures. Such a
+// free var represents the receiver value and may be of any type that
+// has concrete methods.
+//
+// Pos() returns the position of the value that was captured, which
+// belongs to an enclosing function.
+//
+type FreeVar struct {
+ name string
+ typ types.Type
+ pos token.Pos
+ parent *Function
+ referrers []Instruction
+
+ // Transiently needed during building.
+ outer Value // the Value captured from the enclosing context.
+}
+
+// A Parameter represents an input parameter of a function.
+//
+type Parameter struct {
+ name string
+ object types.Object // a *types.Var; nil for non-source locals
+ typ types.Type
+ pos token.Pos
+ parent *Function
+ referrers []Instruction
+}
+
+// A Const represents the value of a constant expression.
+//
+// The underlying type of a constant may be any boolean, numeric, or
+// string type. In addition, a Const may represent the nil value of
+// any reference type---interface, map, channel, pointer, slice, or
+// function---but not "untyped nil".
+//
+// All source-level constant expressions are represented by a Const
+// of the same type and value.
+//
+// Value holds the exact value of the constant, independent of its
+// Type(), using the same representation as package go/exact uses for
+// constants, or nil for a typed nil value.
+//
+// Pos() returns token.NoPos.
+//
+// Example printed form:
+// 42:int
+// "hello":untyped string
+// 3+4i:MyComplex
+//
+type Const struct {
+ typ types.Type
+ Value exact.Value
+}
+
+// A Global is a named Value holding the address of a package-level
+// variable.
+//
+// Pos() returns the position of the ast.ValueSpec.Names[*]
+// identifier.
+//
+type Global struct {
+ name string
+ object types.Object // a *types.Var; may be nil for synthetics e.g. init$guard
+ typ types.Type
+ pos token.Pos
+
+ Pkg *Package
+}
+
+// A Builtin represents a specific use of a built-in function, e.g. len.
+//
+// Builtins are immutable values. Builtins do not have addresses.
+// Builtins can only appear in CallCommon.Func.
+//
+// Name() indicates the function: one of the built-in functions from the
+// Go spec (excluding "make" and "new") or one of these ssa-defined
+// intrinsics:
+//
+// // wrapnilchk returns ptr if non-nil, panics otherwise.
+// // (For use in indirection wrappers.)
+// func ssa:wrapnilchk(ptr *T, recvType, methodName string) *T
+//
+// Object() returns a *types.Builtin for built-ins defined by the spec,
+// nil for others.
+//
+// Type() returns a *types.Signature representing the effective
+// signature of the built-in for this call.
+//
+type Builtin struct {
+ name string
+ sig *types.Signature
+}
+
+// Value-defining instructions ----------------------------------------
+
+// The Alloc instruction reserves space for a variable of the given type,
+// zero-initializes it, and yields its address.
+//
+// Alloc values are always addresses, and have pointer types, so the
+// type of the allocated variable is actually
+// Type().Underlying().(*types.Pointer).Elem().
+//
+// If Heap is false, Alloc allocates space in the function's
+// activation record (frame); we refer to an Alloc(Heap=false) as a
+// "local" alloc. Each local Alloc returns the same address each time
+// it is executed within the same activation; the space is
+// re-initialized to zero.
+//
+// If Heap is true, Alloc allocates space in the heap; we
+// refer to an Alloc(Heap=true) as a "new" alloc. Each new Alloc
+// returns a different address each time it is executed.
+//
+// When Alloc is applied to a channel, map or slice type, it returns
+// the address of an uninitialized (nil) reference of that kind; store
+// the result of MakeSlice, MakeMap or MakeChan in that location to
+// instantiate these types.
+//
+// Pos() returns the ast.CompositeLit.Lbrace for a composite literal,
+// or the ast.CallExpr.Rparen for a call to new() or for a call that
+// allocates a varargs slice.
+//
+// Example printed form:
+// t0 = local int
+// t1 = new int
+//
+type Alloc struct {
+ register
+ Comment string
+ Heap bool
+ index int // dense numbering; for lifting
+}
+
+// The Phi instruction represents an SSA φ-node, which combines values
+// that differ across incoming control-flow edges and yields a new
+// value. Within a block, all φ-nodes must appear before all non-φ
+// nodes.
+//
+// Pos() returns the position of the && or || for short-circuit
+// control-flow joins, or that of the *Alloc for φ-nodes inserted
+// during SSA renaming.
+//
+// Example printed form:
+// t2 = phi [0: t0, 1: t1]
+//
+type Phi struct {
+ register
+ Comment string // a hint as to its purpose
+ Edges []Value // Edges[i] is value for Block().Preds[i]
+}
+
+// The Call instruction represents a function or method call.
+//
+// The Call instruction yields the function result if there is exactly
+// one. Otherwise it returns a tuple, the components of which are
+// accessed via Extract.
+//
+// See CallCommon for generic function call documentation.
+//
+// Pos() returns the ast.CallExpr.Lparen, if explicit in the source.
+//
+// Example printed form:
+// t2 = println(t0, t1)
+// t4 = t3()
+// t7 = invoke t5.Println(...t6)
+//
+type Call struct {
+ register
+ Call CallCommon
+}
+
+// The BinOp instruction yields the result of binary operation X Op Y.
+//
+// Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source.
+//
+// Example printed form:
+// t1 = t0 + 1:int
+//
+type BinOp struct {
+ register
+ // One of:
+ // ADD SUB MUL QUO REM + - * / %
+ // AND OR XOR SHL SHR AND_NOT & | ^ << >> &~
+ // EQL LSS GTR NEQ LEQ GEQ == != < <= < >=
+ Op token.Token
+ X, Y Value
+}
+
+// The UnOp instruction yields the result of Op X.
+// ARROW is channel receive.
+// MUL is pointer indirection (load).
+// XOR is bitwise complement.
+// SUB is negation.
+// NOT is logical negation.
+//
+// If CommaOk and Op=ARROW, the result is a 2-tuple of the value above
+// and a boolean indicating the success of the receive. The
+// components of the tuple are accessed using Extract.
+//
+// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source.
+// For receive operations (ARROW) implicit in ranging over a channel,
+// Pos() returns the ast.RangeStmt.For.
+// For implicit memory loads (STAR), Pos() returns the position of the
+// most closely associated source-level construct; the details are not
+// specified.
+//
+// Example printed form:
+// t0 = *x
+// t2 = <-t1,ok
+//
+type UnOp struct {
+ register
+ Op token.Token // One of: NOT SUB ARROW MUL XOR ! - <- * ^
+ X Value
+ CommaOk bool
+}
+
+// The ChangeType instruction applies to X a value-preserving type
+// change to Type().
+//
+// Type changes are permitted:
+// - between a named type and its underlying type.
+// - between two named types of the same underlying type.
+// - between (possibly named) pointers to identical base types.
+// - from a bidirectional channel to a read- or write-channel,
+// optionally adding/removing a name.
+//
+// This operation cannot fail dynamically.
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
+//
+// Example printed form:
+// t1 = changetype *int <- IntPtr (t0)
+//
+type ChangeType struct {
+ register
+ X Value
+}
+
+// The Convert instruction yields the conversion of value X to type
+// Type(). One or both of those types is basic (but possibly named).
+//
+// A conversion may change the value and representation of its operand.
+// Conversions are permitted:
+// - between real numeric types.
+// - between complex numeric types.
+// - between string and []byte or []rune.
+// - between pointers and unsafe.Pointer.
+// - between unsafe.Pointer and uintptr.
+// - from (Unicode) integer to (UTF-8) string.
+// A conversion may imply a type name change also.
+//
+// This operation cannot fail dynamically.
+//
+// Conversions of untyped string/number/bool constants to a specific
+// representation are eliminated during SSA construction.
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
+//
+// Example printed form:
+// t1 = convert []byte <- string (t0)
+//
+type Convert struct {
+ register
+ X Value
+}
+
+// ChangeInterface constructs a value of one interface type from a
+// value of another interface type known to be assignable to it.
+// This operation cannot fail.
+//
+// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
+// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
+// instruction arose from an explicit e.(T) operation; or token.NoPos
+// otherwise.
+//
+// Example printed form:
+// t1 = change interface interface{} <- I (t0)
+//
+type ChangeInterface struct {
+ register
+ X Value
+}
+
+// MakeInterface constructs an instance of an interface type from a
+// value of a concrete type.
+//
+// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
+// of X, and Program.Method(m) to find the implementation of a method.
+//
+// To construct the zero value of an interface type T, use:
+// NewConst(exact.MakeNil(), T, pos)
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
+//
+// Example printed form:
+// t1 = make interface{} <- int (42:int)
+// t2 = make Stringer <- t0
+//
+type MakeInterface struct {
+ register
+ X Value
+}
+
+// The MakeClosure instruction yields a closure value whose code is
+// Fn and whose free variables' values are supplied by Bindings.
+//
+// Type() returns a (possibly named) *types.Signature.
+//
+// Pos() returns the ast.FuncLit.Type.Func for a function literal
+// closure or the ast.SelectorExpr.Sel for a bound method closure.
+//
+// Example printed form:
+// t0 = make closure anon@1.2 [x y z]
+// t1 = make closure bound$(main.I).add [i]
+//
+type MakeClosure struct {
+ register
+ Fn Value // always a *Function
+ Bindings []Value // values for each free variable in Fn.FreeVars
+}
+
+// The MakeMap instruction creates a new hash-table-based map object
+// and yields a value of kind map.
+//
+// Type() returns a (possibly named) *types.Map.
+//
+// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or
+// the ast.CompositeLit.Lbrack if created by a literal.
+//
+// Example printed form:
+// t1 = make map[string]int t0
+// t1 = make StringIntMap t0
+//
+type MakeMap struct {
+ register
+ Reserve Value // initial space reservation; nil => default
+}
+
+// The MakeChan instruction creates a new channel object and yields a
+// value of kind chan.
+//
+// Type() returns a (possibly named) *types.Chan.
+//
+// Pos() returns the ast.CallExpr.Lparen for the make(chan) that
+// created it.
+//
+// Example printed form:
+// t0 = make chan int 0
+// t0 = make IntChan 0
+//
+type MakeChan struct {
+ register
+ Size Value // int; size of buffer; zero => synchronous.
+}
+
+// The MakeSlice instruction yields a slice of length Len backed by a
+// newly allocated array of length Cap.
+//
+// Both Len and Cap must be non-nil Values of integer type.
+//
+// (Alloc(types.Array) followed by Slice will not suffice because
+// Alloc can only create arrays of constant length.)
+//
+// Type() returns a (possibly named) *types.Slice.
+//
+// Pos() returns the ast.CallExpr.Lparen for the make([]T) that
+// created it.
+//
+// Example printed form:
+// t1 = make []string 1:int t0
+// t1 = make StringSlice 1:int t0
+//
+type MakeSlice struct {
+ register
+ Len Value
+ Cap Value
+}
+
+// The Slice instruction yields a slice of an existing string, slice
+// or *array X between optional integer bounds Low and High.
+//
+// Dynamically, this instruction panics if X evaluates to a nil *array
+// pointer.
+//
+// Type() returns string if the type of X was string, otherwise a
+// *types.Slice with the same element type as X.
+//
+// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice
+// operation, the ast.CompositeLit.Lbrace if created by a literal, or
+// NoPos if not explicit in the source (e.g. a variadic argument slice).
+//
+// Example printed form:
+// t1 = slice t0[1:]
+//
+type Slice struct {
+ register
+ X Value // slice, string, or *array
+ Low, High, Max Value // each may be nil
+}
+
+// The FieldAddr instruction yields the address of Field of *struct X.
+//
+// The field is identified by its index within the field list of the
+// struct type of X.
+//
+// Dynamically, this instruction panics if X evaluates to a nil
+// pointer.
+//
+// Type() returns a (possibly named) *types.Pointer.
+//
+// Pos() returns the position of the ast.SelectorExpr.Sel for the
+// field, if explicit in the source.
+//
+// Example printed form:
+// t1 = &t0.name [#1]
+//
+type FieldAddr struct {
+ register
+ X Value // *struct
+ Field int // index into X.Type().Deref().(*types.Struct).Fields
+}
+
+// The Field instruction yields the Field of struct X.
+//
+// The field is identified by its index within the field list of the
+// struct type of X; by using numeric indices we avoid ambiguity of
+// package-local identifiers and permit compact representations.
+//
+// Pos() returns the position of the ast.SelectorExpr.Sel for the
+// field, if explicit in the source.
+//
+// Example printed form:
+// t1 = t0.name [#1]
+//
+type Field struct {
+ register
+ X Value // struct
+ Field int // index into X.Type().(*types.Struct).Fields
+}
+
+// The IndexAddr instruction yields the address of the element at
+// index Index of collection X. Index is an integer expression.
+//
+// The elements of maps and strings are not addressable; use Lookup or
+// MapUpdate instead.
+//
+// Dynamically, this instruction panics if X evaluates to a nil *array
+// pointer.
+//
+// Type() returns a (possibly named) *types.Pointer.
+//
+// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if
+// explicit in the source.
+//
+// Example printed form:
+// t2 = &t0[t1]
+//
+type IndexAddr struct {
+ register
+ X Value // slice or *array,
+ Index Value // numeric index
+}
+
+// The Index instruction yields element Index of array X.
+//
+// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if
+// explicit in the source.
+//
+// Example printed form:
+// t2 = t0[t1]
+//
+type Index struct {
+ register
+ X Value // array
+ Index Value // integer index
+}
+
+// The Lookup instruction yields element Index of collection X, a map
+// or string. Index is an integer expression if X is a string or the
+// appropriate key type if X is a map.
+//
+// If CommaOk, the result is a 2-tuple of the value above and a
+// boolean indicating the result of a map membership test for the key.
+// The components of the tuple are accessed using Extract.
+//
+// Pos() returns the ast.IndexExpr.Lbrack, if explicit in the source.
+//
+// Example printed form:
+// t2 = t0[t1]
+// t5 = t3[t4],ok
+//
+type Lookup struct {
+ register
+ X Value // string or map
+ Index Value // numeric or key-typed index
+ CommaOk bool // return a value,ok pair
+}
+
+// SelectState is a helper for Select.
+// It represents one goal state and its corresponding communication.
+//
+type SelectState struct {
+ Dir types.ChanDir // direction of case (SendOnly or RecvOnly)
+ Chan Value // channel to use (for send or receive)
+ Send Value // value to send (for send)
+ Pos token.Pos // position of token.ARROW
+ DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
+}
+
+// The Select instruction tests whether (or blocks until) one
+// of the specified sent or received states is entered.
+//
+// Let n be the number of States for which Dir==RECV and T_i (0<=i<n)
+// be the element type of each such state's Chan.
+// Select returns an n+2-tuple
+// (index int, recvOk bool, r_0 T_0, ... r_n-1 T_n-1)
+// The tuple's components, described below, must be accessed via the
+// Extract instruction.
+//
+// If Blocking, select waits until exactly one state holds, i.e. a
+// channel becomes ready for the designated operation of sending or
+// receiving; select chooses one among the ready states
+// pseudorandomly, performs the send or receive operation, and sets
+// 'index' to the index of the chosen channel.
+//
+// If !Blocking, select doesn't block if no states hold; instead it
+// returns immediately with index equal to -1.
+//
+// If the chosen channel was used for a receive, the r_i component is
+// set to the received value, where i is the index of that state among
+// all n receive states; otherwise r_i has the zero value of type T_i.
+// Note that the receive index i is not the same as the state
+// index index.
+//
+// The second component of the triple, recvOk, is a boolean whose value
+// is true iff the selected operation was a receive and the receive
+// successfully yielded a value.
+//
+// Pos() returns the ast.SelectStmt.Select.
+//
+// Example printed form:
+// t3 = select nonblocking [<-t0, t1<-t2]
+// t4 = select blocking []
+//
+type Select struct {
+ register
+ States []*SelectState
+ Blocking bool
+}
+
+// The Range instruction yields an iterator over the domain and range
+// of X, which must be a string or map.
+//
+// Elements are accessed via Next.
+//
+// Type() returns an opaque and degenerate "rangeIter" type.
+//
+// Pos() returns the ast.RangeStmt.For.
+//
+// Example printed form:
+// t0 = range "hello":string
+//
+type Range struct {
+ register
+ X Value // string or map
+}
+
+// The Next instruction reads and advances the (map or string)
+// iterator Iter and returns a 3-tuple value (ok, k, v). If the
+// iterator is not exhausted, ok is true and k and v are the next
+// elements of the domain and range, respectively. Otherwise ok is
+// false and k and v are undefined.
+//
+// Components of the tuple are accessed using Extract.
+//
+// The IsString field distinguishes iterators over strings from those
+// over maps, as the Type() alone is insufficient: consider
+// map[int]rune.
+//
+// Type() returns a *types.Tuple for the triple (ok, k, v).
+// The types of k and/or v may be types.Invalid.
+//
+// Example printed form:
+// t1 = next t0
+//
+type Next struct {
+ register
+ Iter Value
+ IsString bool // true => string iterator; false => map iterator.
+}
+
+// The TypeAssert instruction tests whether interface value X has type
+// AssertedType.
+//
+// If !CommaOk, on success it returns v, the result of the conversion
+// (defined below); on failure it panics.
+//
+// If CommaOk: on success it returns a pair (v, true) where v is the
+// result of the conversion; on failure it returns (z, false) where z
+// is AssertedType's zero value. The components of the pair must be
+// accessed using the Extract instruction.
+//
+// If AssertedType is a concrete type, TypeAssert checks whether the
+// dynamic type in interface X is equal to it, and if so, the result
+// of the conversion is a copy of the value in the interface.
+//
+// If AssertedType is an interface, TypeAssert checks whether the
+// dynamic type of the interface is assignable to it, and if so, the
+// result of the conversion is a copy of the interface value X.
+// If AssertedType is a superinterface of X.Type(), the operation will
+// fail iff the operand is nil. (Contrast with ChangeInterface, which
+// performs no nil-check.)
+//
+// Type() reflects the actual type of the result, possibly a
+// 2-types.Tuple; AssertedType is the asserted type.
+//
+// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
+// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
+// instruction arose from an explicit e.(T) operation; or the
+// ast.CaseClause.Case if the instruction arose from a case of a
+// type-switch statement.
+//
+// Example printed form:
+// t1 = typeassert t0.(int)
+// t3 = typeassert,ok t2.(T)
+//
+type TypeAssert struct {
+ register
+ X Value
+ AssertedType types.Type
+ CommaOk bool
+}
+
+// The Extract instruction yields component Index of Tuple.
+//
+// This is used to access the results of instructions with multiple
+// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and
+// IndexExpr(Map).
+//
+// Example printed form:
+// t1 = extract t0 #1
+//
+type Extract struct {
+ register
+ Tuple Value
+ Index int
+}
+
+// Instructions executed for effect. They do not yield a value. --------------------
+
+// The Jump instruction transfers control to the sole successor of its
+// owning block.
+//
+// A Jump must be the last instruction of its containing BasicBlock.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// jump done
+//
+type Jump struct {
+ anInstruction
+}
+
+// The If instruction transfers control to one of the two successors
+// of its owning block, depending on the boolean Cond: the first if
+// true, the second if false.
+//
+// An If instruction must be the last instruction of its containing
+// BasicBlock.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// if t0 goto done else body
+//
+type If struct {
+ anInstruction
+ Cond Value
+}
+
+// The Return instruction returns values and control back to the calling
+// function.
+//
+// len(Results) is always equal to the number of results in the
+// function's signature.
+//
+// If len(Results) > 1, Return returns a tuple value with the specified
+// components which the caller must access using Extract instructions.
+//
+// There is no instruction to return a ready-made tuple like those
+// returned by a "value,ok"-mode TypeAssert, Lookup or UnOp(ARROW) or
+// a tail-call to a function with multiple result parameters.
+//
+// Return must be the last instruction of its containing BasicBlock.
+// Such a block has no successors.
+//
+// Pos() returns the ast.ReturnStmt.Return, if explicit in the source.
+//
+// Example printed form:
+// return
+// return nil:I, 2:int
+//
+type Return struct {
+ anInstruction
+ Results []Value
+ pos token.Pos
+}
+
+// The RunDefers instruction pops and invokes the entire stack of
+// procedure calls pushed by Defer instructions in this function.
+//
+// It is legal to encounter multiple 'rundefers' instructions in a
+// single control-flow path through a function; this is useful in
+// the combined init() function, for example.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// rundefers
+//
+type RunDefers struct {
+ anInstruction
+}
+
+// The Panic instruction initiates a panic with value X.
+//
+// A Panic instruction must be the last instruction of its containing
+// BasicBlock, which must have no successors.
+//
+// NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction;
+// they are treated as calls to a built-in function.
+//
+// Pos() returns the ast.CallExpr.Lparen if this panic was explicit
+// in the source.
+//
+// Example printed form:
+// panic t0
+//
+type Panic struct {
+ anInstruction
+ X Value // an interface{}
+ pos token.Pos
+}
+
+// The Go instruction creates a new goroutine and calls the specified
+// function within it.
+//
+// See CallCommon for generic function call documentation.
+//
+// Pos() returns the ast.GoStmt.Go.
+//
+// Example printed form:
+// go println(t0, t1)
+// go t3()
+// go invoke t5.Println(...t6)
+//
+type Go struct {
+ anInstruction
+ Call CallCommon
+ pos token.Pos
+}
+
+// The Defer instruction pushes the specified call onto a stack of
+// functions to be called by a RunDefers instruction or by a panic.
+//
+// See CallCommon for generic function call documentation.
+//
+// Pos() returns the ast.DeferStmt.Defer.
+//
+// Example printed form:
+// defer println(t0, t1)
+// defer t3()
+// defer invoke t5.Println(...t6)
+//
+type Defer struct {
+ anInstruction
+ Call CallCommon
+ pos token.Pos
+}
+
+// The Send instruction sends X on channel Chan.
+//
+// Pos() returns the ast.SendStmt.Arrow, if explicit in the source.
+//
+// Example printed form:
+// send t0 <- t1
+//
+type Send struct {
+ anInstruction
+ Chan, X Value
+ pos token.Pos
+}
+
+// The Store instruction stores Val at address Addr.
+// Stores can be of arbitrary types.
+//
+// Pos() returns the position of the source-level construct most closely
+// associated with the memory store operation.
+// Since implicit memory stores are numerous and varied and depend upon
+// implementation choices, the details are not specified.
+//
+// Example printed form:
+// *x = y
+//
+type Store struct {
+ anInstruction
+ Addr Value
+ Val Value
+ pos token.Pos
+}
+
+// The MapUpdate instruction updates the association of Map[Key] to
+// Value.
+//
+// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,
+// if explicit in the source.
+//
+// Example printed form:
+// t0[t1] = t2
+//
+type MapUpdate struct {
+ anInstruction
+ Map Value
+ Key Value
+ Value Value
+ pos token.Pos
+}
+
+// A DebugRef instruction maps a source-level expression Expr to the
+// SSA value X that represents the value (!IsAddr) or address (IsAddr)
+// of that expression.
+//
+// DebugRef is a pseudo-instruction: it has no dynamic effect.
+//
+// Pos() returns Expr.Pos(), the start position of the source-level
+// expression. This is not the same as the "designated" token as
+// documented at Value.Pos(). e.g. CallExpr.Pos() does not return the
+// position of the ("designated") Lparen token.
+//
+// If Expr is an *ast.Ident denoting a var or func, Object() returns
+// the object; though this information can be obtained from the type
+// checker, including it here greatly facilitates debugging.
+// For non-Ident expressions, Object() returns nil.
+//
+// DebugRefs are generated only for functions built with debugging
+// enabled; see Package.SetDebugMode() and the GlobalDebug builder
+// mode flag.
+//
+// DebugRefs are not emitted for ast.Idents referring to constants or
+// predeclared identifiers, since they are trivial and numerous.
+// Nor are they emitted for ast.ParenExprs.
+//
+// (By representing these as instructions, rather than out-of-band,
+// consistency is maintained during transformation passes by the
+// ordinary SSA renaming machinery.)
+//
+// Example printed form:
+// ; *ast.CallExpr @ 102:9 is t5
+// ; var x float64 @ 109:72 is x
+// ; address of *ast.CompositeLit @ 216:10 is t0
+//
+type DebugRef struct {
+ anInstruction
+ Expr ast.Expr // the referring expression (never *ast.ParenExpr)
+ object types.Object // the identity of the source var/func
+ IsAddr bool // Expr is addressable and X is the address it denotes
+ X Value // the value or address of Expr
+}
+
+// Embeddable mix-ins and helpers for common parts of other structs. -----------
+
+// register is a mix-in embedded by all SSA values that are also
+// instructions, i.e. virtual registers, and provides a uniform
+// implementation of most of the Value interface: Value.Name() is a
+// numbered register (e.g. "t0"); the other methods are field accessors.
+//
+// Temporary names are automatically assigned to each register on
+// completion of building a function in SSA form.
+//
+// Clients must not assume that the 'id' value (and the Name() derived
+// from it) is unique within a function. As always in this API,
+// semantics are determined only by identity; names exist only to
+// facilitate debugging.
+//
+type register struct {
+ anInstruction
+ num int // "name" of virtual register, e.g. "t0". Not guaranteed unique.
+ typ types.Type // type of virtual register
+ pos token.Pos // position of source expression, or NoPos
+ referrers []Instruction
+}
+
+// anInstruction is a mix-in embedded by all Instructions.
+// It provides the implementations of the Block and setBlock methods.
+type anInstruction struct {
+ block *BasicBlock // the basic block of this instruction
+}
+
+// CallCommon is contained by Go, Defer and Call to hold the
+// common parts of a function or method call.
+//
+// Each CallCommon exists in one of two modes, function call and
+// interface method invocation, or "call" and "invoke" for short.
+//
+// 1. "call" mode: when Method is nil (!IsInvoke), a CallCommon
+// represents an ordinary function call of the value in Value,
+// which may be a *Builtin, a *Function or any other value of kind
+// 'func'.
+//
+// Value may be one of:
+// (a) a *Function, indicating a statically dispatched call
+// to a package-level function, an anonymous function, or
+// a method of a named type.
+// (b) a *MakeClosure, indicating an immediately applied
+// function literal with free variables.
+// (c) a *Builtin, indicating a statically dispatched call
+// to a built-in function.
+// (d) any other value, indicating a dynamically dispatched
+// function call.
+// StaticCallee returns the identity of the callee in cases
+// (a) and (b), nil otherwise.
+//
+// Args contains the arguments to the call. If Value is a method,
+// Args[0] contains the receiver parameter.
+//
+// Example printed form:
+// t2 = println(t0, t1)
+// go t3()
+// defer t5(...t6)
+//
+// 2. "invoke" mode: when Method is non-nil (IsInvoke), a CallCommon
+// represents a dynamically dispatched call to an interface method.
+// In this mode, Value is the interface value and Method is the
+// interface's abstract method. Note: an abstract method may be
+// shared by multiple interfaces due to embedding; Value.Type()
+// provides the specific interface used for this call.
+//
+// Value is implicitly supplied to the concrete method implementation
+// as the receiver parameter; in other words, Args[0] holds not the
+// receiver but the first true argument.
+//
+// Example printed form:
+// t1 = invoke t0.String()
+// go invoke t3.Run(t2)
+// defer invoke t4.Handle(...t5)
+//
+// For all calls to variadic functions (Signature().Variadic()),
+// the last element of Args is a slice.
+//
+type CallCommon struct {
+ Value Value // receiver (invoke mode) or func value (call mode)
+ Method *types.Func // abstract method (invoke mode)
+ Args []Value // actual parameters (in static method call, includes receiver)
+ pos token.Pos // position of CallExpr.Lparen, iff explicit in source
+}
+
+// IsInvoke returns true if this call has "invoke" (not "call") mode.
+func (c *CallCommon) IsInvoke() bool {
+ return c.Method != nil
+}
+
+func (c *CallCommon) Pos() token.Pos { return c.pos }
+
+// Signature returns the signature of the called function.
+//
+// For an "invoke"-mode call, the signature of the interface method is
+// returned.
+//
+// In either "call" or "invoke" mode, if the callee is a method, its
+// receiver is represented by sig.Recv, not sig.Params().At(0).
+//
+func (c *CallCommon) Signature() *types.Signature {
+ if c.Method != nil {
+ return c.Method.Type().(*types.Signature)
+ }
+ return c.Value.Type().Underlying().(*types.Signature)
+}
+
+// StaticCallee returns the callee if this is a trivially static
+// "call"-mode call to a function.
+func (c *CallCommon) StaticCallee() *Function {
+ switch fn := c.Value.(type) {
+ case *Function:
+ return fn
+ case *MakeClosure:
+ return fn.Fn.(*Function)
+ }
+ return nil
+}
+
+// Description returns a description of the mode of this call suitable
+// for a user interface, e.g., "static method call".
+func (c *CallCommon) Description() string {
+ switch fn := c.Value.(type) {
+ case *Builtin:
+ return "built-in function call"
+ case *MakeClosure:
+ return "static function closure call"
+ case *Function:
+ if fn.Signature.Recv() != nil {
+ return "static method call"
+ }
+ return "static function call"
+ }
+ if c.IsInvoke() {
+ return "dynamic method call" // ("invoke" mode)
+ }
+ return "dynamic function call"
+}
+
+// The CallInstruction interface, implemented by *Go, *Defer and *Call,
+// exposes the common parts of function-calling instructions,
+// yet provides a way back to the Value defined by *Call alone.
+//
+type CallInstruction interface {
+ Instruction
+ Common() *CallCommon // returns the common parts of the call
+ Value() *Call // returns the result value of the call (*Call) or nil (*Go, *Defer)
+}
+
+func (s *Call) Common() *CallCommon { return &s.Call }
+func (s *Defer) Common() *CallCommon { return &s.Call }
+func (s *Go) Common() *CallCommon { return &s.Call }
+
+func (s *Call) Value() *Call { return s }
+func (s *Defer) Value() *Call { return nil }
+func (s *Go) Value() *Call { return nil }
+
+func (v *Builtin) Type() types.Type { return v.sig }
+func (v *Builtin) Name() string { return v.name }
+func (*Builtin) Referrers() *[]Instruction { return nil }
+func (v *Builtin) Pos() token.Pos { return token.NoPos }
+func (v *Builtin) Object() types.Object { return types.Universe.Lookup(v.name) }
+func (v *Builtin) Parent() *Function { return nil }
+
+func (v *FreeVar) Type() types.Type { return v.typ }
+func (v *FreeVar) Name() string { return v.name }
+func (v *FreeVar) Referrers() *[]Instruction { return &v.referrers }
+func (v *FreeVar) Pos() token.Pos { return v.pos }
+func (v *FreeVar) Parent() *Function { return v.parent }
+
+func (v *Global) Type() types.Type { return v.typ }
+func (v *Global) Name() string { return v.name }
+func (v *Global) Parent() *Function { return nil }
+func (v *Global) Pos() token.Pos { return v.pos }
+func (v *Global) Referrers() *[]Instruction { return nil }
+func (v *Global) Token() token.Token { return token.VAR }
+func (v *Global) Object() types.Object { return v.object }
+func (v *Global) String() string { return v.RelString(nil) }
+func (v *Global) Package() *Package { return v.Pkg }
+func (v *Global) RelString(from *types.Package) string { return relString(v, from) }
+
+func (v *Function) Name() string { return v.name }
+func (v *Function) Type() types.Type { return v.Signature }
+func (v *Function) Pos() token.Pos { return v.pos }
+func (v *Function) Token() token.Token { return token.FUNC }
+func (v *Function) Object() types.Object { return v.object }
+func (v *Function) String() string { return v.RelString(nil) }
+func (v *Function) Package() *Package { return v.Pkg }
+func (v *Function) Parent() *Function { return v.parent }
+func (v *Function) Referrers() *[]Instruction {
+ if v.parent != nil {
+ return &v.referrers
+ }
+ return nil
+}
+
+func (v *Parameter) Type() types.Type { return v.typ }
+func (v *Parameter) Name() string { return v.name }
+func (v *Parameter) Object() types.Object { return v.object }
+func (v *Parameter) Referrers() *[]Instruction { return &v.referrers }
+func (v *Parameter) Pos() token.Pos { return v.pos }
+func (v *Parameter) Parent() *Function { return v.parent }
+
+func (v *Alloc) Type() types.Type { return v.typ }
+func (v *Alloc) Referrers() *[]Instruction { return &v.referrers }
+func (v *Alloc) Pos() token.Pos { return v.pos }
+
+func (v *register) Type() types.Type { return v.typ }
+func (v *register) setType(typ types.Type) { v.typ = typ }
+func (v *register) Name() string { return fmt.Sprintf("t%d", v.num) }
+func (v *register) setNum(num int) { v.num = num }
+func (v *register) Referrers() *[]Instruction { return &v.referrers }
+func (v *register) Pos() token.Pos { return v.pos }
+func (v *register) setPos(pos token.Pos) { v.pos = pos }
+
+func (v *anInstruction) Parent() *Function { return v.block.parent }
+func (v *anInstruction) Block() *BasicBlock { return v.block }
+func (v *anInstruction) setBlock(block *BasicBlock) { v.block = block }
+func (v *anInstruction) Referrers() *[]Instruction { return nil }
+
+func (t *Type) Name() string { return t.object.Name() }
+func (t *Type) Pos() token.Pos { return t.object.Pos() }
+func (t *Type) Type() types.Type { return t.object.Type() }
+func (t *Type) Token() token.Token { return token.TYPE }
+func (t *Type) Object() types.Object { return t.object }
+func (t *Type) String() string { return t.RelString(nil) }
+func (t *Type) Package() *Package { return t.pkg }
+func (t *Type) RelString(from *types.Package) string { return relString(t, from) }
+
+func (c *NamedConst) Name() string { return c.object.Name() }
+func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
+func (c *NamedConst) String() string { return c.RelString(nil) }
+func (c *NamedConst) Type() types.Type { return c.object.Type() }
+func (c *NamedConst) Token() token.Token { return token.CONST }
+func (c *NamedConst) Object() types.Object { return c.object }
+func (c *NamedConst) Package() *Package { return c.pkg }
+func (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) }
+
+// Func returns the package-level function of the specified name,
+// or nil if not found.
+//
+func (p *Package) Func(name string) (f *Function) {
+ f, _ = p.Members[name].(*Function)
+ return
+}
+
+// Var returns the package-level variable of the specified name,
+// or nil if not found.
+//
+func (p *Package) Var(name string) (g *Global) {
+ g, _ = p.Members[name].(*Global)
+ return
+}
+
+// Const returns the package-level constant of the specified name,
+// or nil if not found.
+//
+func (p *Package) Const(name string) (c *NamedConst) {
+ c, _ = p.Members[name].(*NamedConst)
+ return
+}
+
+// Type returns the package-level type of the specified name,
+// or nil if not found.
+//
+func (p *Package) Type(name string) (t *Type) {
+ t, _ = p.Members[name].(*Type)
+ return
+}
+
+func (v *Call) Pos() token.Pos { return v.Call.pos }
+func (s *Defer) Pos() token.Pos { return s.pos }
+func (s *Go) Pos() token.Pos { return s.pos }
+func (s *MapUpdate) Pos() token.Pos { return s.pos }
+func (s *Panic) Pos() token.Pos { return s.pos }
+func (s *Return) Pos() token.Pos { return s.pos }
+func (s *Send) Pos() token.Pos { return s.pos }
+func (s *Store) Pos() token.Pos { return s.pos }
+func (s *If) Pos() token.Pos { return token.NoPos }
+func (s *Jump) Pos() token.Pos { return token.NoPos }
+func (s *RunDefers) Pos() token.Pos { return token.NoPos }
+func (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() }
+
+// Operands.
+
+func (v *Alloc) Operands(rands []*Value) []*Value {
+ return rands
+}
+
+func (v *BinOp) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Y)
+}
+
+func (c *CallCommon) Operands(rands []*Value) []*Value {
+ rands = append(rands, &c.Value)
+ for i := range c.Args {
+ rands = append(rands, &c.Args[i])
+ }
+ return rands
+}
+
+func (s *Go) Operands(rands []*Value) []*Value {
+ return s.Call.Operands(rands)
+}
+
+func (s *Call) Operands(rands []*Value) []*Value {
+ return s.Call.Operands(rands)
+}
+
+func (s *Defer) Operands(rands []*Value) []*Value {
+ return s.Call.Operands(rands)
+}
+
+func (v *ChangeInterface) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *ChangeType) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *Convert) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (s *DebugRef) Operands(rands []*Value) []*Value {
+ return append(rands, &s.X)
+}
+
+func (v *Extract) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Tuple)
+}
+
+func (v *Field) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *FieldAddr) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (s *If) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Cond)
+}
+
+func (v *Index) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Index)
+}
+
+func (v *IndexAddr) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Index)
+}
+
+func (*Jump) Operands(rands []*Value) []*Value {
+ return rands
+}
+
+func (v *Lookup) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Index)
+}
+
+func (v *MakeChan) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Size)
+}
+
+func (v *MakeClosure) Operands(rands []*Value) []*Value {
+ rands = append(rands, &v.Fn)
+ for i := range v.Bindings {
+ rands = append(rands, &v.Bindings[i])
+ }
+ return rands
+}
+
+func (v *MakeInterface) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *MakeMap) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Reserve)
+}
+
+func (v *MakeSlice) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Len, &v.Cap)
+}
+
+func (v *MapUpdate) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Map, &v.Key, &v.Value)
+}
+
+func (v *Next) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Iter)
+}
+
+func (s *Panic) Operands(rands []*Value) []*Value {
+ return append(rands, &s.X)
+}
+
+func (v *Phi) Operands(rands []*Value) []*Value {
+ for i := range v.Edges {
+ rands = append(rands, &v.Edges[i])
+ }
+ return rands
+}
+
+func (v *Range) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (s *Return) Operands(rands []*Value) []*Value {
+ for i := range s.Results {
+ rands = append(rands, &s.Results[i])
+ }
+ return rands
+}
+
+func (*RunDefers) Operands(rands []*Value) []*Value {
+ return rands
+}
+
+func (v *Select) Operands(rands []*Value) []*Value {
+ for i := range v.States {
+ rands = append(rands, &v.States[i].Chan, &v.States[i].Send)
+ }
+ return rands
+}
+
+func (s *Send) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Chan, &s.X)
+}
+
+func (v *Slice) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Low, &v.High, &v.Max)
+}
+
+func (s *Store) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Addr, &s.Val)
+}
+
+func (v *TypeAssert) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *UnOp) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+// Non-Instruction Values:
+func (v *Builtin) Operands(rands []*Value) []*Value { return rands }
+func (v *FreeVar) Operands(rands []*Value) []*Value { return rands }
+func (v *Const) Operands(rands []*Value) []*Value { return rands }
+func (v *Function) Operands(rands []*Value) []*Value { return rands }
+func (v *Global) Operands(rands []*Value) []*Value { return rands }
+func (v *Parameter) Operands(rands []*Value) []*Value { return rands }
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/load.go b/go/src/golang.org/x/tools/go/ssa/ssautil/load.go
index c2b8ce1..7c57838 100644
--- a/go/src/golang.org/x/tools/go/ssa/ssautil/load.go
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/load.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssautil
// This file defines utility functions for constructing programs in SSA form.
@@ -9,10 +11,10 @@
import (
"go/ast"
"go/token"
+ "go/types"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// CreateProgram returns a new program in SSA form, given a program
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/load14.go b/go/src/golang.org/x/tools/go/ssa/ssautil/load14.go
new file mode 100644
index 0000000..752a9d9
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/load14.go
@@ -0,0 +1,97 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssautil
+
+// This file defines utility functions for constructing programs in SSA form.
+
+import (
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+// CreateProgram returns a new program in SSA form, given a program
+// loaded from source. An SSA package is created for each transitively
+// error-free package of lprog.
+//
+// Code for bodies of functions is not built until BuildAll() is called
+// on the result.
+//
+// mode controls diagnostics and checking during SSA construction.
+//
+func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
+ prog := ssa.NewProgram(lprog.Fset, mode)
+
+ for _, info := range lprog.AllPackages {
+ if info.TransitivelyErrorFree {
+ prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
+ }
+ }
+
+ return prog
+}
+
+// BuildPackage builds an SSA program with IR for a single package.
+//
+// It populates pkg by type-checking the specified file ASTs. All
+// dependencies are loaded using the importer specified by tc, which
+// typically loads compiler export data; SSA code cannot be built for
+// those packages. BuildPackage then constructs an ssa.Program with all
+// dependency packages created, and builds and returns the SSA package
+// corresponding to pkg.
+//
+// The caller must have set pkg.Path() to the import path.
+//
+// The operation fails if there were any type-checking or import errors.
+//
+// See ../ssa/example_test.go for an example.
+//
+func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
+ if fset == nil {
+ panic("no token.FileSet")
+ }
+ if pkg.Path() == "" {
+ panic("package has no import path")
+ }
+
+ info := &types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ Implicits: make(map[ast.Node]types.Object),
+ Scopes: make(map[ast.Node]*types.Scope),
+ Selections: make(map[*ast.SelectorExpr]*types.Selection),
+ }
+ if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
+ return nil, nil, err
+ }
+
+ prog := ssa.NewProgram(fset, mode)
+
+ // Create SSA packages for all imports.
+ // Order is not significant.
+ created := make(map[*types.Package]bool)
+ var createAll func(pkgs []*types.Package)
+ createAll = func(pkgs []*types.Package) {
+ for _, p := range pkgs {
+ if !created[p] {
+ created[p] = true
+ prog.CreatePackage(p, nil, nil, true)
+ createAll(p.Imports())
+ }
+ }
+ }
+ createAll(pkg.Imports())
+
+ // Create and build the primary package.
+ ssapkg := prog.CreatePackage(pkg, files, info, false)
+ ssapkg.Build()
+ return ssapkg, info, nil
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/load14_test.go b/go/src/golang.org/x/tools/go/ssa/ssautil/load14_test.go
new file mode 100644
index 0000000..41955ec
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/load14_test.go
@@ -0,0 +1,67 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssautil_test
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "os"
+ "testing"
+
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+
+ _ "golang.org/x/tools/go/gcimporter"
+)
+
+const hello = `package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello, world")
+}
+`
+
+func TestBuildPackage(t *testing.T) {
+ // There is a more substantial test of BuildPackage and the
+ // SSA program it builds in ../ssa/builder_test.go.
+
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "hello.go", hello, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pkg := types.NewPackage("hello", "")
+ ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pkg.Name() != "main" {
+ t.Errorf("pkg.Name() = %s, want main", pkg.Name())
+ }
+ if ssapkg.Func("main") == nil {
+ ssapkg.WriteTo(os.Stderr)
+ t.Errorf("ssapkg has no main function")
+ }
+}
+
+func TestBuildPackage_MissingImport(t *testing.T) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "bad.go", `package bad; import "missing"`, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pkg := types.NewPackage("bad", "")
+ ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
+ if err == nil || ssapkg != nil {
+ t.Fatal("BuildPackage succeeded unexpectedly")
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/load_test.go b/go/src/golang.org/x/tools/go/ssa/ssautil/load_test.go
index 458d2dc..8ce73bc 100644
--- a/go/src/golang.org/x/tools/go/ssa/ssautil/load_test.go
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/load_test.go
@@ -2,19 +2,20 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssautil_test
import (
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
"os"
"testing"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
-
- _ "golang.org/x/tools/go/gcimporter"
)
const hello = `package main
@@ -37,7 +38,7 @@
}
pkg := types.NewPackage("hello", "")
- ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
+ ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, pkg, []*ast.File{f}, 0)
if err != nil {
t.Fatal(err)
}
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/switch.go b/go/src/golang.org/x/tools/go/ssa/ssautil/switch.go
index 70fff9c..2fcc167 100644
--- a/go/src/golang.org/x/tools/go/ssa/ssautil/switch.go
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/switch.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssautil
// This file implements discovery of switch and type-switch constructs
@@ -22,9 +24,9 @@
"bytes"
"fmt"
"go/token"
+ "go/types"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// A ConstCase represents a single constant comparison.
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/switch14.go b/go/src/golang.org/x/tools/go/ssa/ssautil/switch14.go
new file mode 100644
index 0000000..b2f7f21
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/switch14.go
@@ -0,0 +1,236 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssautil
+
+// This file implements discovery of switch and type-switch constructs
+// from low-level control flow.
+//
+// Many techniques exist for compiling a high-level switch with
+// constant cases to efficient machine code. The optimal choice will
+// depend on the data type, the specific case values, the code in the
+// body of each case, and the hardware.
+// Some examples:
+// - a lookup table (for a switch that maps constants to constants)
+// - a computed goto
+// - a binary tree
+// - a perfect hash
+// - a two-level switch (to partition constant strings by their first byte).
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+// A ConstCase represents a single constant comparison.
+// It is part of a Switch.
+type ConstCase struct {
+ Block *ssa.BasicBlock // block performing the comparison
+ Body *ssa.BasicBlock // body of the case
+ Value *ssa.Const // case comparand
+}
+
+// A TypeCase represents a single type assertion.
+// It is part of a Switch.
+type TypeCase struct {
+ Block *ssa.BasicBlock // block performing the type assert
+ Body *ssa.BasicBlock // body of the case
+ Type types.Type // case type
+ Binding ssa.Value // value bound by this case
+}
+
+// A Switch is a logical high-level control flow operation
+// (a multiway branch) discovered by analysis of a CFG containing
+// only if/else chains. It is not part of the ssa.Instruction set.
+//
+// One of ConstCases and TypeCases has length >= 2;
+// the other is nil.
+//
+// In a value switch, the list of cases may contain duplicate constants.
+// A type switch may contain duplicate types, or types assignable
+// to an interface type also in the list.
+// TODO(adonovan): eliminate such duplicates.
+//
+type Switch struct {
+ Start *ssa.BasicBlock // block containing start of if/else chain
+ X ssa.Value // the switch operand
+ ConstCases []ConstCase // ordered list of constant comparisons
+ TypeCases []TypeCase // ordered list of type assertions
+ Default *ssa.BasicBlock // successor if all comparisons fail
+}
+
+func (sw *Switch) String() string {
+ // We represent each block by the String() of its
+ // first Instruction, e.g. "print(42:int)".
+ var buf bytes.Buffer
+ if sw.ConstCases != nil {
+ fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
+ for _, c := range sw.ConstCases {
+ fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0])
+ }
+ } else {
+ fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
+ for _, c := range sw.TypeCases {
+ fmt.Fprintf(&buf, "case %s %s: %s\n",
+ c.Binding.Name(), c.Type, c.Body.Instrs[0])
+ }
+ }
+ if sw.Default != nil {
+ fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0])
+ }
+ fmt.Fprintf(&buf, "}")
+ return buf.String()
+}
+
+// Switches examines the control-flow graph of fn and returns the
+// set of inferred value and type switches. A value switch tests an
+// ssa.Value for equality against two or more compile-time constant
+// values. Switches involving link-time constants (addresses) are
+// ignored. A type switch type-asserts an ssa.Value against two or
+// more types.
+//
+// The switches are returned in dominance order.
+//
+// The resulting switches do not necessarily correspond to uses of the
+// 'switch' keyword in the source: for example, a single source-level
+// switch statement with non-constant cases may result in zero, one or
+// many Switches, one per plural sequence of constant cases.
+// Switches may even be inferred from if/else- or goto-based control flow.
+// (In general, the control flow constructs of the source program
+// cannot be faithfully reproduced from the SSA representation.)
+//
+func Switches(fn *ssa.Function) []Switch {
+ // Traverse the CFG in dominance order, so we don't
+ // enter an if/else-chain in the middle.
+ var switches []Switch
+ seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
+ for _, b := range fn.DomPreorder() {
+ if x, k := isComparisonBlock(b); x != nil {
+ // Block b starts a switch.
+ sw := Switch{Start: b, X: x}
+ valueSwitch(&sw, k, seen)
+ if len(sw.ConstCases) > 1 {
+ switches = append(switches, sw)
+ }
+ }
+
+ if y, x, T := isTypeAssertBlock(b); y != nil {
+ // Block b starts a type switch.
+ sw := Switch{Start: b, X: x}
+ typeSwitch(&sw, y, T, seen)
+ if len(sw.TypeCases) > 1 {
+ switches = append(switches, sw)
+ }
+ }
+ }
+ return switches
+}
+
+func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) {
+ b := sw.Start
+ x := sw.X
+ for x == sw.X {
+ if seen[b] {
+ break
+ }
+ seen[b] = true
+
+ sw.ConstCases = append(sw.ConstCases, ConstCase{
+ Block: b,
+ Body: b.Succs[0],
+ Value: k,
+ })
+ b = b.Succs[1]
+ if len(b.Instrs) > 2 {
+ // Block b contains not just 'if x == k',
+ // so it may have side effects that
+ // make it unsafe to elide.
+ break
+ }
+ if len(b.Preds) != 1 {
+ // Block b has multiple predecessors,
+ // so it cannot be treated as a case.
+ break
+ }
+ x, k = isComparisonBlock(b)
+ }
+ sw.Default = b
+}
+
+func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) {
+ b := sw.Start
+ x := sw.X
+ for x == sw.X {
+ if seen[b] {
+ break
+ }
+ seen[b] = true
+
+ sw.TypeCases = append(sw.TypeCases, TypeCase{
+ Block: b,
+ Body: b.Succs[0],
+ Type: T,
+ Binding: y,
+ })
+ b = b.Succs[1]
+ if len(b.Instrs) > 4 {
+ // Block b contains not just
+ // {TypeAssert; Extract #0; Extract #1; If}
+ // so it may have side effects that
+ // make it unsafe to elide.
+ break
+ }
+ if len(b.Preds) != 1 {
+ // Block b has multiple predecessors,
+ // so it cannot be treated as a case.
+ break
+ }
+ y, x, T = isTypeAssertBlock(b)
+ }
+ sw.Default = b
+}
+
+// isComparisonBlock returns the operands (v, k) if a block ends with
+// a comparison v==k, where k is a compile-time constant.
+//
+func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) {
+ if n := len(b.Instrs); n >= 2 {
+ if i, ok := b.Instrs[n-1].(*ssa.If); ok {
+ if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {
+ if k, ok := binop.Y.(*ssa.Const); ok {
+ return binop.X, k
+ }
+ if k, ok := binop.X.(*ssa.Const); ok {
+ return binop.Y, k
+ }
+ }
+ }
+ }
+ return
+}
+
+// isTypeAssertBlock returns the operands (y, x, T) if a block ends with
+// a type assertion "if y, ok := x.(T); ok {".
+//
+func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) {
+ if n := len(b.Instrs); n >= 4 {
+ if i, ok := b.Instrs[n-1].(*ssa.If); ok {
+ if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 {
+ if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b {
+ // hack: relies upon instruction ordering.
+ if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok {
+ return ext0, ta.X, ta.AssertedType
+ }
+ }
+ }
+ }
+ }
+ return
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/ssautil/visit.go b/go/src/golang.org/x/tools/go/ssa/ssautil/visit.go
index 30843c3..6c51f93 100644
--- a/go/src/golang.org/x/tools/go/ssa/ssautil/visit.go
+++ b/go/src/golang.org/x/tools/go/ssa/ssautil/visit.go
@@ -44,7 +44,7 @@
for _, T := range visit.prog.RuntimeTypes() {
mset := visit.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
- visit.function(visit.prog.Method(mset.At(i)))
+ visit.function(visit.prog.MethodValue(mset.At(i)))
}
}
}
diff --git a/go/src/golang.org/x/tools/go/ssa/stdlib_test.go b/go/src/golang.org/x/tools/go/ssa/stdlib_test.go
index d339d07..99c615d 100644
--- a/go/src/golang.org/x/tools/go/ssa/stdlib_test.go
+++ b/go/src/golang.org/x/tools/go/ssa/stdlib_test.go
@@ -27,6 +27,18 @@
"golang.org/x/tools/go/ssa/ssautil"
)
+// Skip the set of packages that transitively depend on
+// cmd/internal/objfile, which uses vendoring,
+// which go/loader does not yet support.
+// TODO(adonovan): add support for vendoring and delete this.
+var skip = map[string]bool{
+ "cmd/addr2line": true,
+ "cmd/internal/objfile": true,
+ "cmd/nm": true,
+ "cmd/objdump": true,
+ "cmd/pprof": true,
+}
+
func bytesAllocated() uint64 {
runtime.GC()
var stats runtime.MemStats
@@ -35,6 +47,9 @@
}
func TestStdlib(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode; too slow (golang.org/issue/14113)")
+ }
// Load, parse and type-check the program.
t0 := time.Now()
alloc0 := bytesAllocated()
@@ -43,9 +58,11 @@
ctxt := build.Default // copy
ctxt.GOPATH = "" // disable GOPATH
conf := loader.Config{Build: &ctxt}
- if _, err := conf.FromArgs(buildutil.AllPackages(conf.Build), true); err != nil {
- t.Errorf("FromArgs failed: %v", err)
- return
+ for _, path := range buildutil.AllPackages(conf.Build) {
+ if skip[path] {
+ continue
+ }
+ conf.ImportWithTests(path)
}
iprog, err := conf.Load()
@@ -66,7 +83,7 @@
t2 := time.Now()
// Build SSA.
- prog.BuildAll()
+ prog.Build()
t3 := time.Now()
alloc3 := bytesAllocated()
diff --git a/go/src/golang.org/x/tools/go/ssa/testdata/valueforexpr.go b/go/src/golang.org/x/tools/go/ssa/testdata/valueforexpr.go
index 028c153..4a2cb85 100644
--- a/go/src/golang.org/x/tools/go/ssa/testdata/valueforexpr.go
+++ b/go/src/golang.org/x/tools/go/ssa/testdata/valueforexpr.go
@@ -12,7 +12,11 @@
_ = /*@Parameter*/ (unspilled)
_ = /*@<nil>*/ (1 + 2) // (constant)
i := 0
+
+ f := func() (int, int) { return 0, 0 }
+
/*@Call*/ (print( /*@BinOp*/ (i + 1)))
+ _, _ = /*@Call*/ (f())
ch := /*@MakeChan*/ (make(chan int))
/*@UnOp*/ (<-ch)
x := /*@UnOp*/ (<-ch)
diff --git a/go/src/golang.org/x/tools/go/ssa/testmain.go b/go/src/golang.org/x/tools/go/ssa/testmain.go
index a7b1242..48b184a 100644
--- a/go/src/golang.org/x/tools/go/ssa/testmain.go
+++ b/go/src/golang.org/x/tools/go/ssa/testmain.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// CreateTestMainPackage synthesizes a main package that runs all the
@@ -10,13 +12,12 @@
import (
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"os"
"sort"
"strings"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
)
// FindTests returns the list of packages that define at least one Test,
@@ -102,7 +103,7 @@
Prog: prog,
Members: make(map[string]Member),
values: make(map[types.Object]Value),
- Object: types.NewPackage("test$main", "main"),
+ Pkg: types.NewPackage("test$main", "main"),
}
// Build package's init function.
@@ -127,23 +128,23 @@
v.setType(types.NewTuple())
init.emit(&v)
- pkgpaths = append(pkgpaths, pkg.Object.Path())
+ pkgpaths = append(pkgpaths, pkg.Pkg.Path())
}
sort.Strings(pkgpaths)
init.emit(new(Return))
init.finishBody()
testmain.init = init
- testmain.Object.MarkComplete()
+ testmain.Pkg.MarkComplete()
testmain.Members[init.name] = init
// For debugging convenience, define an unexported const
// that enumerates the packages.
- packagesConst := types.NewConst(token.NoPos, testmain.Object, "packages", tString,
+ packagesConst := types.NewConst(token.NoPos, testmain.Pkg, "packages", tString,
exact.MakeString(strings.Join(pkgpaths, " ")))
memberFromObject(testmain, packagesConst, nil)
// Create main *types.Func and *ssa.Function
- mainFunc := types.NewFunc(token.NoPos, testmain.Object, "main", new(types.Signature))
+ mainFunc := types.NewFunc(token.NoPos, testmain.Pkg, "main", new(types.Signature))
memberFromObject(testmain, mainFunc, nil)
main := testmain.Func("main")
main.Synthetic = "test main function"
@@ -222,7 +223,7 @@
sanityCheckPackage(testmain)
}
- prog.packages[testmain.Object] = testmain
+ prog.packages[testmain.Pkg] = testmain
return testmain
}
@@ -241,6 +242,12 @@
tPtrElem := types.NewPointer(tElem)
tPtrFunc := types.NewPointer(funcField(slice))
+ // TODO(adonovan): fix: populate the
+ // testing.InternalExample.Output field correctly so that tests
+ // work correctly under the interpreter. This requires that we
+ // do this step using ASTs, not *ssa.Functions---quite a
+ // redesign. See also the fake runExample in go/ssa/interp.
+
// Emit: array = new [n]testing.InternalTest
tArray := types.NewArray(tElem, int64(len(testfuncs)))
array := emitNew(fn, tArray, token.NoPos)
diff --git a/go/src/golang.org/x/tools/go/ssa/testmain14.go b/go/src/golang.org/x/tools/go/ssa/testmain14.go
new file mode 100644
index 0000000..ddd27a1
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/testmain14.go
@@ -0,0 +1,304 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// CreateTestMainPackage synthesizes a main package that runs all the
+// tests of the supplied packages.
+// It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing.
+
+import (
+ "go/ast"
+ "go/token"
+ "os"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+)
+
+// FindTests returns the list of packages that define at least one Test,
+// Example or Benchmark function (as defined by "go test"), and the
+// lists of all such functions.
+//
+func FindTests(pkgs []*Package) (testpkgs []*Package, tests, benchmarks, examples []*Function) {
+ if len(pkgs) == 0 {
+ return
+ }
+ prog := pkgs[0].Prog
+
+ // The first two of these may be nil: if the program doesn't import "testing",
+ // it can't contain any tests, but it may yet contain Examples.
+ var testSig *types.Signature // func(*testing.T)
+ var benchmarkSig *types.Signature // func(*testing.B)
+ var exampleSig = types.NewSignature(nil, nil, nil, false) // func()
+
+ // Obtain the types from the parameters of testing.Main().
+ if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
+ params := testingPkg.Func("Main").Signature.Params()
+ testSig = funcField(params.At(1).Type())
+ benchmarkSig = funcField(params.At(2).Type())
+ }
+
+ seen := make(map[*Package]bool)
+ for _, pkg := range pkgs {
+ if pkg.Prog != prog {
+ panic("wrong Program")
+ }
+
+ // TODO(adonovan): use a stable order, e.g. lexical.
+ for _, mem := range pkg.Members {
+ if f, ok := mem.(*Function); ok &&
+ ast.IsExported(f.Name()) &&
+ strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
+
+ switch {
+ case testSig != nil && isTestSig(f, "Test", testSig):
+ tests = append(tests, f)
+ case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig):
+ benchmarks = append(benchmarks, f)
+ case isTestSig(f, "Example", exampleSig):
+ examples = append(examples, f)
+ default:
+ continue
+ }
+
+ if !seen[pkg] {
+ seen[pkg] = true
+ testpkgs = append(testpkgs, pkg)
+ }
+ }
+ }
+ }
+ return
+}
+
+// Like isTest, but checks the signature too.
+func isTestSig(f *Function, prefix string, sig *types.Signature) bool {
+ return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig)
+}
+
+// If non-nil, testMainStartBodyHook is called immediately after
+// startBody for main.init and main.main, making it easy for users to
+// add custom imports and initialization steps for proprietary build
+// systems that don't exactly follow 'go test' conventions.
+var testMainStartBodyHook func(*Function)
+
+// CreateTestMainPackage creates and returns a synthetic "main"
+// package that runs all the tests of the supplied packages, similar
+// to the one that would be created by the 'go test' tool.
+//
+// It returns nil if the program contains no tests.
+//
+func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
+ pkgs, tests, benchmarks, examples := FindTests(pkgs)
+ if len(pkgs) == 0 {
+ return nil
+ }
+
+ testmain := &Package{
+ Prog: prog,
+ Members: make(map[string]Member),
+ values: make(map[types.Object]Value),
+ Pkg: types.NewPackage("test$main", "main"),
+ }
+
+ // Build package's init function.
+ init := &Function{
+ name: "init",
+ Signature: new(types.Signature),
+ Synthetic: "package initializer",
+ Pkg: testmain,
+ Prog: prog,
+ }
+ init.startBody()
+
+ if testMainStartBodyHook != nil {
+ testMainStartBodyHook(init)
+ }
+
+ // Initialize packages to test.
+ var pkgpaths []string
+ for _, pkg := range pkgs {
+ var v Call
+ v.Call.Value = pkg.init
+ v.setType(types.NewTuple())
+ init.emit(&v)
+
+ pkgpaths = append(pkgpaths, pkg.Pkg.Path())
+ }
+ sort.Strings(pkgpaths)
+ init.emit(new(Return))
+ init.finishBody()
+ testmain.init = init
+ testmain.Pkg.MarkComplete()
+ testmain.Members[init.name] = init
+
+ // For debugging convenience, define an unexported const
+ // that enumerates the packages.
+ packagesConst := types.NewConst(token.NoPos, testmain.Pkg, "packages", tString,
+ exact.MakeString(strings.Join(pkgpaths, " ")))
+ memberFromObject(testmain, packagesConst, nil)
+
+ // Create main *types.Func and *ssa.Function
+ mainFunc := types.NewFunc(token.NoPos, testmain.Pkg, "main", new(types.Signature))
+ memberFromObject(testmain, mainFunc, nil)
+ main := testmain.Func("main")
+ main.Synthetic = "test main function"
+
+ main.startBody()
+
+ if testMainStartBodyHook != nil {
+ testMainStartBodyHook(main)
+ }
+
+ if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
+ testingMain := testingPkg.Func("Main")
+ testingMainParams := testingMain.Signature.Params()
+
+ // The generated code is as if compiled from this:
+ //
+ // func main() {
+ // match := func(_, _ string) (bool, error) { return true, nil }
+ // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...}
+ // benchmarks := []testing.InternalBenchmark{...}
+ // examples := []testing.InternalExample{...}
+ // testing.Main(match, tests, benchmarks, examples)
+ // }
+
+ matcher := &Function{
+ name: "matcher",
+ Signature: testingMainParams.At(0).Type().(*types.Signature),
+ Synthetic: "test matcher predicate",
+ parent: main,
+ Pkg: testmain,
+ Prog: prog,
+ }
+ main.AnonFuncs = append(main.AnonFuncs, matcher)
+ matcher.startBody()
+ matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}})
+ matcher.finishBody()
+
+ // Emit call: testing.Main(matcher, tests, benchmarks, examples).
+ var c Call
+ c.Call.Value = testingMain
+ c.Call.Args = []Value{
+ matcher,
+ testMainSlice(main, tests, testingMainParams.At(1).Type()),
+ testMainSlice(main, benchmarks, testingMainParams.At(2).Type()),
+ testMainSlice(main, examples, testingMainParams.At(3).Type()),
+ }
+ emitTailCall(main, &c)
+ } else {
+ // The program does not import "testing", but FindTests
+ // returned non-nil, which must mean there were Examples
+ // but no Tests or Benchmarks.
+ // We'll simply call them from testmain.main; this will
+ // ensure they don't panic, but will not check any
+ // "Output:" comments.
+ for _, eg := range examples {
+ var c Call
+ c.Call.Value = eg
+ c.setType(types.NewTuple())
+ main.emit(&c)
+ }
+ main.emit(&Return{})
+ main.currentBlock = nil
+ }
+
+ main.finishBody()
+
+ testmain.Members["main"] = main
+
+ if prog.mode&PrintPackages != 0 {
+ printMu.Lock()
+ testmain.WriteTo(os.Stdout)
+ printMu.Unlock()
+ }
+
+ if prog.mode&SanityCheckFunctions != 0 {
+ sanityCheckPackage(testmain)
+ }
+
+ prog.packages[testmain.Pkg] = testmain
+
+ return testmain
+}
+
+// testMainSlice emits to fn code to construct a slice of type slice
+// (one of []testing.Internal{Test,Benchmark,Example}) for all
+// functions in testfuncs. It returns the slice value.
+//
+func testMainSlice(fn *Function, testfuncs []*Function, slice types.Type) Value {
+ if testfuncs == nil {
+ return nilConst(slice)
+ }
+
+ tElem := slice.(*types.Slice).Elem()
+ tPtrString := types.NewPointer(tString)
+ tPtrElem := types.NewPointer(tElem)
+ tPtrFunc := types.NewPointer(funcField(slice))
+
+ // TODO(adonovan): fix: populate the
+ // testing.InternalExample.Output field correctly so that tests
+ // work correctly under the interpreter. This requires that we
+ // do this step using ASTs, not *ssa.Functions---quite a
+ // redesign. See also the fake runExample in go/ssa/interp.
+
+ // Emit: array = new [n]testing.InternalTest
+ tArray := types.NewArray(tElem, int64(len(testfuncs)))
+ array := emitNew(fn, tArray, token.NoPos)
+ array.Comment = "test main"
+ for i, testfunc := range testfuncs {
+ // Emit: pitem = &array[i]
+ ia := &IndexAddr{X: array, Index: intConst(int64(i))}
+ ia.setType(tPtrElem)
+ pitem := fn.emit(ia)
+
+ // Emit: pname = &pitem.Name
+ fa := &FieldAddr{X: pitem, Field: 0} // .Name
+ fa.setType(tPtrString)
+ pname := fn.emit(fa)
+
+ // Emit: *pname = "testfunc"
+ emitStore(fn, pname, stringConst(testfunc.Name()), token.NoPos)
+
+ // Emit: pfunc = &pitem.F
+ fa = &FieldAddr{X: pitem, Field: 1} // .F
+ fa.setType(tPtrFunc)
+ pfunc := fn.emit(fa)
+
+ // Emit: *pfunc = testfunc
+ emitStore(fn, pfunc, testfunc, token.NoPos)
+ }
+
+ // Emit: slice array[:]
+ sl := &Slice{X: array}
+ sl.setType(slice)
+ return fn.emit(sl)
+}
+
+// Given the type of one of the three slice parameters of testing.Main,
+// returns the function type.
+func funcField(slice types.Type) *types.Signature {
+ return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature)
+}
+
+// Plundered from $GOROOT/src/cmd/go/test.go
+
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ return ast.IsExported(name[len(prefix):])
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/util.go b/go/src/golang.org/x/tools/go/ssa/util.go
index 4f9d43d..317a013 100644
--- a/go/src/golang.org/x/tools/go/ssa/util.go
+++ b/go/src/golang.org/x/tools/go/ssa/util.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file defines a number of miscellaneous utility functions.
@@ -10,11 +12,11 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"io"
"os"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
)
//// AST utilities
diff --git a/go/src/golang.org/x/tools/go/ssa/util14.go b/go/src/golang.org/x/tools/go/ssa/util14.go
new file mode 100644
index 0000000..444d69c
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/util14.go
@@ -0,0 +1,121 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file defines a number of miscellaneous utility functions.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "io"
+ "os"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/types"
+)
+
+//// AST utilities
+
+func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
+
+// isBlankIdent returns true iff e is an Ident with name "_".
+// They have no associated types.Object, and thus no type.
+//
+func isBlankIdent(e ast.Expr) bool {
+ id, ok := e.(*ast.Ident)
+ return ok && id.Name == "_"
+}
+
+//// Type utilities. Some of these belong in go/types.
+
+// isPointer returns true for types whose underlying type is a pointer.
+func isPointer(typ types.Type) bool {
+ _, ok := typ.Underlying().(*types.Pointer)
+ return ok
+}
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
+
+// recvType returns the receiver type of method obj.
+func recvType(obj *types.Func) types.Type {
+ return obj.Type().(*types.Signature).Recv().Type()
+}
+
+// DefaultType returns the default "typed" type for an "untyped" type;
+// it returns the incoming type for all other types. The default type
+// for untyped nil is untyped nil.
+//
+// Exported to ssa/interp.
+//
+// TODO(gri): this is a copy of go/types.defaultType; export that function.
+//
+func DefaultType(typ types.Type) types.Type {
+ if t, ok := typ.(*types.Basic); ok {
+ k := t.Kind()
+ switch k {
+ case types.UntypedBool:
+ k = types.Bool
+ case types.UntypedInt:
+ k = types.Int
+ case types.UntypedRune:
+ k = types.Rune
+ case types.UntypedFloat:
+ k = types.Float64
+ case types.UntypedComplex:
+ k = types.Complex128
+ case types.UntypedString:
+ k = types.String
+ }
+ typ = types.Typ[k]
+ }
+ return typ
+}
+
+// logStack prints the formatted "start" message to stderr and
+// returns a closure that prints the corresponding "end" message.
+// Call using 'defer logStack(...)()' to show builder stack on panic.
+// Don't forget trailing parens!
+//
+func logStack(format string, args ...interface{}) func() {
+ msg := fmt.Sprintf(format, args...)
+ io.WriteString(os.Stderr, msg)
+ io.WriteString(os.Stderr, "\n")
+ return func() {
+ io.WriteString(os.Stderr, msg)
+ io.WriteString(os.Stderr, " end\n")
+ }
+}
+
+// newVar creates a 'var' for use in a types.Tuple.
+func newVar(name string, typ types.Type) *types.Var {
+ return types.NewParam(token.NoPos, nil, name, typ)
+}
+
+// anonVar creates an anonymous 'var' for use in a types.Tuple.
+func anonVar(typ types.Type) *types.Var {
+ return newVar("", typ)
+}
+
+var lenResults = types.NewTuple(anonVar(tInt))
+
+// makeLen returns the len builtin specialized to type func(T)int.
+func makeLen(T types.Type) *Builtin {
+ lenParams := types.NewTuple(anonVar(T))
+ return &Builtin{
+ name: "len",
+ sig: types.NewSignature(nil, lenParams, lenResults, false),
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/ssa/wrappers.go b/go/src/golang.org/x/tools/go/ssa/wrappers.go
index ff1eac5..6ca01ab 100644
--- a/go/src/golang.org/x/tools/go/ssa/wrappers.go
+++ b/go/src/golang.org/x/tools/go/ssa/wrappers.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package ssa
// This file defines synthesis of Functions that delegate to declared
@@ -22,7 +24,7 @@
import (
"fmt"
- "golang.org/x/tools/go/types"
+ "go/types"
)
// -- wrappers -----------------------------------------------------------
diff --git a/go/src/golang.org/x/tools/go/ssa/wrappers14.go b/go/src/golang.org/x/tools/go/ssa/wrappers14.go
new file mode 100644
index 0000000..89f71b7
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/ssa/wrappers14.go
@@ -0,0 +1,296 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package ssa
+
+// This file defines synthesis of Functions that delegate to declared
+// methods; they come in three kinds:
+//
+// (1) wrappers: methods that wrap declared methods, performing
+// implicit pointer indirections and embedded field selections.
+//
+// (2) thunks: funcs that wrap declared methods. Like wrappers,
+// thunks perform indirections and field selections. The thunk's
+// first parameter is used as the receiver for the method call.
+//
+// (3) bounds: funcs that wrap declared methods. The bound's sole
+// free variable, supplied by a closure, is used as the receiver
+// for the method call. No indirections or field selections are
+// performed since they can be done before the call.
+
+import (
+ "fmt"
+
+ "golang.org/x/tools/go/types"
+)
+
+// -- wrappers -----------------------------------------------------------
+
+// makeWrapper returns a synthetic method that delegates to the
+// declared method denoted by meth.Obj(), first performing any
+// necessary pointer indirections or field selections implied by meth.
+//
+// The resulting method's receiver type is meth.Recv().
+//
+// This function is versatile but quite subtle! Consider the
+// following axes of variation when making changes:
+// - optional receiver indirection
+// - optional implicit field selections
+// - meth.Obj() may denote a concrete or an interface method
+// - the result may be a thunk or a wrapper.
+//
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+//
+func makeWrapper(prog *Program, sel *types.Selection) *Function {
+ obj := sel.Obj().(*types.Func) // the declared function
+ sig := sel.Type().(*types.Signature) // type of this wrapper
+
+ var recv *types.Var // wrapper's receiver or thunk's params[0]
+ name := obj.Name()
+ var description string
+ var start int // first regular param
+ if sel.Kind() == types.MethodExpr {
+ name += "$thunk"
+ description = "thunk"
+ recv = sig.Params().At(0)
+ start = 1
+ } else {
+ description = "wrapper"
+ recv = sig.Recv()
+ }
+
+ description = fmt.Sprintf("%s for %s", description, sel.Obj())
+ if prog.mode&LogSource != 0 {
+ defer logStack("make %s to (%s)", description, recv.Type())()
+ }
+ fn := &Function{
+ name: name,
+ method: sel,
+ object: obj,
+ Signature: sig,
+ Synthetic: description,
+ Prog: prog,
+ pos: obj.Pos(),
+ }
+ fn.startBody()
+ fn.addSpilledParam(recv)
+ createParams(fn, start)
+
+ indices := sel.Index()
+
+ var v Value = fn.Locals[0] // spilled receiver
+ if isPointer(sel.Recv()) {
+ v = emitLoad(fn, v)
+
+ // For simple indirection wrappers, perform an informative nil-check:
+ // "value method (T).f called using nil *T pointer"
+ if len(indices) == 1 && !isPointer(recvType(obj)) {
+ var c Call
+ c.Call.Value = &Builtin{
+ name: "ssa:wrapnilchk",
+ sig: types.NewSignature(nil,
+ types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
+ types.NewTuple(anonVar(sel.Recv())), false),
+ }
+ c.Call.Args = []Value{
+ v,
+ stringConst(deref(sel.Recv()).String()),
+ stringConst(sel.Obj().Name()),
+ }
+ c.setType(v.Type())
+ v = fn.emit(&c)
+ }
+ }
+
+ // Invariant: v is a pointer, either
+ // value of *A receiver param, or
+ // address of A spilled receiver.
+
+ // We use pointer arithmetic (FieldAddr possibly followed by
+ // Load) in preference to value extraction (Field possibly
+ // preceded by Load).
+
+ v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
+
+ // Invariant: v is a pointer, either
+ // value of implicit *C field, or
+ // address of implicit C field.
+
+ var c Call
+ if r := recvType(obj); !isInterface(r) { // concrete method
+ if !isPointer(r) {
+ v = emitLoad(fn, v)
+ }
+ c.Call.Value = prog.declaredFunc(obj)
+ c.Call.Args = append(c.Call.Args, v)
+ } else {
+ c.Call.Method = obj
+ c.Call.Value = emitLoad(fn, v)
+ }
+ for _, arg := range fn.Params[1:] {
+ c.Call.Args = append(c.Call.Args, arg)
+ }
+ emitTailCall(fn, &c)
+ fn.finishBody()
+ return fn
+}
+
+// createParams creates parameters for wrapper method fn based on its
+// Signature.Params, which do not include the receiver.
+// start is the index of the first regular parameter to use.
+//
+func createParams(fn *Function, start int) {
+ var last *Parameter
+ tparams := fn.Signature.Params()
+ for i, n := start, tparams.Len(); i < n; i++ {
+ last = fn.addParamObj(tparams.At(i))
+ }
+ if fn.Signature.Variadic() {
+ last.typ = types.NewSlice(last.typ)
+ }
+}
+
+// -- bounds -----------------------------------------------------------
+
+// makeBound returns a bound method wrapper (or "bound"), a synthetic
+// function that delegates to a concrete or interface method denoted
+// by obj. The resulting function has no receiver, but has one free
+// variable which will be used as the method's receiver in the
+// tail-call.
+//
+// Use MakeClosure with such a wrapper to construct a bound method
+// closure. e.g.:
+//
+// type T int or: type T interface { meth() }
+// func (t T) meth()
+// var t T
+// f := t.meth
+// f() // calls t.meth()
+//
+// f is a closure of a synthetic wrapper defined as if by:
+//
+// f := func() { return t.meth() }
+//
+// Unlike makeWrapper, makeBound need perform no indirection or field
+// selections because that can be done before the closure is
+// constructed.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
+//
+func makeBound(prog *Program, obj *types.Func) *Function {
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+ fn, ok := prog.bounds[obj]
+ if !ok {
+ description := fmt.Sprintf("bound method wrapper for %s", obj)
+ if prog.mode&LogSource != 0 {
+ defer logStack("%s", description)()
+ }
+ fn = &Function{
+ name: obj.Name() + "$bound",
+ object: obj,
+ Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
+ Synthetic: description,
+ Prog: prog,
+ pos: obj.Pos(),
+ }
+
+ fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
+ fn.FreeVars = []*FreeVar{fv}
+ fn.startBody()
+ createParams(fn, 0)
+ var c Call
+
+ if !isInterface(recvType(obj)) { // concrete
+ c.Call.Value = prog.declaredFunc(obj)
+ c.Call.Args = []Value{fv}
+ } else {
+ c.Call.Value = fv
+ c.Call.Method = obj
+ }
+ for _, arg := range fn.Params {
+ c.Call.Args = append(c.Call.Args, arg)
+ }
+ emitTailCall(fn, &c)
+ fn.finishBody()
+
+ prog.bounds[obj] = fn
+ }
+ return fn
+}
+
+// -- thunks -----------------------------------------------------------
+
+// makeThunk returns a thunk, a synthetic function that delegates to a
+// concrete or interface method denoted by sel.Obj(). The resulting
+// function has no receiver, but has an additional (first) regular
+// parameter.
+//
+// Precondition: sel.Kind() == types.MethodExpr.
+//
+// type T int or: type T interface { meth() }
+// func (t T) meth()
+// f := T.meth
+// var t T
+// f(t) // calls t.meth()
+//
+// f is a synthetic wrapper defined as if by:
+//
+// f := func(t T) { return t.meth() }
+//
+// TODO(adonovan): opt: currently the stub is created even when used
+// directly in a function call: C.f(i, 0). This is less efficient
+// than inlining the stub.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
+//
+func makeThunk(prog *Program, sel *types.Selection) *Function {
+ if sel.Kind() != types.MethodExpr {
+ panic(sel)
+ }
+
+ key := selectionKey{
+ kind: sel.Kind(),
+ recv: sel.Recv(),
+ obj: sel.Obj(),
+ index: fmt.Sprint(sel.Index()),
+ indirect: sel.Indirect(),
+ }
+
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ // Canonicalize key.recv to avoid constructing duplicate thunks.
+ canonRecv, ok := prog.canon.At(key.recv).(types.Type)
+ if !ok {
+ canonRecv = key.recv
+ prog.canon.Set(key.recv, canonRecv)
+ }
+ key.recv = canonRecv
+
+ fn, ok := prog.thunks[key]
+ if !ok {
+ fn = makeWrapper(prog, sel)
+ if fn.Signature.Recv() != nil {
+ panic(fn) // unexpected receiver
+ }
+ prog.thunks[key] = fn
+ }
+ return fn
+}
+
+func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
+ return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
+}
+
+// selectionKey is like types.Selection but a usable map key.
+type selectionKey struct {
+ kind types.SelectionKind
+ recv types.Type // canonicalized via Program.canon
+ obj types.Object
+ index string
+ indirect bool
+}
diff --git a/go/src/golang.org/x/tools/go/types/expr.go b/go/src/golang.org/x/tools/go/types/expr.go
index 6efc7b4..79dad12 100644
--- a/go/src/golang.org/x/tools/go/types/expr.go
+++ b/go/src/golang.org/x/tools/go/types/expr.go
@@ -1246,7 +1246,7 @@
switch typ := x.typ.Underlying().(type) {
case *Basic:
if isString(typ) {
- if slice3(e) {
+ if e.Slice3 {
check.invalidOp(x.pos(), "3-index slice of string")
goto Error
}
@@ -1290,14 +1290,14 @@
x.mode = value
// spec: "Only the first index may be omitted; it defaults to 0."
- if slice3(e) && (e.High == nil || sliceMax(e) == nil) {
+ if e.Slice3 && (e.High == nil || e.Max == nil) {
check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice")
goto Error
}
// check indices
var ind [3]int64
- for i, expr := range []ast.Expr{e.Low, e.High, sliceMax(e)} {
+ for i, expr := range []ast.Expr{e.Low, e.High, e.Max} {
x := int64(-1)
switch {
case expr != nil:
diff --git a/go/src/golang.org/x/tools/go/types/go11.go b/go/src/golang.org/x/tools/go/types/go11.go
deleted file mode 100644
index cf41cab..0000000
--- a/go/src/golang.org/x/tools/go/types/go11.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.2
-
-package types
-
-import "go/ast"
-
-func slice3(x *ast.SliceExpr) bool {
- return false
-}
-
-func sliceMax(x *ast.SliceExpr) ast.Expr {
- return nil
-}
diff --git a/go/src/golang.org/x/tools/go/types/go12.go b/go/src/golang.org/x/tools/go/types/go12.go
deleted file mode 100644
index 2017442..0000000
--- a/go/src/golang.org/x/tools/go/types/go12.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.2
-
-package types
-
-import "go/ast"
-
-func slice3(x *ast.SliceExpr) bool {
- return x.Slice3
-}
-
-func sliceMax(x *ast.SliceExpr) ast.Expr {
- return x.Max
-}
diff --git a/go/src/golang.org/x/tools/go/types/stdlib_test.go b/go/src/golang.org/x/tools/go/types/stdlib_test.go
index d6aa82a..7554f91 100644
--- a/go/src/golang.org/x/tools/go/types/stdlib_test.go
+++ b/go/src/golang.org/x/tools/go/types/stdlib_test.go
@@ -143,6 +143,8 @@
"issue7746.go", // large constants - consumes too much memory
"issue11326.go", // large constants
"issue11326b.go", // large constants
+ "issue11362.go", // canonical import path check is implementation-defined behavior
+ "issue13471.go", // large constants
)
}
@@ -187,6 +189,13 @@
files = append(files, file)
}
+ // gcimporter doesn't support vendored imports.
+ // TODO(gri): fix.
+ if strings.HasSuffix(path, "src/cmd/internal/objfile") ||
+ strings.HasSuffix(path, "src/net/http") {
+ return
+ }
+
// typecheck package files
var conf Config
conf.Error = func(err error) { t.Error(err) }
diff --git a/go/src/golang.org/x/tools/go/types/type.go b/go/src/golang.org/x/tools/go/types/type.go
index 1df8b45..8e24b54 100644
--- a/go/src/golang.org/x/tools/go/types/type.go
+++ b/go/src/golang.org/x/tools/go/types/type.go
@@ -398,7 +398,7 @@
return typ
}
-// TypeName returns the type name for the named type t.
+// Obj returns the type name for the named type t.
func (t *Named) Obj() *TypeName { return t.obj }
// NumMethods returns the number of explicit methods whose receiver is named type t.
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/example_test.go b/go/src/golang.org/x/tools/go/types/typeutil/example_test.go
index 9e3ada7..fe49644 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/example_test.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/example_test.go
@@ -2,17 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil_test
import (
"fmt"
- "sort"
-
"go/ast"
"go/parser"
"go/token"
+ "go/types"
+ "sort"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/imports.go b/go/src/golang.org/x/tools/go/types/typeutil/imports.go
index 967fe1e..4b753f4 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/imports.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/imports.go
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil
-import "golang.org/x/tools/go/types"
+import "go/types"
// Dependencies returns all dependencies of the specified packages.
//
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/imports14.go b/go/src/golang.org/x/tools/go/types/typeutil/imports14.go
new file mode 100644
index 0000000..9741df3
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/types/typeutil/imports14.go
@@ -0,0 +1,33 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package typeutil
+
+import "golang.org/x/tools/go/types"
+
+// Dependencies returns all dependencies of the specified packages.
+//
+// Dependent packages appear in topological order: if package P imports
+// package Q, Q appears earlier than P in the result.
+// The algorithm follows import statements in the order they
+// appear in the source code, so the result is a total order.
+//
+func Dependencies(pkgs ...*types.Package) []*types.Package {
+ var result []*types.Package
+ seen := make(map[*types.Package]bool)
+ var visit func(pkgs []*types.Package)
+ visit = func(pkgs []*types.Package) {
+ for _, p := range pkgs {
+ if !seen[p] {
+ seen[p] = true
+ visit(p.Imports())
+ result = append(result, p)
+ }
+ }
+ }
+ visit(pkgs)
+ return result
+}
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/imports14_test.go b/go/src/golang.org/x/tools/go/types/typeutil/imports14_test.go
new file mode 100644
index 0000000..b70f5f0
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/types/typeutil/imports14_test.go
@@ -0,0 +1,81 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package typeutil_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "testing"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+func TestDependencies(t *testing.T) {
+ packages := make(map[string]*types.Package)
+ conf := types.Config{
+ Packages: packages,
+ Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
+ return packages[path], nil
+ },
+ }
+ fset := token.NewFileSet()
+
+ // All edges go to the right.
+ // /--D--B--A
+ // F \_C_/
+ // \__E_/
+ for i, content := range []string{
+ `package a`,
+ `package c; import (_ "a")`,
+ `package b; import (_ "a")`,
+ `package e; import (_ "c")`,
+ `package d; import (_ "b"; _ "c")`,
+ `package f; import (_ "d"; _ "e")`,
+ } {
+ f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ packages[pkg.Path()] = pkg
+ }
+
+ for _, test := range []struct {
+ roots, want string
+ }{
+ {"a", "a"},
+ {"b", "ab"},
+ {"c", "ac"},
+ {"d", "abcd"},
+ {"e", "ace"},
+ {"f", "abcdef"},
+
+ {"be", "abce"},
+ {"eb", "aceb"},
+ {"de", "abcde"},
+ {"ed", "acebd"},
+ {"ef", "acebdf"},
+ } {
+ var pkgs []*types.Package
+ for _, r := range test.roots {
+ pkgs = append(pkgs, packages[string(r)])
+ }
+ var got string
+ for _, p := range typeutil.Dependencies(pkgs...) {
+ got += p.Path()
+ }
+ if got != test.want {
+ t.Errorf("Dependencies(%q) = %q, want %q", test.roots, got, test.want)
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/imports_test.go b/go/src/golang.org/x/tools/go/types/typeutil/imports_test.go
index 8071ae1..b846fbb 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/imports_test.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/imports_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil_test
import (
@@ -9,19 +11,20 @@
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"testing"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
+type closure map[string]*types.Package
+
+func (c closure) Import(path string) (*types.Package, error) { return c[path], nil }
+
func TestDependencies(t *testing.T) {
packages := make(map[string]*types.Package)
conf := types.Config{
- Packages: packages,
- Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
- return packages[path], nil
- },
+ Importer: closure(packages),
}
fset := token.NewFileSet()
@@ -30,12 +33,12 @@
// F \_C_/
// \__E_/
for i, content := range []string{
- `package A`,
- `package C; import (_ "A")`,
- `package B; import (_ "A")`,
- `package E; import (_ "C")`,
- `package D; import (_ "B"; _ "C")`,
- `package F; import (_ "D"; _ "E")`,
+ `package a`,
+ `package c; import (_ "a")`,
+ `package b; import (_ "a")`,
+ `package e; import (_ "c")`,
+ `package d; import (_ "b"; _ "c")`,
+ `package f; import (_ "d"; _ "e")`,
} {
f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
if err != nil {
@@ -51,22 +54,22 @@
for _, test := range []struct {
roots, want string
}{
- {"A", "A"},
- {"B", "AB"},
- {"C", "AC"},
- {"D", "ABCD"},
- {"E", "ACE"},
- {"F", "ABCDEF"},
+ {"a", "a"},
+ {"b", "ab"},
+ {"c", "ac"},
+ {"d", "abcd"},
+ {"e", "ace"},
+ {"f", "abcdef"},
- {"BE", "ABCE"},
- {"EB", "ACEB"},
- {"DE", "ABCDE"},
- {"ED", "ACEBD"},
- {"EF", "ACEBDF"},
+ {"be", "abce"},
+ {"eb", "aceb"},
+ {"de", "abcde"},
+ {"ed", "acebd"},
+ {"ef", "acebdf"},
} {
var pkgs []*types.Package
for _, r := range test.roots {
- pkgs = append(pkgs, conf.Packages[string(r)])
+ pkgs = append(pkgs, packages[string(r)])
}
var got string
for _, p := range typeutil.Dependencies(pkgs...) {
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/map.go b/go/src/golang.org/x/tools/go/types/typeutil/map.go
index b3a04cc..81dd556 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/map.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/map.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package typeutil defines various utilities for types, such as Map,
// a mapping from types.Type to interface{} values.
package typeutil // import "golang.org/x/tools/go/types/typeutil"
@@ -9,9 +11,8 @@
import (
"bytes"
"fmt"
+ "go/types"
"reflect"
-
- "golang.org/x/tools/go/types"
)
// Map is a hash-table-based mapping from types (types.Type) to
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/map14.go b/go/src/golang.org/x/tools/go/types/typeutil/map14.go
new file mode 100644
index 0000000..16209e3
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/types/typeutil/map14.go
@@ -0,0 +1,316 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package typeutil defines various utilities for types, such as Map,
+// a mapping from types.Type to interface{} values.
+package typeutil // import "golang.org/x/tools/go/types/typeutil"
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+
+ "golang.org/x/tools/go/types"
+)
+
+// Map is a hash-table-based mapping from types (types.Type) to
+// arbitrary interface{} values. The concrete types that implement
+// the Type interface are pointers. Since they are not canonicalized,
+// == cannot be used to check for equivalence, and thus we cannot
+// simply use a Go map.
+//
+// Just as with map[K]V, a nil *Map is a valid empty map.
+//
+// Not thread-safe.
+//
+type Map struct {
+ hasher Hasher // shared by many Maps
+ table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
+ length int // number of map entries
+}
+
+// entry is an entry (key/value association) in a hash bucket.
+type entry struct {
+ key types.Type
+ value interface{}
+}
+
+// SetHasher sets the hasher used by Map.
+//
+// All Hashers are functionally equivalent but contain internal state
+// used to cache the results of hashing previously seen types.
+//
+// A single Hasher created by MakeHasher() may be shared among many
+// Maps. This is recommended if the instances have many keys in
+// common, as it will amortize the cost of hash computation.
+//
+// A Hasher may grow without bound as new types are seen. Even when a
+// type is deleted from the map, the Hasher never shrinks, since other
+// types in the map may reference the deleted type indirectly.
+//
+// Hashers are not thread-safe, and read-only operations such as
+// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
+// read-lock) is require around all Map operations if a shared
+// hasher is accessed from multiple threads.
+//
+// If SetHasher is not called, the Map will create a private hasher at
+// the first call to Insert.
+//
+func (m *Map) SetHasher(hasher Hasher) {
+ m.hasher = hasher
+}
+
+// Delete removes the entry with the given key, if any.
+// It returns true if the entry was found.
+//
+func (m *Map) Delete(key types.Type) bool {
+ if m != nil && m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ for i, e := range bucket {
+ if e.key != nil && types.Identical(key, e.key) {
+ // We can't compact the bucket as it
+ // would disturb iterators.
+ bucket[i] = entry{}
+ m.length--
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// At returns the map entry for the given key.
+// The result is nil if the entry is not present.
+//
+func (m *Map) At(key types.Type) interface{} {
+ if m != nil && m.table != nil {
+ for _, e := range m.table[m.hasher.Hash(key)] {
+ if e.key != nil && types.Identical(key, e.key) {
+ return e.value
+ }
+ }
+ }
+ return nil
+}
+
+// Set sets the map entry for key to val,
+// and returns the previous entry, if any.
+func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
+ if m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ var hole *entry
+ for i, e := range bucket {
+ if e.key == nil {
+ hole = &bucket[i]
+ } else if types.Identical(key, e.key) {
+ prev = e.value
+ bucket[i].value = value
+ return
+ }
+ }
+
+ if hole != nil {
+ *hole = entry{key, value} // overwrite deleted entry
+ } else {
+ m.table[hash] = append(bucket, entry{key, value})
+ }
+ } else {
+ if m.hasher.memo == nil {
+ m.hasher = MakeHasher()
+ }
+ hash := m.hasher.Hash(key)
+ m.table = map[uint32][]entry{hash: {entry{key, value}}}
+ }
+
+ m.length++
+ return
+}
+
+// Len returns the number of map entries.
+func (m *Map) Len() int {
+ if m != nil {
+ return m.length
+ }
+ return 0
+}
+
+// Iterate calls function f on each entry in the map in unspecified order.
+//
+// If f should mutate the map, Iterate provides the same guarantees as
+// Go maps: if f deletes a map entry that Iterate has not yet reached,
+// f will not be invoked for it, but if f inserts a map entry that
+// Iterate has not yet reached, whether or not f will be invoked for
+// it is unspecified.
+//
+func (m *Map) Iterate(f func(key types.Type, value interface{})) {
+ if m != nil {
+ for _, bucket := range m.table {
+ for _, e := range bucket {
+ if e.key != nil {
+ f(e.key, e.value)
+ }
+ }
+ }
+ }
+}
+
+// Keys returns a new slice containing the set of map keys.
+// The order is unspecified.
+func (m *Map) Keys() []types.Type {
+ keys := make([]types.Type, 0, m.Len())
+ m.Iterate(func(key types.Type, _ interface{}) {
+ keys = append(keys, key)
+ })
+ return keys
+}
+
+func (m *Map) toString(values bool) string {
+ if m == nil {
+ return "{}"
+ }
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "{")
+ sep := ""
+ m.Iterate(func(key types.Type, value interface{}) {
+ fmt.Fprint(&buf, sep)
+ sep = ", "
+ fmt.Fprint(&buf, key)
+ if values {
+ fmt.Fprintf(&buf, ": %q", value)
+ }
+ })
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+// String returns a string representation of the map's entries.
+// Values are printed using fmt.Sprintf("%v", v).
+// Order is unspecified.
+//
+func (m *Map) String() string {
+ return m.toString(true)
+}
+
+// KeysString returns a string representation of the map's key set.
+// Order is unspecified.
+//
+func (m *Map) KeysString() string {
+ return m.toString(false)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// A Hasher maps each type to its hash value.
+// For efficiency, a hasher uses memoization; thus its memory
+// footprint grows monotonically over time.
+// Hashers are not thread-safe.
+// Hashers have reference semantics.
+// Call MakeHasher to create a Hasher.
+type Hasher struct {
+ memo map[types.Type]uint32
+}
+
+// MakeHasher returns a new Hasher instance.
+func MakeHasher() Hasher {
+ return Hasher{make(map[types.Type]uint32)}
+}
+
+// Hash computes a hash value for the given type t such that
+// Identical(t, t') => Hash(t) == Hash(t').
+func (h Hasher) Hash(t types.Type) uint32 {
+ hash, ok := h.memo[t]
+ if !ok {
+ hash = h.hashFor(t)
+ h.memo[t] = hash
+ }
+ return hash
+}
+
+// hashString computes the Fowler–Noll–Vo hash of s.
+func hashString(s string) uint32 {
+ var h uint32
+ for i := 0; i < len(s); i++ {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return h
+}
+
+// hashFor computes the hash of t.
+func (h Hasher) hashFor(t types.Type) uint32 {
+ // See Identical for rationale.
+ switch t := t.(type) {
+ case *types.Basic:
+ return uint32(t.Kind())
+
+ case *types.Array:
+ return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
+
+ case *types.Slice:
+ return 9049 + 2*h.Hash(t.Elem())
+
+ case *types.Struct:
+ var hash uint32 = 9059
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ f := t.Field(i)
+ if f.Anonymous() {
+ hash += 8861
+ }
+ hash += hashString(t.Tag(i))
+ hash += hashString(f.Name()) // (ignore f.Pkg)
+ hash += h.Hash(f.Type())
+ }
+ return hash
+
+ case *types.Pointer:
+ return 9067 + 2*h.Hash(t.Elem())
+
+ case *types.Signature:
+ var hash uint32 = 9091
+ if t.Variadic() {
+ hash *= 8863
+ }
+ return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
+
+ case *types.Interface:
+ var hash uint32 = 9103
+ for i, n := 0, t.NumMethods(); i < n; i++ {
+ // See go/types.identicalMethods for rationale.
+ // Method order is not significant.
+ // Ignore m.Pkg().
+ m := t.Method(i)
+ hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
+ }
+ return hash
+
+ case *types.Map:
+ return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
+
+ case *types.Chan:
+ return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
+
+ case *types.Named:
+ // Not safe with a copying GC; objects may move.
+ return uint32(reflect.ValueOf(t.Obj()).Pointer())
+
+ case *types.Tuple:
+ return h.hashTuple(t)
+ }
+ panic(t)
+}
+
+func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
+ // See go/types.identicalTypes for rationale.
+ n := tuple.Len()
+ var hash uint32 = 9137 + 2*uint32(n)
+ for i := 0; i < n; i++ {
+ hash += 3 * h.Hash(tuple.At(i).Type())
+ }
+ return hash
+}
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/map14_test.go b/go/src/golang.org/x/tools/go/types/typeutil/map14_test.go
new file mode 100644
index 0000000..9043d05
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/types/typeutil/map14_test.go
@@ -0,0 +1,176 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package typeutil_test
+
+// TODO(adonovan):
+// - test use of explicit hasher across two maps.
+// - test hashcodes are consistent with equals for a range of types
+// (e.g. all types generated by type-checking some body of real code).
+
+import (
+ "testing"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+var (
+ tStr = types.Typ[types.String] // string
+ tPStr1 = types.NewPointer(tStr) // *string
+ tPStr2 = types.NewPointer(tStr) // *string, again
+ tInt = types.Typ[types.Int] // int
+ tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int
+ tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again
+)
+
+func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) {
+ if !types.Identical(x, y) {
+ t.Errorf("%s: not equal: %s, %s", comment, x, y)
+ }
+ if x == y {
+ t.Errorf("%s: identical: %v, %v", comment, x, y)
+ }
+}
+
+func TestAxioms(t *testing.T) {
+ checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}")
+ checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}")
+}
+
+func TestMap(t *testing.T) {
+ var tmap *typeutil.Map
+
+ // All methods but Set are safe on on (*T)(nil).
+ tmap.Len()
+ tmap.At(tPStr1)
+ tmap.Delete(tPStr1)
+ tmap.KeysString()
+ tmap.String()
+
+ tmap = new(typeutil.Map)
+
+ // Length of empty map.
+ if l := tmap.Len(); l != 0 {
+ t.Errorf("Len() on empty Map: got %d, want 0", l)
+ }
+ // At of missing key.
+ if v := tmap.At(tPStr1); v != nil {
+ t.Errorf("At() on empty Map: got %v, want nil", v)
+ }
+ // Deletion of missing key.
+ if tmap.Delete(tPStr1) {
+ t.Errorf("Delete() on empty Map: got true, want false")
+ }
+ // Set of new key.
+ if prev := tmap.Set(tPStr1, "*string"); prev != nil {
+ t.Errorf("Set() on empty Map returned non-nil previous value %s", prev)
+ }
+
+ // Now: {*string: "*string"}
+
+ // Length of non-empty map.
+ if l := tmap.Len(); l != 1 {
+ t.Errorf("Len(): got %d, want 1", l)
+ }
+ // At via insertion key.
+ if v := tmap.At(tPStr1); v != "*string" {
+ t.Errorf("At(): got %q, want \"*string\"", v)
+ }
+ // At via equal key.
+ if v := tmap.At(tPStr2); v != "*string" {
+ t.Errorf("At(): got %q, want \"*string\"", v)
+ }
+ // Iteration over sole entry.
+ tmap.Iterate(func(key types.Type, value interface{}) {
+ if key != tPStr1 {
+ t.Errorf("Iterate: key: got %s, want %s", key, tPStr1)
+ }
+ if want := "*string"; value != want {
+ t.Errorf("Iterate: value: got %s, want %s", value, want)
+ }
+ })
+
+ // Setion with key equal to present one.
+ if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" {
+ t.Errorf("Set() previous value: got %s, want \"*string\"", prev)
+ }
+
+ // Setion of another association.
+ if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil {
+ t.Errorf("Set() previous value: got %s, want nil", prev)
+ }
+
+ // Now: {*string: "*string again", <-chan int: "<-chan int"}
+
+ want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}"
+ want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}"
+ if s := tmap.String(); s != want1 && s != want2 {
+ t.Errorf("String(): got %s, want %s", s, want1)
+ }
+
+ want1 = "{*string, <-chan int}"
+ want2 = "{<-chan int, *string}"
+ if s := tmap.KeysString(); s != want1 && s != want2 {
+ t.Errorf("KeysString(): got %s, want %s", s, want1)
+ }
+
+ // Keys().
+ I := types.Identical
+ switch k := tmap.Keys(); {
+ case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok
+ case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok
+ default:
+ t.Errorf("Keys(): got %v, want %s", k, want2)
+ }
+
+ if l := tmap.Len(); l != 2 {
+ t.Errorf("Len(): got %d, want 1", l)
+ }
+ // At via original key.
+ if v := tmap.At(tPStr1); v != "*string again" {
+ t.Errorf("At(): got %q, want \"*string again\"", v)
+ }
+ hamming := 1
+ tmap.Iterate(func(key types.Type, value interface{}) {
+ switch {
+ case I(key, tChanInt1):
+ hamming *= 2 // ok
+ case I(key, tPStr1):
+ hamming *= 3 // ok
+ }
+ })
+ if hamming != 6 {
+ t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6)
+ }
+
+ if v := tmap.At(tChanInt2); v != "<-chan int" {
+ t.Errorf("At(): got %q, want \"<-chan int\"", v)
+ }
+ // Deletion with key equal to present one.
+ if !tmap.Delete(tChanInt2) {
+ t.Errorf("Delete() of existing key: got false, want true")
+ }
+
+ // Now: {*string: "*string again"}
+
+ if l := tmap.Len(); l != 1 {
+ t.Errorf("Len(): got %d, want 1", l)
+ }
+ // Deletion again.
+ if !tmap.Delete(tPStr2) {
+ t.Errorf("Delete() of existing key: got false, want true")
+ }
+
+ // Now: {}
+
+ if l := tmap.Len(); l != 0 {
+ t.Errorf("Len(): got %d, want %d", l, 0)
+ }
+ if s := tmap.String(); s != "{}" {
+ t.Errorf("Len(): got %q, want %q", s, "")
+ }
+}
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/map_test.go b/go/src/golang.org/x/tools/go/types/typeutil/map_test.go
index 776b5e2..e5dc4e4 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/map_test.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/map_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil_test
// TODO(adonovan):
@@ -10,9 +12,9 @@
// (e.g. all types generated by type-checking some body of real code).
import (
+ "go/types"
"testing"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache.go b/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache.go
index daad644..edc3f5b 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache.go
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This file implements a cache of method sets.
package typeutil
import (
+ "go/types"
"sync"
-
- "golang.org/x/tools/go/types"
)
// A MethodSetCache records the method set of each type T for which
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache14.go b/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache14.go
new file mode 100644
index 0000000..83b5e76
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/types/typeutil/methodsetcache14.go
@@ -0,0 +1,75 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// This file implements a cache of method sets.
+
+package typeutil
+
+import (
+ "sync"
+
+ "golang.org/x/tools/go/types"
+)
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+ mu sync.Mutex
+ named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
+ others map[types.Type]*types.MethodSet // all other types
+}
+
+// MethodSet returns the method set of type T. It is thread-safe.
+//
+// If cache is nil, this function is equivalent to types.NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
+ if cache == nil {
+ return types.NewMethodSet(T)
+ }
+ cache.mu.Lock()
+ defer cache.mu.Unlock()
+
+ switch T := T.(type) {
+ case *types.Named:
+ return cache.lookupNamed(T).value
+
+ case *types.Pointer:
+ if N, ok := T.Elem().(*types.Named); ok {
+ return cache.lookupNamed(N).pointer
+ }
+ }
+
+ // all other types
+ // (The map uses pointer equivalence, not type identity.)
+ mset := cache.others[T]
+ if mset == nil {
+ mset = types.NewMethodSet(T)
+ if cache.others == nil {
+ cache.others = make(map[types.Type]*types.MethodSet)
+ }
+ cache.others[T] = mset
+ }
+ return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
+ if cache.named == nil {
+ cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
+ }
+ // Avoid recomputing mset(*T) for each distinct Pointer
+ // instance whose underlying type is a named type.
+ msets, ok := cache.named[named]
+ if !ok {
+ msets.value = types.NewMethodSet(named)
+ msets.pointer = types.NewMethodSet(types.NewPointer(named))
+ cache.named[named] = msets
+ }
+ return msets
+}
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/ui.go b/go/src/golang.org/x/tools/go/types/typeutil/ui.go
index 20c5249..945fb29 100644
--- a/go/src/golang.org/x/tools/go/types/typeutil/ui.go
+++ b/go/src/golang.org/x/tools/go/types/typeutil/ui.go
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil
// This file defines utilities for user interfaces that display types.
-import "golang.org/x/tools/go/types"
+import "go/types"
// IntuitiveMethodSet returns the intuitive method set of a type, T.
//
diff --git a/go/src/golang.org/x/tools/go/types/typeutil/ui14.go b/go/src/golang.org/x/tools/go/types/typeutil/ui14.go
new file mode 100644
index 0000000..bb78e0b
--- /dev/null
+++ b/go/src/golang.org/x/tools/go/types/typeutil/ui14.go
@@ -0,0 +1,40 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package typeutil
+
+// This file defines utilities for user interfaces that display types.
+
+import "golang.org/x/tools/go/types"
+
+// IntuitiveMethodSet returns the intuitive method set of a type, T.
+//
+// The result contains MethodSet(T) and additionally, if T is a
+// concrete type, methods belonging to *T if there is no identically
+// named method on T itself. This corresponds to user intuition about
+// method sets; this function is intended only for user interfaces.
+//
+// The order of the result is as for types.MethodSet(T).
+//
+func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
+ var result []*types.Selection
+ mset := msets.MethodSet(T)
+ if _, ok := T.Underlying().(*types.Interface); ok {
+ for i, n := 0, mset.Len(); i < n; i++ {
+ result = append(result, mset.At(i))
+ }
+ } else {
+ pmset := msets.MethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ meth := pmset.At(i)
+ if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
+ meth = m
+ }
+ result = append(result, meth)
+ }
+ }
+ return result
+}
diff --git a/go/src/golang.org/x/tools/go/vcs/discovery.go b/go/src/golang.org/x/tools/go/vcs/discovery.go
index c4b0e3d..f431dc1 100644
--- a/go/src/golang.org/x/tools/go/vcs/discovery.go
+++ b/go/src/golang.org/x/tools/go/vcs/discovery.go
@@ -36,7 +36,7 @@
for {
t, err = d.Token()
if err != nil {
- if err == io.EOF {
+ if err == io.EOF || len(imports) > 0 {
err = nil
}
return
diff --git a/go/src/golang.org/x/tools/go/vcs/vcs.go b/go/src/golang.org/x/tools/go/vcs/vcs.go
index 2d9b7de..f0aed57 100644
--- a/go/src/golang.org/x/tools/go/vcs/vcs.go
+++ b/go/src/golang.org/x/tools/go/vcs/vcs.go
@@ -335,8 +335,7 @@
// FromDir inspects dir and its parents to determine the
// version control system and code repository to use.
// On return, root is the import path
-// corresponding to the root of the repository
-// (thus root is a prefix of importPath).
+// corresponding to the root of the repository.
func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) {
// Clean and double-check that dir is in (a subdirectory of) srcRoot.
dir = filepath.Clean(dir)
@@ -348,7 +347,7 @@
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
if fi, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil && fi.IsDir() {
- return vcs, dir[len(srcRoot)+1:], nil
+ return vcs, filepath.ToSlash(dir[len(srcRoot)+1:]), nil
}
}
@@ -369,11 +368,11 @@
type RepoRoot struct {
VCS *Cmd
- // repo is the repository URL, including scheme
+ // Repo is the repository URL, including scheme.
Repo string
- // root is the import path corresponding to the root of the
- // repository
+ // Root is the import path corresponding to the root of the
+ // repository.
Root string
}
@@ -478,11 +477,11 @@
// RepoRootForImportDynamic finds a *RepoRoot for a custom domain that's not
// statically known by RepoRootForImportPathStatic.
//
-// This handles "vanity import paths" like "name.tld/pkg/foo".
+// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld".
func RepoRootForImportDynamic(importPath string, verbose bool) (*RepoRoot, error) {
slash := strings.Index(importPath, "/")
if slash < 0 {
- return nil, errors.New("import path doesn't contain a slash")
+ slash = len(importPath)
}
host := importPath[:slash]
if !strings.Contains(host, ".") {
@@ -599,20 +598,6 @@
check: noVCSSuffix,
},
- // Google Code - new syntax
- {
- prefix: "code.google.com/",
- re: `^(?P<root>code\.google\.com/[pr]/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
- repo: "https://{root}",
- check: googleCodeVCS,
- },
-
- // Google Code - old syntax
- {
- re: `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
- check: oldGoogleCode,
- },
-
// Github
{
prefix: "github.com/",
@@ -668,45 +653,6 @@
return nil
}
-var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
-
-// googleCodeVCS determines the version control system for
-// a code.google.com repository, by scraping the project's
-// /source/checkout page.
-func googleCodeVCS(match map[string]string) error {
- if err := noVCSSuffix(match); err != nil {
- return err
- }
- data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
- if err != nil {
- return err
- }
-
- if m := googleCheckout.FindSubmatch(data); m != nil {
- if vcs := ByCmd(string(m[1])); vcs != nil {
- // Subversion requires the old URLs.
- // TODO: Test.
- if vcs == vcsSvn {
- if match["subrepo"] != "" {
- return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
- }
- match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
- }
- match["vcs"] = vcs.Cmd
- return nil
- }
- }
-
- return fmt.Errorf("unable to detect version control system for code.google.com/ path")
-}
-
-// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
-// It prints an error giving the equivalent new path.
-func oldGoogleCode(match map[string]string) error {
- return fmt.Errorf("invalid Google Code import path: use %s instead",
- expand(match, "code.google.com/p/{project}{path}"))
-}
-
// bitbucketVCS determines the version control system for a
// Bitbucket repository, by using the Bitbucket API.
func bitbucketVCS(match map[string]string) error {
diff --git a/go/src/golang.org/x/tools/go/vcs/vcs_test.go b/go/src/golang.org/x/tools/go/vcs/vcs_test.go
index 5d2d498..2d4bcda 100644
--- a/go/src/golang.org/x/tools/go/vcs/vcs_test.go
+++ b/go/src/golang.org/x/tools/go/vcs/vcs_test.go
@@ -7,6 +7,7 @@
import (
"io/ioutil"
"os"
+ pathpkg "path"
"path/filepath"
"reflect"
"runtime"
@@ -47,11 +48,11 @@
}
}
-// Test that FromDir correctly inspects a given directory and returns the right VCS.
+// Test that FromDir correctly inspects a given directory and returns the right VCS and root.
func TestFromDir(t *testing.T) {
type testStruct struct {
path string
- want *Cmd
+ want *RepoRoot
}
tests := make([]testStruct, len(vcsList))
@@ -63,16 +64,29 @@
for i, vcs := range vcsList {
tests[i] = testStruct{
- filepath.Join(tempDir, vcs.Name, "."+vcs.Cmd),
- vcs,
+ path: filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd),
+ want: &RepoRoot{
+ VCS: vcs,
+ Root: pathpkg.Join("example.com", vcs.Name),
+ },
}
}
for _, test := range tests {
os.MkdirAll(test.path, 0755)
- got, _, _ := FromDir(test.path, tempDir)
- if got.Name != test.want.Name {
- t.Errorf("FromDir(%q, %q) = %s, want %s", test.path, tempDir, got, test.want)
+ var (
+ got = new(RepoRoot)
+ err error
+ )
+ got.VCS, got.Root, err = FromDir(test.path, tempDir)
+ if err != nil {
+ t.Errorf("FromDir(%q, %q): %v", test.path, tempDir, err)
+ os.RemoveAll(test.path)
+ continue
+ }
+ want := test.want
+ if got.VCS.Name != want.VCS.Name || got.Root != want.Root {
+ t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", test.path, tempDir, got.VCS, got.Root, want.VCS, want.Root)
}
os.RemoveAll(test.path)
}
diff --git a/go/src/golang.org/x/tools/godoc/analysis/analysis.go b/go/src/golang.org/x/tools/godoc/analysis/analysis.go
index c11ecbd..5c43bbe 100644
--- a/go/src/golang.org/x/tools/godoc/analysis/analysis.go
+++ b/go/src/golang.org/x/tools/godoc/analysis/analysis.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package analysis performs type and pointer analysis
// and generates mark-up for the Go source view.
//
@@ -45,8 +47,10 @@
import (
"fmt"
"go/build"
+ exact "go/constant"
"go/scanner"
"go/token"
+ "go/types"
"html"
"io"
"log"
@@ -57,12 +61,10 @@
"strings"
"sync"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
// -- links ------------------------------------------------------------
@@ -405,7 +407,7 @@
}
}
for _, pkg := range allPackages {
- if pkg.Object.Name() == "main" && pkg.Func("main") != nil {
+ if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
mainPkgs = append(mainPkgs, pkg)
}
}
@@ -413,7 +415,7 @@
// Build SSA code for bodies of all functions in the whole program.
result.setStatusf("Constructing SSA form...")
- prog.BuildAll()
+ prog.Build()
log.Print("SSA construction complete")
a := analysis{
diff --git a/go/src/golang.org/x/tools/godoc/analysis/analysis14.go b/go/src/golang.org/x/tools/godoc/analysis/analysis14.go
new file mode 100644
index 0000000..ca35b41
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/analysis/analysis14.go
@@ -0,0 +1,622 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package analysis performs type and pointer analysis
+// and generates mark-up for the Go source view.
+//
+// The Run method populates a Result object by running type and
+// (optionally) pointer analysis. The Result object is thread-safe
+// and at all times may be accessed by a serving thread, even as it is
+// progressively populated as analysis facts are derived.
+//
+// The Result is a mapping from each godoc file URL
+// (e.g. /src/fmt/print.go) to information about that file. The
+// information is a list of HTML markup links and a JSON array of
+// structured data values. Some of the links call client-side
+// JavaScript functions that index this array.
+//
+// The analysis computes mark-up for the following relations:
+//
+// IMPORTS: for each ast.ImportSpec, the package that it denotes.
+//
+// RESOLUTION: for each ast.Ident, its kind and type, and the location
+// of its definition.
+//
+// METHOD SETS, IMPLEMENTS: for each ast.Ident defining a named type,
+// its method-set, the set of interfaces it implements or is
+// implemented by, and its size/align values.
+//
+// CALLERS, CALLEES: for each function declaration ('func' token), its
+// callers, and for each call-site ('(' token), its callees.
+//
+// CALLGRAPH: the package docs include an interactive viewer for the
+// intra-package call graph of "fmt".
+//
+// CHANNEL PEERS: for each channel operation make/<-/close, the set of
+// other channel ops that alias the same channel(s).
+//
+// ERRORS: for each locus of a frontend (scanner/parser/type) error, the
+// location is highlighted in red and hover text provides the compiler
+// error message.
+//
+package analysis // import "golang.org/x/tools/godoc/analysis"
+
+import (
+ "fmt"
+ "go/build"
+ "go/scanner"
+ "go/token"
+ "html"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+// -- links ------------------------------------------------------------
+
+// A Link is an HTML decoration of the bytes [Start, End) of a file.
+// Write is called before/after those bytes to emit the mark-up.
+type Link interface {
+ Start() int
+ End() int
+ Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature
+}
+
+// An <a> element.
+type aLink struct {
+ start, end int // =godoc.Segment
+ title string // hover text
+ onclick string // JS code (NB: trusted)
+ href string // URL (NB: trusted)
+}
+
+func (a aLink) Start() int { return a.start }
+func (a aLink) End() int { return a.end }
+func (a aLink) Write(w io.Writer, _ int, start bool) {
+ if start {
+ fmt.Fprintf(w, `<a title='%s'`, html.EscapeString(a.title))
+ if a.onclick != "" {
+ fmt.Fprintf(w, ` onclick='%s'`, html.EscapeString(a.onclick))
+ }
+ if a.href != "" {
+ // TODO(adonovan): I think that in principle, a.href must first be
+ // url.QueryEscape'd, but if I do that, a leading slash becomes "%2F",
+ // which causes the browser to treat the path as relative, not absolute.
+ // WTF?
+ fmt.Fprintf(w, ` href='%s'`, html.EscapeString(a.href))
+ }
+ fmt.Fprintf(w, ">")
+ } else {
+ fmt.Fprintf(w, "</a>")
+ }
+}
+
+// An <a class='error'> element.
+type errorLink struct {
+ start int
+ msg string
+}
+
+func (e errorLink) Start() int { return e.start }
+func (e errorLink) End() int { return e.start + 1 }
+
+func (e errorLink) Write(w io.Writer, _ int, start bool) {
+ // <span> causes havoc, not sure why, so use <a>.
+ if start {
+ fmt.Fprintf(w, `<a class='error' title='%s'>`, html.EscapeString(e.msg))
+ } else {
+ fmt.Fprintf(w, "</a>")
+ }
+}
+
+// -- fileInfo ---------------------------------------------------------
+
+// FileInfo holds analysis information for the source file view.
+// Clients must not mutate it.
+type FileInfo struct {
+ Data []interface{} // JSON serializable values
+ Links []Link // HTML link markup
+}
+
+// A fileInfo is the server's store of hyperlinks and JSON data for a
+// particular file.
+type fileInfo struct {
+ mu sync.Mutex
+ data []interface{} // JSON objects
+ links []Link
+ sorted bool
+ hasErrors bool // TODO(adonovan): surface this in the UI
+}
+
+// addLink adds a link to the Go source file fi.
+func (fi *fileInfo) addLink(link Link) {
+ fi.mu.Lock()
+ fi.links = append(fi.links, link)
+ fi.sorted = false
+ if _, ok := link.(errorLink); ok {
+ fi.hasErrors = true
+ }
+ fi.mu.Unlock()
+}
+
+// addData adds the structured value x to the JSON data for the Go
+// source file fi. Its index is returned.
+func (fi *fileInfo) addData(x interface{}) int {
+ fi.mu.Lock()
+ index := len(fi.data)
+ fi.data = append(fi.data, x)
+ fi.mu.Unlock()
+ return index
+}
+
+// get returns the file info in external form.
+// Callers must not mutate its fields.
+func (fi *fileInfo) get() FileInfo {
+ var r FileInfo
+ // Copy slices, to avoid races.
+ fi.mu.Lock()
+ r.Data = append(r.Data, fi.data...)
+ if !fi.sorted {
+ sort.Sort(linksByStart(fi.links))
+ fi.sorted = true
+ }
+ r.Links = append(r.Links, fi.links...)
+ fi.mu.Unlock()
+ return r
+}
+
+// PackageInfo holds analysis information for the package view.
+// Clients must not mutate it.
+type PackageInfo struct {
+ CallGraph []*PCGNodeJSON
+ CallGraphIndex map[string]int
+ Types []*TypeInfoJSON
+}
+
+type pkgInfo struct {
+ mu sync.Mutex
+ callGraph []*PCGNodeJSON
+ callGraphIndex map[string]int // keys are (*ssa.Function).RelString()
+ types []*TypeInfoJSON // type info for exported types
+}
+
+func (pi *pkgInfo) setCallGraph(callGraph []*PCGNodeJSON, callGraphIndex map[string]int) {
+ pi.mu.Lock()
+ pi.callGraph = callGraph
+ pi.callGraphIndex = callGraphIndex
+ pi.mu.Unlock()
+}
+
+func (pi *pkgInfo) addType(t *TypeInfoJSON) {
+ pi.mu.Lock()
+ pi.types = append(pi.types, t)
+ pi.mu.Unlock()
+}
+
+// get returns the package info in external form.
+// Callers must not mutate its fields.
+func (pi *pkgInfo) get() PackageInfo {
+ var r PackageInfo
+ // Copy slices, to avoid races.
+ pi.mu.Lock()
+ r.CallGraph = append(r.CallGraph, pi.callGraph...)
+ r.CallGraphIndex = pi.callGraphIndex
+ r.Types = append(r.Types, pi.types...)
+ pi.mu.Unlock()
+ return r
+}
+
+// -- Result -----------------------------------------------------------
+
+// Result contains the results of analysis.
+// The result contains a mapping from filenames to a set of HTML links
+// and JavaScript data referenced by the links.
+type Result struct {
+ mu sync.Mutex // guards maps (but not their contents)
+ status string // global analysis status
+ fileInfos map[string]*fileInfo // keys are godoc file URLs
+ pkgInfos map[string]*pkgInfo // keys are import paths
+}
+
+// fileInfo returns the fileInfo for the specified godoc file URL,
+// constructing it as needed. Thread-safe.
+func (res *Result) fileInfo(url string) *fileInfo {
+ res.mu.Lock()
+ fi, ok := res.fileInfos[url]
+ if !ok {
+ if res.fileInfos == nil {
+ res.fileInfos = make(map[string]*fileInfo)
+ }
+ fi = new(fileInfo)
+ res.fileInfos[url] = fi
+ }
+ res.mu.Unlock()
+ return fi
+}
+
+// Status returns a human-readable description of the current analysis status.
+func (res *Result) Status() string {
+ res.mu.Lock()
+ defer res.mu.Unlock()
+ return res.status
+}
+
+func (res *Result) setStatusf(format string, args ...interface{}) {
+ res.mu.Lock()
+ res.status = fmt.Sprintf(format, args...)
+ log.Printf(format, args...)
+ res.mu.Unlock()
+}
+
+// FileInfo returns new slices containing opaque JSON values and the
+// HTML link markup for the specified godoc file URL. Thread-safe.
+// Callers must not mutate the elements.
+// It returns "zero" if no data is available.
+//
+func (res *Result) FileInfo(url string) (fi FileInfo) {
+ return res.fileInfo(url).get()
+}
+
+// pkgInfo returns the pkgInfo for the specified import path,
+// constructing it as needed. Thread-safe.
+func (res *Result) pkgInfo(importPath string) *pkgInfo {
+ res.mu.Lock()
+ pi, ok := res.pkgInfos[importPath]
+ if !ok {
+ if res.pkgInfos == nil {
+ res.pkgInfos = make(map[string]*pkgInfo)
+ }
+ pi = new(pkgInfo)
+ res.pkgInfos[importPath] = pi
+ }
+ res.mu.Unlock()
+ return pi
+}
+
+// PackageInfo returns new slices of JSON values for the callgraph and
+// type info for the specified package. Thread-safe.
+// Callers must not mutate its fields.
+// PackageInfo returns "zero" if no data is available.
+//
+func (res *Result) PackageInfo(importPath string) PackageInfo {
+ return res.pkgInfo(importPath).get()
+}
+
+// -- analysis ---------------------------------------------------------
+
+type analysis struct {
+ result *Result
+ prog *ssa.Program
+ ops []chanOp // all channel ops in program
+ allNamed []*types.Named // all named types in the program
+ ptaConfig pointer.Config
+ path2url map[string]string // maps openable path to godoc file URL (/src/fmt/print.go)
+ pcgs map[*ssa.Package]*packageCallGraph
+}
+
+// fileAndOffset returns the file and offset for a given pos.
+func (a *analysis) fileAndOffset(pos token.Pos) (fi *fileInfo, offset int) {
+ return a.fileAndOffsetPosn(a.prog.Fset.Position(pos))
+}
+
+// fileAndOffsetPosn returns the file and offset for a given position.
+func (a *analysis) fileAndOffsetPosn(posn token.Position) (fi *fileInfo, offset int) {
+ url := a.path2url[posn.Filename]
+ return a.result.fileInfo(url), posn.Offset
+}
+
+// posURL returns the URL of the source extent [pos, pos+len).
+func (a *analysis) posURL(pos token.Pos, len int) string {
+ if pos == token.NoPos {
+ return ""
+ }
+ posn := a.prog.Fset.Position(pos)
+ url := a.path2url[posn.Filename]
+ return fmt.Sprintf("%s?s=%d:%d#L%d",
+ url, posn.Offset, posn.Offset+len, posn.Line)
+}
+
+// ----------------------------------------------------------------------
+
+// Run runs program analysis and computes the resulting markup,
+// populating *result in a thread-safe manner, first with type
+// information then later with pointer analysis information if
+// enabled by the pta flag.
+//
+func Run(pta bool, result *Result) {
+ conf := loader.Config{
+ AllowErrors: true,
+ }
+
+ // Silence the default error handler.
+ // Don't print all errors; we'll report just
+ // one per errant package later.
+ conf.TypeChecker.Error = func(e error) {}
+
+ var roots, args []string // roots[i] ends with os.PathSeparator
+
+ // Enumerate packages in $GOROOT.
+ root := filepath.Join(runtime.GOROOT(), "src") + string(os.PathSeparator)
+ roots = append(roots, root)
+ args = allPackages(root)
+ log.Printf("GOROOT=%s: %s\n", root, args)
+
+ // Enumerate packages in $GOPATH.
+ for i, dir := range filepath.SplitList(build.Default.GOPATH) {
+ root := filepath.Join(dir, "src") + string(os.PathSeparator)
+ roots = append(roots, root)
+ pkgs := allPackages(root)
+ log.Printf("GOPATH[%d]=%s: %s\n", i, root, pkgs)
+ args = append(args, pkgs...)
+ }
+
+ // Uncomment to make startup quicker during debugging.
+ //args = []string{"golang.org/x/tools/cmd/godoc"}
+ //args = []string{"fmt"}
+
+ if _, err := conf.FromArgs(args, true); err != nil {
+ // TODO(adonovan): degrade gracefully, not fail totally.
+ // (The crippling case is a parse error in an external test file.)
+ result.setStatusf("Analysis failed: %s.", err) // import error
+ return
+ }
+
+ result.setStatusf("Loading and type-checking packages...")
+ iprog, err := conf.Load()
+ if iprog != nil {
+ // Report only the first error of each package.
+ for _, info := range iprog.AllPackages {
+ for _, err := range info.Errors {
+ fmt.Fprintln(os.Stderr, err)
+ break
+ }
+ }
+ log.Printf("Loaded %d packages.", len(iprog.AllPackages))
+ }
+ if err != nil {
+ result.setStatusf("Loading failed: %s.\n", err)
+ return
+ }
+
+ // Create SSA-form program representation.
+ // Only the transitively error-free packages are used.
+ prog := ssautil.CreateProgram(iprog, ssa.GlobalDebug)
+
+ // Compute the set of main packages, including testmain.
+ allPackages := prog.AllPackages()
+ var mainPkgs []*ssa.Package
+ if testmain := prog.CreateTestMainPackage(allPackages...); testmain != nil {
+ mainPkgs = append(mainPkgs, testmain)
+ if p := testmain.Const("packages"); p != nil {
+ log.Printf("Tested packages: %v", exact.StringVal(p.Value.Value))
+ }
+ }
+ for _, pkg := range allPackages {
+ if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
+ mainPkgs = append(mainPkgs, pkg)
+ }
+ }
+ log.Print("Transitively error-free main packages: ", mainPkgs)
+
+ // Build SSA code for bodies of all functions in the whole program.
+ result.setStatusf("Constructing SSA form...")
+ prog.Build()
+ log.Print("SSA construction complete")
+
+ a := analysis{
+ result: result,
+ prog: prog,
+ pcgs: make(map[*ssa.Package]*packageCallGraph),
+ }
+
+ // Build a mapping from openable filenames to godoc file URLs,
+ // i.e. "/src/" plus path relative to GOROOT/src or GOPATH[i]/src.
+ a.path2url = make(map[string]string)
+ for _, info := range iprog.AllPackages {
+ nextfile:
+ for _, f := range info.Files {
+ if f.Pos() == 0 {
+ continue // e.g. files generated by cgo
+ }
+ abs := iprog.Fset.File(f.Pos()).Name()
+ // Find the root to which this file belongs.
+ for _, root := range roots {
+ rel := strings.TrimPrefix(abs, root)
+ if len(rel) < len(abs) {
+ a.path2url[abs] = "/src/" + filepath.ToSlash(rel)
+ continue nextfile
+ }
+ }
+
+ log.Printf("Can't locate file %s (package %q) beneath any root",
+ abs, info.Pkg.Path())
+ }
+ }
+
+ // Add links for scanner, parser, type-checker errors.
+ // TODO(adonovan): fix: these links can overlap with
+ // identifier markup, causing the renderer to emit some
+ // characters twice.
+ errors := make(map[token.Position][]string)
+ for _, info := range iprog.AllPackages {
+ for _, err := range info.Errors {
+ switch err := err.(type) {
+ case types.Error:
+ posn := a.prog.Fset.Position(err.Pos)
+ errors[posn] = append(errors[posn], err.Msg)
+ case scanner.ErrorList:
+ for _, e := range err {
+ errors[e.Pos] = append(errors[e.Pos], e.Msg)
+ }
+ default:
+ log.Printf("Package %q has error (%T) without position: %v\n",
+ info.Pkg.Path(), err, err)
+ }
+ }
+ }
+ for posn, errs := range errors {
+ fi, offset := a.fileAndOffsetPosn(posn)
+ fi.addLink(errorLink{
+ start: offset,
+ msg: strings.Join(errs, "\n"),
+ })
+ }
+
+ // ---------- type-based analyses ----------
+
+ // Compute the all-pairs IMPLEMENTS relation.
+ // Collect all named types, even local types
+ // (which can have methods via promotion)
+ // and the built-in "error".
+ errorType := types.Universe.Lookup("error").Type().(*types.Named)
+ a.allNamed = append(a.allNamed, errorType)
+ for _, info := range iprog.AllPackages {
+ for _, obj := range info.Defs {
+ if obj, ok := obj.(*types.TypeName); ok {
+ a.allNamed = append(a.allNamed, obj.Type().(*types.Named))
+ }
+ }
+ }
+ log.Print("Computing implements relation...")
+ facts := computeImplements(&a.prog.MethodSets, a.allNamed)
+
+ // Add the type-based analysis results.
+ log.Print("Extracting type info...")
+ for _, info := range iprog.AllPackages {
+ a.doTypeInfo(info, facts)
+ }
+
+ a.visitInstrs(pta)
+
+ result.setStatusf("Type analysis complete.")
+
+ if pta {
+ a.pointer(mainPkgs)
+ }
+}
+
+// visitInstrs visits all SSA instructions in the program.
+func (a *analysis) visitInstrs(pta bool) {
+ log.Print("Visit instructions...")
+ for fn := range ssautil.AllFunctions(a.prog) {
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ // CALLEES (static)
+ // (Dynamic calls require pointer analysis.)
+ //
+ // We use the SSA representation to find the static callee,
+ // since in many cases it does better than the
+ // types.Info.{Refs,Selection} information. For example:
+ //
+ // defer func(){}() // static call to anon function
+ // f := func(){}; f() // static call to anon function
+ // f := fmt.Println; f() // static call to named function
+ //
+ // The downside is that we get no static callee information
+ // for packages that (transitively) contain errors.
+ if site, ok := instr.(ssa.CallInstruction); ok {
+ if callee := site.Common().StaticCallee(); callee != nil {
+ // TODO(adonovan): callgraph: elide wrappers.
+ // (Do static calls ever go to wrappers?)
+ if site.Common().Pos() != token.NoPos {
+ a.addCallees(site, []*ssa.Function{callee})
+ }
+ }
+ }
+
+ if !pta {
+ continue
+ }
+
+ // CHANNEL PEERS
+ // Collect send/receive/close instructions in the whole ssa.Program.
+ for _, op := range chanOps(instr) {
+ a.ops = append(a.ops, op)
+ a.ptaConfig.AddQuery(op.ch) // add channel ssa.Value to PTA query
+ }
+ }
+ }
+ }
+ log.Print("Visit instructions complete")
+}
+
+// pointer runs the pointer analysis.
+func (a *analysis) pointer(mainPkgs []*ssa.Package) {
+ // Run the pointer analysis and build the complete callgraph.
+ a.ptaConfig.Mains = mainPkgs
+ a.ptaConfig.BuildCallGraph = true
+ a.ptaConfig.Reflection = false // (for now)
+
+ a.result.setStatusf("Pointer analysis running...")
+
+ ptares, err := pointer.Analyze(&a.ptaConfig)
+ if err != nil {
+ // If this happens, it indicates a bug.
+ a.result.setStatusf("Pointer analysis failed: %s.", err)
+ return
+ }
+ log.Print("Pointer analysis complete.")
+
+ // Add the results of pointer analysis.
+
+ a.result.setStatusf("Computing channel peers...")
+ a.doChannelPeers(ptares.Queries)
+ a.result.setStatusf("Computing dynamic call graph edges...")
+ a.doCallgraph(ptares.CallGraph)
+
+ a.result.setStatusf("Analysis complete.")
+}
+
+type linksByStart []Link
+
+func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() }
+func (a linksByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a linksByStart) Len() int { return len(a) }
+
+// allPackages returns a new sorted slice of all packages beneath the
+// specified package root directory, e.g. $GOROOT/src or $GOPATH/src.
+// Derived from from go/ssa/stdlib_test.go
+// root must end with os.PathSeparator.
+//
+// TODO(adonovan): use buildutil.AllPackages when the tree thaws.
+func allPackages(root string) []string {
+ var pkgs []string
+ filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
+ if info == nil {
+ return nil // non-existent root directory?
+ }
+ if !info.IsDir() {
+ return nil // not a directory
+ }
+ // Prune the search if we encounter any of these names:
+ base := filepath.Base(path)
+ if base == "testdata" || strings.HasPrefix(base, ".") {
+ return filepath.SkipDir
+ }
+ pkg := filepath.ToSlash(strings.TrimPrefix(path, root))
+ switch pkg {
+ case "builtin":
+ return filepath.SkipDir
+ case "":
+ return nil // ignore root of tree
+ }
+ pkgs = append(pkgs, pkg)
+ return nil
+ })
+ return pkgs
+}
diff --git a/go/src/golang.org/x/tools/godoc/analysis/callgraph.go b/go/src/golang.org/x/tools/godoc/analysis/callgraph.go
index a98d294..4e97061 100644
--- a/go/src/golang.org/x/tools/godoc/analysis/callgraph.go
+++ b/go/src/golang.org/x/tools/godoc/analysis/callgraph.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package analysis
// This file computes the CALLERS and CALLEES relations from the call
@@ -12,13 +14,13 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"log"
"math/big"
"sort"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// doCallgraph computes the CALLEES and CALLERS relations.
@@ -104,7 +106,7 @@
var this *types.Package // for relativizing names
if callee.Pkg != nil {
- this = callee.Pkg.Object
+ this = callee.Pkg.Pkg
}
// Compute sites grouped by parent, with text and URLs.
@@ -169,14 +171,14 @@
roots := &pcg.nodes[0].edges
roots.SetBit(roots, i, 1)
}
- index[n.fn.RelString(pkg.Object)] = i
+ index[n.fn.RelString(pkg.Pkg)] = i
}
json := a.pcgJSON(pcg)
// TODO(adonovan): pkg.Path() is not unique!
// It is possible to declare a non-test package called x_test.
- a.result.pkgInfo(pkg.Object.Path()).setCallGraph(json, index)
+ a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
}
}
@@ -188,7 +190,7 @@
}
var this *types.Package // for relativizing names
if p := site.Parent().Package(); p != nil {
- this = p.Object
+ this = p.Pkg
}
for _, fn := range fns {
@@ -242,10 +244,10 @@
}
if fn.Synthetic != "" && fn.Name() == "init" {
// (This is the actual initializer, not a declared 'func init').
- if fn.Pkg.Object == this {
+ if fn.Pkg.Pkg == this {
return "package initializer"
}
- return fmt.Sprintf("%q package initializer", fn.Pkg.Object.Path())
+ return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
}
return fn.RelString(this)
}
@@ -273,7 +275,7 @@
for fn := range pcg.nodeIndex {
nodes = append(nodes, &pcgNode{
fn: fn,
- pretty: prettyFunc(fn.Pkg.Object, fn),
+ pretty: prettyFunc(fn.Pkg.Pkg, fn),
})
}
sort.Sort(pcgNodesByPretty(nodes[1:]))
diff --git a/go/src/golang.org/x/tools/godoc/analysis/callgraph14.go b/go/src/golang.org/x/tools/godoc/analysis/callgraph14.go
new file mode 100644
index 0000000..2692d7e
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/analysis/callgraph14.go
@@ -0,0 +1,353 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package analysis
+
+// This file computes the CALLERS and CALLEES relations from the call
+// graph. CALLERS/CALLEES information is displayed in the lower pane
+// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "log"
+ "math/big"
+ "sort"
+
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+// doCallgraph computes the CALLEES and CALLERS relations.
+func (a *analysis) doCallgraph(cg *callgraph.Graph) {
+ log.Print("Deleting synthetic nodes...")
+ // TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
+ // inefficient and can be (unpredictably) slow.
+ cg.DeleteSyntheticNodes()
+ log.Print("Synthetic nodes deleted")
+
+ // Populate nodes of package call graphs (PCGs).
+ for _, n := range cg.Nodes {
+ a.pcgAddNode(n.Func)
+ }
+ // Within each PCG, sort funcs by name.
+ for _, pcg := range a.pcgs {
+ pcg.sortNodes()
+ }
+
+ calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
+ callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
+ for _, n := range cg.Nodes {
+ for _, e := range n.Out {
+ if e.Site == nil {
+ continue // a call from a synthetic node such as <root>
+ }
+
+ // Add (site pos, callee) to calledFuncs.
+ // (Dynamic calls only.)
+ callee := e.Callee.Func
+
+ a.pcgAddEdge(n.Func, callee)
+
+ if callee.Synthetic != "" {
+ continue // call of a package initializer
+ }
+
+ if e.Site.Common().StaticCallee() == nil {
+ // dynamic call
+ // (CALLEES information for static calls
+ // is computed using SSA information.)
+ lparen := e.Site.Common().Pos()
+ if lparen != token.NoPos {
+ fns := calledFuncs[e.Site]
+ if fns == nil {
+ fns = make(map[*ssa.Function]bool)
+ calledFuncs[e.Site] = fns
+ }
+ fns[callee] = true
+ }
+ }
+
+ // Add (callee, site) to callingSites.
+ fns := callingSites[callee]
+ if fns == nil {
+ fns = make(map[ssa.CallInstruction]bool)
+ callingSites[callee] = fns
+ }
+ fns[e.Site] = true
+ }
+ }
+
+ // CALLEES.
+ log.Print("Callees...")
+ for site, fns := range calledFuncs {
+ var funcs funcsByPos
+ for fn := range fns {
+ funcs = append(funcs, fn)
+ }
+ sort.Sort(funcs)
+
+ a.addCallees(site, funcs)
+ }
+
+ // CALLERS
+ log.Print("Callers...")
+ for callee, sites := range callingSites {
+ pos := funcToken(callee)
+ if pos == token.NoPos {
+ log.Printf("CALLERS: skipping %s: no pos", callee)
+ continue
+ }
+
+ var this *types.Package // for relativizing names
+ if callee.Pkg != nil {
+ this = callee.Pkg.Pkg
+ }
+
+ // Compute sites grouped by parent, with text and URLs.
+ sitesByParent := make(map[*ssa.Function]sitesByPos)
+ for site := range sites {
+ fn := site.Parent()
+ sitesByParent[fn] = append(sitesByParent[fn], site)
+ }
+ var funcs funcsByPos
+ for fn := range sitesByParent {
+ funcs = append(funcs, fn)
+ }
+ sort.Sort(funcs)
+
+ v := callersJSON{
+ Callee: callee.String(),
+ Callers: []callerJSON{}, // (JS wants non-nil)
+ }
+ for _, fn := range funcs {
+ caller := callerJSON{
+ Func: prettyFunc(this, fn),
+ Sites: []anchorJSON{}, // (JS wants non-nil)
+ }
+ sites := sitesByParent[fn]
+ sort.Sort(sites)
+ for _, site := range sites {
+ pos := site.Common().Pos()
+ if pos != token.NoPos {
+ caller.Sites = append(caller.Sites, anchorJSON{
+ Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
+ Href: a.posURL(pos, len("(")),
+ })
+ }
+ }
+ v.Callers = append(v.Callers, caller)
+ }
+
+ fi, offset := a.fileAndOffset(pos)
+ fi.addLink(aLink{
+ start: offset,
+ end: offset + len("func"),
+ title: fmt.Sprintf("%d callers", len(sites)),
+ onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
+ })
+ }
+
+ // PACKAGE CALLGRAPH
+ log.Print("Package call graph...")
+ for pkg, pcg := range a.pcgs {
+ // Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
+ index := make(map[string]int)
+
+ // Treat exported functions (and exported methods of
+ // exported named types) as roots even if they aren't
+ // actually called from outside the package.
+ for i, n := range pcg.nodes {
+ if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
+ continue
+ }
+ recv := n.fn.Signature.Recv()
+ if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
+ roots := &pcg.nodes[0].edges
+ roots.SetBit(roots, i, 1)
+ }
+ index[n.fn.RelString(pkg.Pkg)] = i
+ }
+
+ json := a.pcgJSON(pcg)
+
+ // TODO(adonovan): pkg.Path() is not unique!
+ // It is possible to declare a non-test package called x_test.
+ a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
+ }
+}
+
+// addCallees adds client data and links for the facts that site calls fns.
+func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
+ v := calleesJSON{
+ Descr: site.Common().Description(),
+ Callees: []anchorJSON{}, // (JS wants non-nil)
+ }
+ var this *types.Package // for relativizing names
+ if p := site.Parent().Package(); p != nil {
+ this = p.Pkg
+ }
+
+ for _, fn := range fns {
+ v.Callees = append(v.Callees, anchorJSON{
+ Text: prettyFunc(this, fn),
+ Href: a.posURL(funcToken(fn), len("func")),
+ })
+ }
+
+ fi, offset := a.fileAndOffset(site.Common().Pos())
+ fi.addLink(aLink{
+ start: offset,
+ end: offset + len("("),
+ title: fmt.Sprintf("%d callees", len(v.Callees)),
+ onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
+ })
+}
+
+// -- utilities --------------------------------------------------------
+
+// stable order within packages but undefined across packages.
+type funcsByPos []*ssa.Function
+
+func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
+func (a funcsByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a funcsByPos) Len() int { return len(a) }
+
+type sitesByPos []ssa.CallInstruction
+
+func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
+func (a sitesByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a sitesByPos) Len() int { return len(a) }
+
+func funcToken(fn *ssa.Function) token.Pos {
+ switch syntax := fn.Syntax().(type) {
+ case *ast.FuncLit:
+ return syntax.Type.Func
+ case *ast.FuncDecl:
+ return syntax.Type.Func
+ }
+ return token.NoPos
+}
+
+// prettyFunc pretty-prints fn for the user interface.
+// TODO(adonovan): return HTML so we have more markup freedom.
+func prettyFunc(this *types.Package, fn *ssa.Function) string {
+ if fn.Parent() != nil {
+ return fmt.Sprintf("%s in %s",
+ types.TypeString(fn.Signature, types.RelativeTo(this)),
+ prettyFunc(this, fn.Parent()))
+ }
+ if fn.Synthetic != "" && fn.Name() == "init" {
+ // (This is the actual initializer, not a declared 'func init').
+ if fn.Pkg.Pkg == this {
+ return "package initializer"
+ }
+ return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
+ }
+ return fn.RelString(this)
+}
+
+// -- intra-package callgraph ------------------------------------------
+
+// pcgNode represents a node in the package call graph (PCG).
+type pcgNode struct {
+ fn *ssa.Function
+ pretty string // cache of prettyFunc(fn)
+ edges big.Int // set of callee func indices
+}
+
+// A packageCallGraph represents the intra-package edges of the global call graph.
+// The zeroth node indicates "all external functions".
+type packageCallGraph struct {
+ nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
+ nodes []*pcgNode // maps node index to node
+}
+
+// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
+func (pcg *packageCallGraph) sortNodes() {
+ nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
+ nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
+ for fn := range pcg.nodeIndex {
+ nodes = append(nodes, &pcgNode{
+ fn: fn,
+ pretty: prettyFunc(fn.Pkg.Pkg, fn),
+ })
+ }
+ sort.Sort(pcgNodesByPretty(nodes[1:]))
+ for i, n := range nodes {
+ pcg.nodeIndex[n.fn] = i
+ }
+ pcg.nodes = nodes
+}
+
+func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
+ var callerIndex int
+ if caller.Pkg == callee.Pkg {
+ // intra-package edge
+ callerIndex = pcg.nodeIndex[caller]
+ if callerIndex < 1 {
+ panic(caller)
+ }
+ }
+ edges := &pcg.nodes[callerIndex].edges
+ edges.SetBit(edges, pcg.nodeIndex[callee], 1)
+}
+
+func (a *analysis) pcgAddNode(fn *ssa.Function) {
+ if fn.Pkg == nil {
+ return
+ }
+ pcg, ok := a.pcgs[fn.Pkg]
+ if !ok {
+ pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
+ a.pcgs[fn.Pkg] = pcg
+ }
+ pcg.nodeIndex[fn] = -1
+}
+
+func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
+ if callee.Pkg != nil {
+ a.pcgs[callee.Pkg].addEdge(caller, callee)
+ }
+}
+
+// pcgJSON returns a new slice of callgraph JSON values.
+func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
+ var nodes []*PCGNodeJSON
+ for _, n := range pcg.nodes {
+
+ // TODO(adonovan): why is there no good way to iterate
+ // over the set bits of a big.Int?
+ var callees []int
+ nbits := n.edges.BitLen()
+ for j := 0; j < nbits; j++ {
+ if n.edges.Bit(j) == 1 {
+ callees = append(callees, j)
+ }
+ }
+
+ var pos token.Pos
+ if n.fn != nil {
+ pos = funcToken(n.fn)
+ }
+ nodes = append(nodes, &PCGNodeJSON{
+ Func: anchorJSON{
+ Text: n.pretty,
+ Href: a.posURL(pos, len("func")),
+ },
+ Callees: callees,
+ })
+ }
+ return nodes
+}
+
+type pcgNodesByPretty []*pcgNode
+
+func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
+func (a pcgNodesByPretty) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a pcgNodesByPretty) Len() int { return len(a) }
diff --git a/go/src/golang.org/x/tools/godoc/analysis/implements.go b/go/src/golang.org/x/tools/godoc/analysis/implements.go
index bee04d0..1c856c3 100644
--- a/go/src/golang.org/x/tools/godoc/analysis/implements.go
+++ b/go/src/golang.org/x/tools/godoc/analysis/implements.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package analysis
// This file computes the "implements" relation over all pairs of
@@ -11,9 +13,9 @@
// belong to different packages and at least one is not exported?
import (
+ "go/types"
"sort"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/godoc/analysis/implements14.go b/go/src/golang.org/x/tools/godoc/analysis/implements14.go
new file mode 100644
index 0000000..0ad1008
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/analysis/implements14.go
@@ -0,0 +1,197 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package analysis
+
+// This file computes the "implements" relation over all pairs of
+// named types in the program. (The mark-up is done by typeinfo.go.)
+
+// TODO(adonovan): do we want to report implements(C, I) where C and I
+// belong to different packages and at least one is not exported?
+
+import (
+ "sort"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// computeImplements computes the "implements" relation over all pairs
+// of named types in allNamed.
+func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
+ // Information about a single type's method set.
+ type msetInfo struct {
+ typ types.Type
+ mset *types.MethodSet
+ mask1, mask2 uint64
+ }
+
+ initMsetInfo := func(info *msetInfo, typ types.Type) {
+ info.typ = typ
+ info.mset = cache.MethodSet(typ)
+ for i := 0; i < info.mset.Len(); i++ {
+ name := info.mset.At(i).Obj().Name()
+ info.mask1 |= 1 << methodBit(name[0])
+ info.mask2 |= 1 << methodBit(name[len(name)-1])
+ }
+ }
+
+ // satisfies(T, U) reports whether type T satisfies type U.
+ // U must be an interface.
+ //
+ // Since there are thousands of types (and thus millions of
+ // pairs of types) and types.Assignable(T, U) is relatively
+ // expensive, we compute assignability directly from the
+ // method sets. (At least one of T and U must be an
+ // interface.)
+ //
+ // We use a trick (thanks gri!) related to a Bloom filter to
+ // quickly reject most tests, which are false. For each
+ // method set, we precompute a mask, a set of bits, one per
+ // distinct initial byte of each method name. Thus the mask
+ // for io.ReadWriter would be {'R','W'}. AssignableTo(T, U)
+ // cannot be true unless mask(T)&mask(U)==mask(U).
+ //
+ // As with a Bloom filter, we can improve precision by testing
+ // additional hashes, e.g. using the last letter of each
+ // method name, so long as the subset mask property holds.
+ //
+ // When analyzing the standard library, there are about 1e6
+ // calls to satisfies(), of which 0.6% return true. With a
+ // 1-hash filter, 95% of calls avoid the expensive check; with
+ // a 2-hash filter, this grows to 98.2%.
+ satisfies := func(T, U *msetInfo) bool {
+ return T.mask1&U.mask1 == U.mask1 &&
+ T.mask2&U.mask2 == U.mask2 &&
+ containsAllIdsOf(T.mset, U.mset)
+ }
+
+ // Information about a named type N, and perhaps also *N.
+ type namedInfo struct {
+ isInterface bool
+ base msetInfo // N
+ ptr msetInfo // *N, iff N !isInterface
+ }
+
+ var infos []namedInfo
+
+ // Precompute the method sets and their masks.
+ for _, N := range allNamed {
+ var info namedInfo
+ initMsetInfo(&info.base, N)
+ _, info.isInterface = N.Underlying().(*types.Interface)
+ if !info.isInterface {
+ initMsetInfo(&info.ptr, types.NewPointer(N))
+ }
+
+ if info.base.mask1|info.ptr.mask1 == 0 {
+ continue // neither N nor *N has methods
+ }
+
+ infos = append(infos, info)
+ }
+
+ facts := make(map[*types.Named]implementsFacts)
+
+ // Test all pairs of distinct named types (T, U).
+ // TODO(adonovan): opt: compute (U, T) at the same time.
+ for t := range infos {
+ T := &infos[t]
+ var to, from, fromPtr []types.Type
+ for u := range infos {
+ if t == u {
+ continue
+ }
+ U := &infos[u]
+ switch {
+ case T.isInterface && U.isInterface:
+ if satisfies(&U.base, &T.base) {
+ to = append(to, U.base.typ)
+ }
+ if satisfies(&T.base, &U.base) {
+ from = append(from, U.base.typ)
+ }
+ case T.isInterface: // U concrete
+ if satisfies(&U.base, &T.base) {
+ to = append(to, U.base.typ)
+ } else if satisfies(&U.ptr, &T.base) {
+ to = append(to, U.ptr.typ)
+ }
+ case U.isInterface: // T concrete
+ if satisfies(&T.base, &U.base) {
+ from = append(from, U.base.typ)
+ } else if satisfies(&T.ptr, &U.base) {
+ fromPtr = append(fromPtr, U.base.typ)
+ }
+ }
+ }
+
+ // Sort types (arbitrarily) to avoid nondeterminism.
+ sort.Sort(typesByString(to))
+ sort.Sort(typesByString(from))
+ sort.Sort(typesByString(fromPtr))
+
+ facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
+ }
+
+ return facts
+}
+
+type implementsFacts struct {
+ to []types.Type // named or ptr-to-named types assignable to interface T
+ from []types.Type // named interfaces assignable from T
+ fromPtr []types.Type // named interfaces assignable only from *T
+}
+
+type typesByString []types.Type
+
+func (p typesByString) Len() int { return len(p) }
+func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
+func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+// methodBit returns the index of x in [a-zA-Z], or 52 if not found.
+func methodBit(x byte) uint64 {
+ switch {
+ case 'a' <= x && x <= 'z':
+ return uint64(x - 'a')
+ case 'A' <= x && x <= 'Z':
+ return uint64(26 + x - 'A')
+ }
+ return 52 // all other bytes
+}
+
+// containsAllIdsOf reports whether the method identifiers of T are a
+// superset of those in U. If U belongs to an interface type, the
+// result is equal to types.Assignable(T, U), but is cheaper to compute.
+//
+// TODO(gri): make this a method of *types.MethodSet.
+//
+func containsAllIdsOf(T, U *types.MethodSet) bool {
+ t, tlen := 0, T.Len()
+ u, ulen := 0, U.Len()
+ for t < tlen && u < ulen {
+ tMeth := T.At(t).Obj()
+ uMeth := U.At(u).Obj()
+ tId := tMeth.Id()
+ uId := uMeth.Id()
+ if tId > uId {
+ // U has a method T lacks: fail.
+ return false
+ }
+ if tId < uId {
+ // T has a method U lacks: ignore it.
+ t++
+ continue
+ }
+ // U and T both have a method of this Id. Check types.
+ if !types.Identical(tMeth.Type(), uMeth.Type()) {
+ return false // type mismatch
+ }
+ u++
+ t++
+ }
+ return u == ulen
+}
diff --git a/go/src/golang.org/x/tools/godoc/analysis/peers.go b/go/src/golang.org/x/tools/godoc/analysis/peers.go
index e1696e2..74a08a1 100644
--- a/go/src/golang.org/x/tools/godoc/analysis/peers.go
+++ b/go/src/golang.org/x/tools/godoc/analysis/peers.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package analysis
// This file computes the channel "peers" relation over all pairs of
@@ -14,10 +16,10 @@
import (
"fmt"
"go/token"
+ "go/types"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
@@ -72,7 +74,7 @@
Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
len("make")),
},
- Fn: makechan.Parent().RelString(op.fn.Package().Object),
+ Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
})
for _, op := range aliasedOps[makechan] {
ops[op] = true
diff --git a/go/src/golang.org/x/tools/godoc/analysis/peers14.go b/go/src/golang.org/x/tools/godoc/analysis/peers14.go
new file mode 100644
index 0000000..ba5e8d6
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/analysis/peers14.go
@@ -0,0 +1,156 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package analysis
+
+// This file computes the channel "peers" relation over all pairs of
+// channel operations in the program. The peers are displayed in the
+// lower pane when a channel operation (make, <-, close) is clicked.
+
+// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
+// then enable reflection in PTA.
+
+import (
+ "fmt"
+ "go/token"
+
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+)
+
+func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
+ addSendRecv := func(j *commJSON, op chanOp) {
+ j.Ops = append(j.Ops, commOpJSON{
+ Op: anchorJSON{
+ Text: op.mode,
+ Href: a.posURL(op.pos, op.len),
+ },
+ Fn: prettyFunc(nil, op.fn),
+ })
+ }
+
+ // Build an undirected bipartite multigraph (binary relation)
+ // of MakeChan ops and send/recv/close ops.
+ //
+ // TODO(adonovan): opt: use channel element types to partition
+ // the O(n^2) problem into subproblems.
+ aliasedOps := make(map[*ssa.MakeChan][]chanOp)
+ opToMakes := make(map[chanOp][]*ssa.MakeChan)
+ for _, op := range a.ops {
+ // Combine the PT sets from all contexts.
+ var makes []*ssa.MakeChan // aliased ops
+ ptr, ok := ptsets[op.ch]
+ if !ok {
+ continue // e.g. channel op in dead code
+ }
+ for _, label := range ptr.PointsTo().Labels() {
+ makechan, ok := label.Value().(*ssa.MakeChan)
+ if !ok {
+ continue // skip intrinsically-created channels for now
+ }
+ if makechan.Pos() == token.NoPos {
+ continue // not possible?
+ }
+ makes = append(makes, makechan)
+ aliasedOps[makechan] = append(aliasedOps[makechan], op)
+ }
+ opToMakes[op] = makes
+ }
+
+ // Now that complete relation is built, build links for ops.
+ for _, op := range a.ops {
+ v := commJSON{
+ Ops: []commOpJSON{}, // (JS wants non-nil)
+ }
+ ops := make(map[chanOp]bool)
+ for _, makechan := range opToMakes[op] {
+ v.Ops = append(v.Ops, commOpJSON{
+ Op: anchorJSON{
+ Text: "made",
+ Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
+ len("make")),
+ },
+ Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
+ })
+ for _, op := range aliasedOps[makechan] {
+ ops[op] = true
+ }
+ }
+ for op := range ops {
+ addSendRecv(&v, op)
+ }
+
+ // Add links for each aliased op.
+ fi, offset := a.fileAndOffset(op.pos)
+ fi.addLink(aLink{
+ start: offset,
+ end: offset + op.len,
+ title: "show channel ops",
+ onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
+ })
+ }
+ // Add links for makechan ops themselves.
+ for makechan, ops := range aliasedOps {
+ v := commJSON{
+ Ops: []commOpJSON{}, // (JS wants non-nil)
+ }
+ for _, op := range ops {
+ addSendRecv(&v, op)
+ }
+
+ fi, offset := a.fileAndOffset(makechan.Pos())
+ fi.addLink(aLink{
+ start: offset - len("make"),
+ end: offset,
+ title: "show channel ops",
+ onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
+ })
+ }
+}
+
+// -- utilities --------------------------------------------------------
+
+// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
+// Derived from oracle/peers.go.
+type chanOp struct {
+ ch ssa.Value
+ mode string // sent|received|closed
+ pos token.Pos
+ len int
+ fn *ssa.Function
+}
+
+// chanOps returns a slice of all the channel operations in the instruction.
+// Derived from oracle/peers.go.
+func chanOps(instr ssa.Instruction) []chanOp {
+ fn := instr.Parent()
+ var ops []chanOp
+ switch instr := instr.(type) {
+ case *ssa.UnOp:
+ if instr.Op == token.ARROW {
+ // TODO(adonovan): don't assume <-ch; could be 'range ch'.
+ ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
+ }
+ case *ssa.Send:
+ ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
+ case *ssa.Select:
+ for _, st := range instr.States {
+ mode := "received"
+ if st.Dir == types.SendOnly {
+ mode = "sent"
+ }
+ ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
+ }
+ case ssa.CallInstruction:
+ call := instr.Common()
+ if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
+ pos := instr.Common().Pos()
+ ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
+ }
+ }
+ return ops
+}
diff --git a/go/src/golang.org/x/tools/godoc/analysis/typeinfo.go b/go/src/golang.org/x/tools/godoc/analysis/typeinfo.go
index 83e19c1..1ccff52 100644
--- a/go/src/golang.org/x/tools/godoc/analysis/typeinfo.go
+++ b/go/src/golang.org/x/tools/godoc/analysis/typeinfo.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package analysis
// This file computes the markup for information from go/types:
@@ -20,12 +22,12 @@
import (
"fmt"
+ "go/types"
"reflect"
"strconv"
"strings"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/godoc/analysis/typeinfo14.go b/go/src/golang.org/x/tools/godoc/analysis/typeinfo14.go
new file mode 100644
index 0000000..d10abce
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/analysis/typeinfo14.go
@@ -0,0 +1,234 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package analysis
+
+// This file computes the markup for information from go/types:
+// IMPORTS, identifier RESOLUTION, METHOD SETS, size/alignment, and
+// the IMPLEMENTS relation.
+//
+// IMPORTS links connect import specs to the documentation for the
+// imported package.
+//
+// RESOLUTION links referring identifiers to their defining
+// identifier, and adds tooltips for kind and type.
+//
+// METHOD SETS, size/alignment, and the IMPLEMENTS relation are
+// displayed in the lower pane when a type's defining identifier is
+// clicked.
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// TODO(adonovan): audit to make sure it's safe on ill-typed packages.
+
+// TODO(adonovan): use same Sizes as loader.Config.
+var sizes = types.StdSizes{8, 8}
+
+func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) {
+ // We must not assume the corresponding SSA packages were
+ // created (i.e. were transitively error-free).
+
+ // IMPORTS
+ for _, f := range info.Files {
+ // Package decl.
+ fi, offset := a.fileAndOffset(f.Name.Pos())
+ fi.addLink(aLink{
+ start: offset,
+ end: offset + len(f.Name.Name),
+ title: "Package docs for " + info.Pkg.Path(),
+ // TODO(adonovan): fix: we're putting the untrusted Path()
+ // into a trusted field. What's the appropriate sanitizer?
+ href: "/pkg/" + info.Pkg.Path(),
+ })
+
+ // Import specs.
+ for _, imp := range f.Imports {
+ // Remove quotes.
+ L := int(imp.End()-imp.Path.Pos()) - len(`""`)
+ path, _ := strconv.Unquote(imp.Path.Value)
+ fi, offset := a.fileAndOffset(imp.Path.Pos())
+ fi.addLink(aLink{
+ start: offset + 1,
+ end: offset + 1 + L,
+ title: "Package docs for " + path,
+ // TODO(adonovan): fix: we're putting the untrusted path
+ // into a trusted field. What's the appropriate sanitizer?
+ href: "/pkg/" + path,
+ })
+ }
+ }
+
+ // RESOLUTION
+ qualifier := types.RelativeTo(info.Pkg)
+ for id, obj := range info.Uses {
+ // Position of the object definition.
+ pos := obj.Pos()
+ Len := len(obj.Name())
+
+ // Correct the position for non-renaming import specs.
+ // import "sync/atomic"
+ // ^^^^^^^^^^^
+ if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() {
+ // Assume this is a non-renaming import.
+ // NB: not true for degenerate renamings: `import foo "foo"`.
+ pos++
+ Len = len(obj.Imported().Path())
+ }
+
+ if obj.Pkg() == nil {
+ continue // don't mark up built-ins.
+ }
+
+ fi, offset := a.fileAndOffset(id.NamePos)
+ fi.addLink(aLink{
+ start: offset,
+ end: offset + len(id.Name),
+ title: types.ObjectString(obj, qualifier),
+ href: a.posURL(pos, Len),
+ })
+ }
+
+ // IMPLEMENTS & METHOD SETS
+ for _, obj := range info.Defs {
+ if obj, ok := obj.(*types.TypeName); ok {
+ a.namedType(obj, implements)
+ }
+ }
+}
+
+func (a *analysis) namedType(obj *types.TypeName, implements map[*types.Named]implementsFacts) {
+ qualifier := types.RelativeTo(obj.Pkg())
+ T := obj.Type().(*types.Named)
+ v := &TypeInfoJSON{
+ Name: obj.Name(),
+ Size: sizes.Sizeof(T),
+ Align: sizes.Alignof(T),
+ Methods: []anchorJSON{}, // (JS wants non-nil)
+ }
+
+ // addFact adds the fact "is implemented by T" (by) or
+ // "implements T" (!by) to group.
+ addFact := func(group *implGroupJSON, T types.Type, by bool) {
+ Tobj := deref(T).(*types.Named).Obj()
+ var byKind string
+ if by {
+ // Show underlying kind of implementing type,
+ // e.g. "slice", "array", "struct".
+ s := reflect.TypeOf(T.Underlying()).String()
+ byKind = strings.ToLower(strings.TrimPrefix(s, "*types."))
+ }
+ group.Facts = append(group.Facts, implFactJSON{
+ ByKind: byKind,
+ Other: anchorJSON{
+ Href: a.posURL(Tobj.Pos(), len(Tobj.Name())),
+ Text: types.TypeString(T, qualifier),
+ },
+ })
+ }
+
+ // IMPLEMENTS
+ if r, ok := implements[T]; ok {
+ if isInterface(T) {
+ // "T is implemented by <conc>" ...
+ // "T is implemented by <iface>"...
+ // "T implements <iface>"...
+ group := implGroupJSON{
+ Descr: types.TypeString(T, qualifier),
+ }
+ // Show concrete types first; use two passes.
+ for _, sub := range r.to {
+ if !isInterface(sub) {
+ addFact(&group, sub, true)
+ }
+ }
+ for _, sub := range r.to {
+ if isInterface(sub) {
+ addFact(&group, sub, true)
+ }
+ }
+ for _, super := range r.from {
+ addFact(&group, super, false)
+ }
+ v.ImplGroups = append(v.ImplGroups, group)
+ } else {
+ // T is concrete.
+ if r.from != nil {
+ // "T implements <iface>"...
+ group := implGroupJSON{
+ Descr: types.TypeString(T, qualifier),
+ }
+ for _, super := range r.from {
+ addFact(&group, super, false)
+ }
+ v.ImplGroups = append(v.ImplGroups, group)
+ }
+ if r.fromPtr != nil {
+ // "*C implements <iface>"...
+ group := implGroupJSON{
+ Descr: "*" + types.TypeString(T, qualifier),
+ }
+ for _, psuper := range r.fromPtr {
+ addFact(&group, psuper, false)
+ }
+ v.ImplGroups = append(v.ImplGroups, group)
+ }
+ }
+ }
+
+ // METHOD SETS
+ for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) {
+ meth := sel.Obj().(*types.Func)
+ pos := meth.Pos() // may be 0 for error.Error
+ v.Methods = append(v.Methods, anchorJSON{
+ Href: a.posURL(pos, len(meth.Name())),
+ Text: types.SelectionString(sel, qualifier),
+ })
+ }
+
+ // Since there can be many specs per decl, we
+ // can't attach the link to the keyword 'type'
+ // (as we do with 'func'); we use the Ident.
+ fi, offset := a.fileAndOffset(obj.Pos())
+ fi.addLink(aLink{
+ start: offset,
+ end: offset + len(obj.Name()),
+ title: fmt.Sprintf("type info for %s", obj.Name()),
+ onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)),
+ })
+
+ // Add info for exported package-level types to the package info.
+ if obj.Exported() && isPackageLevel(obj) {
+ // TODO(adonovan): Path is not unique!
+ // It is possible to declare a non-test package called x_test.
+ a.result.pkgInfo(obj.Pkg().Path()).addType(v)
+ }
+}
+
+// -- utilities --------------------------------------------------------
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
+
+// isPackageLevel reports whether obj is a package-level object.
+func isPackageLevel(obj types.Object) bool {
+ return obj.Pkg().Scope().Lookup(obj.Name()) == obj
+}
diff --git a/go/src/golang.org/x/tools/godoc/appengine.go b/go/src/golang.org/x/tools/godoc/appengine.go
new file mode 100644
index 0000000..2a68558
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/appengine.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package godoc
+
+import "appengine"
+
+func init() {
+ onAppengine = !appengine.IsDevAppServer()
+}
diff --git a/go/src/golang.org/x/tools/godoc/dl/dl.go b/go/src/golang.org/x/tools/godoc/dl/dl.go
new file mode 100644
index 0000000..ed6e590
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/dl/dl.go
@@ -0,0 +1,511 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+// Package dl implements a simple downloads frontend server.
+//
+// It accepts HTTP POST requests to create a new download metadata entity, and
+// lists entities with sorting and filtering.
+// It is designed to run only on the instance of godoc that serves golang.org.
+package dl
+
+import (
+ "crypto/hmac"
+ "crypto/md5"
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "io"
+ "net/http"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+
+ "google.golang.org/appengine"
+ "google.golang.org/appengine/datastore"
+ "google.golang.org/appengine/log"
+ "google.golang.org/appengine/memcache"
+ "google.golang.org/appengine/user"
+)
+
+const (
+ gcsBaseURL = "https://storage.googleapis.com/golang/"
+ cacheKey = "download_list_3" // increment if listTemplateData changes
+ cacheDuration = time.Hour
+)
+
+func RegisterHandlers(mux *http.ServeMux) {
+ mux.Handle("/dl", http.RedirectHandler("/dl/", http.StatusFound))
+ mux.HandleFunc("/dl/", getHandler) // also serves listHandler
+ mux.HandleFunc("/dl/upload", uploadHandler)
+ mux.HandleFunc("/dl/init", initHandler)
+}
+
+type File struct {
+ Filename string
+ OS string
+ Arch string
+ Version string
+ Checksum string `datastore:",noindex"` // SHA1; deprecated
+ ChecksumSHA256 string `datastore:",noindex"`
+ Size int64 `datastore:",noindex"`
+ Kind string // "archive", "installer", "source"
+ Uploaded time.Time
+}
+
+func (f File) ChecksumType() string {
+ if f.ChecksumSHA256 != "" {
+ return "SHA256"
+ }
+ return "SHA1"
+}
+
+func (f File) PrettyChecksum() string {
+ if f.ChecksumSHA256 != "" {
+ return f.ChecksumSHA256
+ }
+ return f.Checksum
+}
+
+func (f File) PrettyOS() string {
+ if f.OS == "darwin" {
+ switch {
+ case strings.Contains(f.Filename, "osx10.8"):
+ return "OS X 10.8+"
+ case strings.Contains(f.Filename, "osx10.6"):
+ return "OS X 10.6+"
+ }
+ }
+ return pretty(f.OS)
+}
+
+func (f File) PrettySize() string {
+ const mb = 1 << 20
+ if f.Size == 0 {
+ return ""
+ }
+ if f.Size < mb {
+ // All Go releases are >1mb, but handle this case anyway.
+ return fmt.Sprintf("%v bytes", f.Size)
+ }
+ return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
+}
+
+func (f File) Highlight() bool {
+ switch {
+ case f.Kind == "source":
+ return true
+ case f.Arch == "amd64" && f.OS == "linux":
+ return true
+ case f.Arch == "amd64" && f.Kind == "installer":
+ switch f.OS {
+ case "windows":
+ return true
+ case "darwin":
+ if !strings.Contains(f.Filename, "osx10.6") {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (f File) URL() string {
+ return gcsBaseURL + f.Filename
+}
+
+type Release struct {
+ Version string
+ Stable bool
+ Files []File
+ Visible bool // show files on page load
+}
+
+type Feature struct {
+ // The File field will be filled in by the first stable File
+ // whose name matches the given fileRE.
+ File
+ fileRE *regexp.Regexp
+
+ Platform string // "Microsoft Windows", "Mac OS X", "Linux"
+ Requirements string // "Windows XP and above, 64-bit Intel Processor"
+}
+
+// featuredFiles lists the platforms and files to be featured
+// at the top of the downloads page.
+var featuredFiles = []Feature{
+ {
+ Platform: "Microsoft Windows",
+ Requirements: "Windows XP or later, Intel 64-bit processor",
+ fileRE: regexp.MustCompile(`\.windows-amd64\.msi$`),
+ },
+ {
+ Platform: "Apple OS X",
+ Requirements: "OS X 10.8 or later, Intel 64-bit processor",
+ fileRE: regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
+ },
+ {
+ Platform: "Linux",
+ Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
+ fileRE: regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
+ },
+ {
+ Platform: "Source",
+ fileRE: regexp.MustCompile(`\.src\.tar\.gz$`),
+ },
+}
+
+// data to send to the template; increment cacheKey if you change this.
+type listTemplateData struct {
+ Featured []Feature
+ Stable, Unstable []Release
+ LoginURL string
+}
+
+var (
+ listTemplate = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
+ templateFuncs = template.FuncMap{"pretty": pretty}
+)
+
+func listHandler(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "GET" {
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ var (
+ c = appengine.NewContext(r)
+ d listTemplateData
+ )
+ if _, err := memcache.Gob.Get(c, cacheKey, &d); err != nil {
+ if err == memcache.ErrCacheMiss {
+ log.Debugf(c, "cache miss")
+ } else {
+ log.Errorf(c, "cache get error: %v", err)
+ }
+
+ var fs []File
+ _, err := datastore.NewQuery("File").Ancestor(rootKey(c)).GetAll(c, &fs)
+ if err != nil {
+ log.Errorf(c, "error listing: %v", err)
+ return
+ }
+ d.Stable, d.Unstable = filesToReleases(fs)
+ if len(d.Stable) > 0 {
+ d.Featured = filesToFeatured(d.Stable[0].Files)
+ }
+
+ d.LoginURL, _ = user.LoginURL(c, "/dl")
+ if user.Current(c) != nil {
+ d.LoginURL, _ = user.LogoutURL(c, "/dl")
+ }
+
+ item := &memcache.Item{Key: cacheKey, Object: &d, Expiration: cacheDuration}
+ if err := memcache.Gob.Set(c, item); err != nil {
+ log.Errorf(c, "cache set error: %v", err)
+ }
+ }
+ if err := listTemplate.ExecuteTemplate(w, "root", d); err != nil {
+ log.Errorf(c, "error executing template: %v", err)
+ }
+}
+
+func filesToFeatured(fs []File) (featured []Feature) {
+ for _, feature := range featuredFiles {
+ for _, file := range fs {
+ if feature.fileRE.MatchString(file.Filename) {
+ feature.File = file
+ featured = append(featured, feature)
+ break
+ }
+ }
+ }
+ return
+}
+
+func filesToReleases(fs []File) (stable, unstable []Release) {
+ sort.Sort(fileOrder(fs))
+
+ var r *Release
+ var stableMaj, stableMin int
+ add := func() {
+ if r == nil {
+ return
+ }
+ if r.Stable {
+ if len(stable) == 0 {
+ // Display files for latest stable release.
+ stableMaj, stableMin, _ = parseVersion(r.Version)
+ r.Visible = len(stable) == 0
+ }
+ stable = append(stable, *r)
+ return
+ }
+ if len(unstable) != 0 {
+ // Only show one (latest) unstable version.
+ return
+ }
+ maj, min, _ := parseVersion(r.Version)
+ if maj < stableMaj || maj == stableMaj && min <= stableMin {
+ // Display unstable version only if newer than the
+ // latest stable release.
+ return
+ }
+ r.Visible = true
+ unstable = append(unstable, *r)
+ }
+ for _, f := range fs {
+ if r == nil || f.Version != r.Version {
+ add()
+ r = &Release{
+ Version: f.Version,
+ Stable: isStable(f.Version),
+ }
+ }
+ r.Files = append(r.Files, f)
+ }
+ add()
+ return
+}
+
+// isStable reports whether the version string v is a stable version.
+func isStable(v string) bool {
+ return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
+}
+
+type fileOrder []File
+
+func (s fileOrder) Len() int { return len(s) }
+func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s fileOrder) Less(i, j int) bool {
+ a, b := s[i], s[j]
+ if av, bv := a.Version, b.Version; av != bv {
+ return versionLess(av, bv)
+ }
+ if a.OS != b.OS {
+ return a.OS < b.OS
+ }
+ if a.Arch != b.Arch {
+ return a.Arch < b.Arch
+ }
+ if a.Kind != b.Kind {
+ return a.Kind < b.Kind
+ }
+ return a.Filename < b.Filename
+}
+
+func versionLess(a, b string) bool {
+ // Put stable releases first.
+ if isStable(a) != isStable(b) {
+ return isStable(a)
+ }
+ maja, mina, ta := parseVersion(a)
+ majb, minb, tb := parseVersion(b)
+ if maja == majb {
+ if mina == minb {
+ return ta >= tb
+ }
+ return mina >= minb
+ }
+ return maja >= majb
+}
+
+func parseVersion(v string) (maj, min int, tail string) {
+ if i := strings.Index(v, "beta"); i > 0 {
+ tail = v[i:]
+ v = v[:i]
+ }
+ if i := strings.Index(v, "rc"); i > 0 {
+ tail = v[i:]
+ v = v[:i]
+ }
+ p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
+ maj, _ = strconv.Atoi(p[0])
+ if len(p) < 2 {
+ return
+ }
+ min, _ = strconv.Atoi(p[1])
+ return
+}
+
+func uploadHandler(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ c := appengine.NewContext(r)
+
+ // Authenticate using a user token (same as gomote).
+ user := r.FormValue("user")
+ if !validUser(user) {
+ http.Error(w, "bad user", http.StatusForbidden)
+ return
+ }
+ if r.FormValue("key") != userKey(c, user) {
+ http.Error(w, "bad key", http.StatusForbidden)
+ return
+ }
+
+ var f File
+ defer r.Body.Close()
+ if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
+ log.Errorf(c, "error decoding upload JSON: %v", err)
+ http.Error(w, "Something broke", http.StatusInternalServerError)
+ return
+ }
+ if f.Filename == "" {
+ http.Error(w, "Must provide Filename", http.StatusBadRequest)
+ return
+ }
+ if f.Uploaded.IsZero() {
+ f.Uploaded = time.Now()
+ }
+ k := datastore.NewKey(c, "File", f.Filename, 0, rootKey(c))
+ if _, err := datastore.Put(c, k, &f); err != nil {
+ log.Errorf(c, "putting File entity: %v", err)
+ http.Error(w, "could not put File entity", http.StatusInternalServerError)
+ return
+ }
+ if err := memcache.Delete(c, cacheKey); err != nil {
+ log.Errorf(c, "cache delete error: %v", err)
+ }
+ io.WriteString(w, "OK")
+}
+
+func getHandler(w http.ResponseWriter, r *http.Request) {
+ name := strings.TrimPrefix(r.URL.Path, "/dl/")
+ if name == "" {
+ listHandler(w, r)
+ return
+ }
+ if !fileRe.MatchString(name) {
+ http.NotFound(w, r)
+ return
+ }
+ http.Redirect(w, r, gcsBaseURL+name, http.StatusFound)
+}
+
+func validUser(user string) bool {
+ switch user {
+ case "adg", "bradfitz", "cbro":
+ return true
+ }
+ return false
+}
+
+func userKey(c context.Context, user string) string {
+ h := hmac.New(md5.New, []byte(secret(c)))
+ h.Write([]byte("user-" + user))
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
+var fileRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
+
+func initHandler(w http.ResponseWriter, r *http.Request) {
+ var fileRoot struct {
+ Root string
+ }
+ c := appengine.NewContext(r)
+ k := rootKey(c)
+ err := datastore.RunInTransaction(c, func(c context.Context) error {
+ err := datastore.Get(c, k, &fileRoot)
+ if err != nil && err != datastore.ErrNoSuchEntity {
+ return err
+ }
+ _, err = datastore.Put(c, k, &fileRoot)
+ return err
+ }, nil)
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
+ }
+ io.WriteString(w, "OK")
+}
+
+// rootKey is the ancestor of all File entities.
+func rootKey(c context.Context) *datastore.Key {
+ return datastore.NewKey(c, "FileRoot", "root", 0, nil)
+}
+
+// pretty returns a human-readable version of the given OS, Arch, or Kind.
+func pretty(s string) string {
+ t, ok := prettyStrings[s]
+ if !ok {
+ return s
+ }
+ return t
+}
+
+var prettyStrings = map[string]string{
+ "darwin": "OS X",
+ "freebsd": "FreeBSD",
+ "linux": "Linux",
+ "windows": "Windows",
+
+ "386": "32-bit",
+ "amd64": "64-bit",
+
+ "armv6l": "ARMv6",
+
+ "archive": "Archive",
+ "installer": "Installer",
+ "source": "Source",
+}
+
+// Code below copied from x/build/app/key
+
+var theKey struct {
+ sync.RWMutex
+ builderKey
+}
+
+type builderKey struct {
+ Secret string
+}
+
+func (k *builderKey) Key(c context.Context) *datastore.Key {
+ return datastore.NewKey(c, "BuilderKey", "root", 0, nil)
+}
+
+func secret(c context.Context) string {
+ // check with rlock
+ theKey.RLock()
+ k := theKey.Secret
+ theKey.RUnlock()
+ if k != "" {
+ return k
+ }
+
+ // prepare to fill; check with lock and keep lock
+ theKey.Lock()
+ defer theKey.Unlock()
+ if theKey.Secret != "" {
+ return theKey.Secret
+ }
+
+ // fill
+ if err := datastore.Get(c, theKey.Key(c), &theKey.builderKey); err != nil {
+ if err == datastore.ErrNoSuchEntity {
+ // If the key is not stored in datastore, write it.
+ // This only happens at the beginning of a new deployment.
+ // The code is left here for SDK use and in case a fresh
+ // deployment is ever needed. "gophers rule" is not the
+ // real key.
+ if !appengine.IsDevAppServer() {
+ panic("lost key from datastore")
+ }
+ theKey.Secret = "gophers rule"
+ datastore.Put(c, theKey.Key(c), &theKey.builderKey)
+ return theKey.Secret
+ }
+ panic("cannot load builder key: " + err.Error())
+ }
+
+ return theKey.Secret
+}
diff --git a/go/src/golang.org/x/tools/godoc/dl/dl_test.go b/go/src/golang.org/x/tools/godoc/dl/dl_test.go
new file mode 100644
index 0000000..9d6a057
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/dl/dl_test.go
@@ -0,0 +1,72 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package dl
+
+import (
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestParseVersion(t *testing.T) {
+ for _, c := range []struct {
+ in string
+ maj, min int
+ tail string
+ }{
+ {"go1.5", 5, 0, ""},
+ {"go1.5beta1", 5, 0, "beta1"},
+ {"go1.5.1", 5, 1, ""},
+ {"go1.5.1rc1", 5, 1, "rc1"},
+ } {
+ maj, min, tail := parseVersion(c.in)
+ if maj != c.maj || min != c.min || tail != c.tail {
+ t.Errorf("parseVersion(%q) = %v, %v, %q; want %v, %v, %q",
+ c.in, maj, min, tail, c.maj, c.min, c.tail)
+ }
+ }
+}
+
+func TestFileOrder(t *testing.T) {
+ fs := []File{
+ {Filename: "go1.3.src.tar.gz", Version: "go1.3", OS: "", Arch: "", Kind: "source"},
+ {Filename: "go1.3.1.src.tar.gz", Version: "go1.3.1", OS: "", Arch: "", Kind: "source"},
+ {Filename: "go1.3.linux-amd64.tar.gz", Version: "go1.3", OS: "linux", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.3.1.linux-amd64.tar.gz", Version: "go1.3.1", OS: "linux", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.3.darwin-amd64.tar.gz", Version: "go1.3", OS: "darwin", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.3.darwin-amd64.pkg", Version: "go1.3", OS: "darwin", Arch: "amd64", Kind: "installer"},
+ {Filename: "go1.3.darwin-386.tar.gz", Version: "go1.3", OS: "darwin", Arch: "386", Kind: "archive"},
+ {Filename: "go1.3beta1.linux-amd64.tar.gz", Version: "go1.3beta1", OS: "linux", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.3beta2.linux-amd64.tar.gz", Version: "go1.3beta2", OS: "linux", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.3rc1.linux-amd64.tar.gz", Version: "go1.3rc1", OS: "linux", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.2.linux-amd64.tar.gz", Version: "go1.2", OS: "linux", Arch: "amd64", Kind: "archive"},
+ {Filename: "go1.2.2.linux-amd64.tar.gz", Version: "go1.2.2", OS: "linux", Arch: "amd64", Kind: "archive"},
+ }
+ sort.Sort(fileOrder(fs))
+ var s []string
+ for _, f := range fs {
+ s = append(s, f.Filename)
+ }
+ got := strings.Join(s, "\n")
+ want := strings.Join([]string{
+ "go1.3.1.src.tar.gz",
+ "go1.3.1.linux-amd64.tar.gz",
+ "go1.3.src.tar.gz",
+ "go1.3.darwin-386.tar.gz",
+ "go1.3.darwin-amd64.tar.gz",
+ "go1.3.darwin-amd64.pkg",
+ "go1.3.linux-amd64.tar.gz",
+ "go1.2.2.linux-amd64.tar.gz",
+ "go1.2.linux-amd64.tar.gz",
+ "go1.3rc1.linux-amd64.tar.gz",
+ "go1.3beta2.linux-amd64.tar.gz",
+ "go1.3beta1.linux-amd64.tar.gz",
+ }, "\n")
+ if got != want {
+ t.Errorf("sort order is\n%s\nwant:\n%s", got, want)
+ }
+}
diff --git a/go/src/golang.org/x/tools/godoc/dl/tmpl.go b/go/src/golang.org/x/tools/godoc/dl/tmpl.go
new file mode 100644
index 0000000..54a2d54
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/dl/tmpl.go
@@ -0,0 +1,270 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package dl
+
+// TODO(adg): refactor this to use the tools/godoc/static template.
+
+const templateHTML = `
+{{define "root"}}
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Downloads - The Go Programming Language</title>
+ <link type="text/css" rel="stylesheet" href="/lib/godoc/style.css">
+ <script type="text/javascript">window.initFuncs = [];</script>
+ <style>
+ table.codetable {
+ margin-left: 20px; margin-right: 20px;
+ border-collapse: collapse;
+ }
+ table.codetable tr {
+ background-color: #f0f0f0;
+ }
+ table.codetable tr:nth-child(2n), table.codetable tr.first {
+ background-color: white;
+ }
+ table.codetable td, table.codetable th {
+ white-space: nowrap;
+ padding: 6px 10px;
+ }
+ table.codetable tt {
+ font-size: xx-small;
+ }
+ table.codetable tr.highlight td {
+ font-weight: bold;
+ }
+ a.downloadBox {
+ display: block;
+ color: #222;
+ border: 1px solid #375EAB;
+ border-radius: 5px;
+ background: #E0EBF5;
+ width: 280px;
+ float: left;
+ margin-left: 10px;
+ margin-bottom: 10px;
+ padding: 10px;
+ }
+ a.downloadBox:hover {
+ text-decoration: none;
+ }
+ .downloadBox .platform {
+ font-size: large;
+ }
+ .downloadBox .filename {
+ color: #375EAB;
+ font-weight: bold;
+ line-height: 1.5em;
+ }
+ a.downloadBox:hover .filename {
+ text-decoration: underline;
+ }
+ .downloadBox .size {
+ font-size: small;
+ font-weight: normal;
+ }
+ .downloadBox .reqs {
+ font-size: small;
+ font-style: italic;
+ }
+ .downloadBox .checksum {
+ font-size: 5pt;
+ }
+ </style>
+</head>
+<body>
+
+<div id="topbar"><div class="container">
+
+<div class="top-heading"><a href="/">The Go Programming Language</a></div>
+<form method="GET" action="/search">
+<div id="menu">
+<a href="/doc/">Documents</a>
+<a href="/pkg/">Packages</a>
+<a href="/project/">The Project</a>
+<a href="/help/">Help</a>
+<a href="/blog/">Blog</a>
+<input type="text" id="search" name="q" class="inactive" value="Search" placeholder="Search">
+</div>
+</form>
+
+</div></div>
+
+<div id="page">
+<div class="container">
+
+<h1>Downloads</h1>
+
+<p>
+After downloading a binary release suitable for your system,
+please follow the <a href="/doc/install">installation instructions</a>.
+</p>
+
+<p>
+If you are building from source,
+follow the <a href="/doc/install/source">source installation instructions</a>.
+</p>
+
+<p>
+See the <a href="/doc/devel/release.html">release history</a> for more
+information about Go releases.
+</p>
+
+{{with .Featured}}
+<h3 id="featured">Featured downloads</h3>
+{{range .}}
+{{template "download" .}}
+{{end}}
+{{end}}
+
+<div style="clear: both;"></div>
+
+{{with .Stable}}
+<h3 id="stable">Stable versions</h3>
+{{template "releases" .}}
+{{end}}
+
+{{with .Unstable}}
+<h3 id="unstable">Unstable version</h3>
+{{template "releases" .}}
+{{end}}
+
+<h3>Older versions</h3>
+
+<p>
+Older releases of Go are available at <a href="https://code.google.com/p/go/downloads/list?can=1">Google Code</a>.
+</p>
+
+
+<!-- Disabled for now; there's no admin functionality yet.
+<p>
+<small><a href="{{.LoginURL}}">π</a></small>
+</p>
+-->
+
+<div id="footer">
+ <p>
+ Except as
+ <a href="https://developers.google.com/site-policies#restrictions">noted</a>,
+ the content of this page is licensed under the Creative Commons
+ Attribution 3.0 License,<br>
+ and code is licensed under a <a href="http://golang.org/LICENSE">BSD license</a>.<br>
+ <a href="http://golang.org/doc/tos.html">Terms of Service</a> |
+ <a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
+ </p>
+</div><!-- #footer -->
+
+</div><!-- .container -->
+</div><!-- #page -->
+<script>
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-11222381-2', 'auto');
+ ga('send', 'pageview');
+
+</script>
+</body>
+<script src="/lib/godoc/jquery.js"></script>
+<script src="/lib/godoc/godocs.js"></script>
+<script>
+$(document).ready(function() {
+ $('a.download').click(function(e) {
+ // Try using the link text as the file name,
+ // unless there's a child element of class 'filename'.
+ var filename = $(this).text();
+ var child = $(this).find('.filename');
+ if (child.length > 0) {
+ filename = child.text();
+ }
+
+ // This must be kept in sync with the filenameRE in godocs.js.
+ var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
+ var m = filenameRE.exec(filename);
+ if (!m) {
+ // Don't redirect to the download page if it won't recognize this file.
+ // (Should not happen.)
+ return;
+ }
+
+ var dest = "/doc/install";
+ if (filename.indexOf(".src.") != -1) {
+ dest += "/source";
+ }
+ dest += "?download=" + filename;
+
+ e.preventDefault();
+ e.stopPropagation();
+ window.location = dest;
+ });
+});
+</script>
+</html>
+{{end}}
+
+{{define "releases"}}
+{{range .}}
+<div class="toggle{{if .Visible}}Visible{{end}}" id="{{.Version}}">
+ <div class="collapsed">
+ <h2 class="toggleButton" title="Click to show downloads for this version">{{.Version}} â–¹</h2>
+ </div>
+ <div class="expanded">
+ <h2 class="toggleButton" title="Click to hide downloads for this version">{{.Version}} â–¾</h2>
+ {{if .Stable}}{{else}}
+ <p>This is an <b>unstable</b> version of Go. Use with caution.</p>
+ {{end}}
+ {{template "files" .Files}}
+ </div>
+</div>
+{{end}}
+{{end}}
+
+{{define "files"}}
+<table class="codetable">
+<thead>
+<tr class="first">
+ <th>File name</th>
+ <th>Kind</th>
+ <th>OS</th>
+ <th>Arch</th>
+ <th>Size</th>
+ {{/* Use the checksum type of the first file for the column heading. */}}
+ <th>{{(index . 0).ChecksumType}} Checksum</th>
+</tr>
+</thead>
+{{range .}}
+<tr{{if .Highlight}} class="highlight"{{end}}>
+ <td class="filename"><a class="download" href="{{.URL}}">{{.Filename}}</a></td>
+ <td>{{pretty .Kind}}</td>
+ <td>{{.PrettyOS}}</td>
+ <td>{{pretty .Arch}}</td>
+ <td>{{.PrettySize}}</td>
+ <td><tt>{{.PrettyChecksum}}</tt></td>
+</tr>
+{{else}}
+<tr>
+ <td colspan="5">No downloads available.</td>
+</tr>
+{{end}}
+</table>
+{{end}}
+
+{{define "download"}}
+<a class="download downloadBox" href="{{.URL}}">
+<div class="platform">{{.Platform}}</div>
+{{with .Requirements}}<div class="reqs">{{.}}</div>{{end}}
+<div>
+ <span class="filename">{{.Filename}}</span>
+ {{if .Size}}<span class="size">({{.PrettySize}})</span>{{end}}
+</div>
+<div class="checksum">{{.ChecksumType}}: {{.PrettyChecksum}}</div>
+</a>
+{{end}}
+`
diff --git a/go/src/golang.org/x/tools/godoc/godoc.go b/go/src/golang.org/x/tools/godoc/godoc.go
index 6b176c6..dda1f49 100644
--- a/go/src/golang.org/x/tools/godoc/godoc.go
+++ b/go/src/golang.org/x/tools/godoc/godoc.go
@@ -281,6 +281,7 @@
type PageInfo struct {
Dirname string // directory containing the package
Err error // error or nil
+ Share bool // show share button on examples
// package info
FSet *token.FileSet // nil if no package documentation
@@ -433,7 +434,7 @@
buf.WriteString(indent)
buf.WriteString("Example:\n\t")
buf.WriteString(code)
- buf.WriteString("\n")
+ buf.WriteString("\n\n")
}
return buf.String()
}
@@ -490,7 +491,8 @@
err := p.ExampleHTML.Execute(&buf, struct {
Name, Doc, Code, Play, Output string
- }{eg.Name, eg.Doc, code, play, out})
+ Share bool
+ }{eg.Name, eg.Doc, code, play, out, info.Share})
if err != nil {
log.Print(err)
}
diff --git a/go/src/golang.org/x/tools/godoc/page.go b/go/src/golang.org/x/tools/godoc/page.go
index b296b27..79b1e19 100644
--- a/go/src/golang.org/x/tools/godoc/page.go
+++ b/go/src/golang.org/x/tools/godoc/page.go
@@ -16,6 +16,7 @@
Subtitle string
Query string
Body []byte
+ Share bool
// filled in by servePage
SearchBox bool
@@ -39,5 +40,19 @@
Title: "File " + relpath,
Subtitle: relpath,
Body: applyTemplate(p.ErrorHTML, "errorHTML", err), // err may contain an absolute path!
+ Share: allowShare(r),
})
}
+
+var onAppengine = false // overriden in appengine.go when on app engine
+
+func allowShare(r *http.Request) bool {
+ if !onAppengine {
+ return true
+ }
+ switch r.Header.Get("X-AppEngine-Country") {
+ case "", "ZZ", "CN":
+ return false
+ }
+ return true
+}
diff --git a/go/src/golang.org/x/tools/godoc/pres.go b/go/src/golang.org/x/tools/godoc/pres.go
index a8f8b2b..11e0dd3 100644
--- a/go/src/golang.org/x/tools/godoc/pres.go
+++ b/go/src/golang.org/x/tools/godoc/pres.go
@@ -119,14 +119,15 @@
p: p,
c: c,
pattern: "/cmd/",
- fsRoot: "/src/cmd",
+ fsRoot: "/src",
}
p.pkgHandler = handlerServer{
- p: p,
- c: c,
- pattern: "/pkg/",
- fsRoot: "/src",
- exclude: []string{"/src/cmd"},
+ p: p,
+ c: c,
+ pattern: "/pkg/",
+ stripPrefix: "pkg/",
+ fsRoot: "/src",
+ exclude: []string{"/src/cmd"},
}
p.cmdHandler.registerWithMux(p.mux)
p.pkgHandler.registerWithMux(p.mux)
diff --git a/go/src/golang.org/x/tools/godoc/proxy/proxy.go b/go/src/golang.org/x/tools/godoc/proxy/proxy.go
new file mode 100644
index 0000000..5b331ee
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/proxy/proxy.go
@@ -0,0 +1,169 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+// Package proxy proxies requests to the sandbox compiler service and the
+// playground share handler.
+// It is designed to run only on the instance of godoc that serves golang.org.
+package proxy
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "time"
+
+ "golang.org/x/net/context"
+
+ "google.golang.org/appengine"
+ "google.golang.org/appengine/log"
+ "google.golang.org/appengine/memcache"
+ "google.golang.org/appengine/urlfetch"
+)
+
+type Request struct {
+ Body string
+}
+
+type Response struct {
+ Errors string
+ Events []Event
+}
+
+type Event struct {
+ Message string
+ Kind string // "stdout" or "stderr"
+ Delay time.Duration // time to wait before printing Message
+}
+
+const (
+ // We need to use HTTP here for "reasons", but the traffic isn't
+ // sensitive and it only travels across Google's internal network
+ // so we should be OK.
+ sandboxURL = "http://sandbox.golang.org/compile"
+ playgroundURL = "http://play.golang.org"
+)
+
+const expires = 7 * 24 * time.Hour // 1 week
+var cacheControlHeader = fmt.Sprintf("public, max-age=%d", int(expires.Seconds()))
+
+func RegisterHandlers(mux *http.ServeMux) {
+ mux.HandleFunc("/compile", compile)
+ mux.HandleFunc("/share", share)
+}
+
+func compile(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ http.Error(w, "I only answer to POST requests.", http.StatusMethodNotAllowed)
+ return
+ }
+
+ c := appengine.NewContext(r)
+
+ body := r.FormValue("body")
+ res := &Response{}
+ key := cacheKey(body)
+ if _, err := memcache.Gob.Get(c, key, res); err != nil {
+ if err != memcache.ErrCacheMiss {
+ log.Errorf(c, "getting response cache: %v", err)
+ }
+
+ req := &Request{Body: body}
+ if err := makeSandboxRequest(c, req, res); err != nil {
+ log.Errorf(c, "compile error: %v", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
+
+ item := &memcache.Item{Key: key, Object: res}
+ if err := memcache.Gob.Set(c, item); err != nil {
+ log.Errorf(c, "setting response cache: %v", err)
+ }
+ }
+
+ expiresTime := time.Now().Add(expires).UTC()
+ w.Header().Set("Expires", expiresTime.Format(time.RFC1123))
+ w.Header().Set("Cache-Control", cacheControlHeader)
+
+ var out interface{}
+ switch r.FormValue("version") {
+ case "2":
+ out = res
+ default: // "1"
+ out = struct {
+ CompileErrors string `json:"compile_errors"`
+ Output string `json:"output"`
+ }{res.Errors, flatten(res.Events)}
+ }
+ if err := json.NewEncoder(w).Encode(out); err != nil {
+ log.Errorf(c, "encoding response: %v", err)
+ }
+}
+
+// makeSandboxRequest sends the given Request to the sandbox
+// and stores the response in the given Response.
+func makeSandboxRequest(c context.Context, req *Request, res *Response) error {
+ reqJ, err := json.Marshal(req)
+ if err != nil {
+ return fmt.Errorf("marshalling request: %v", err)
+ }
+ r, err := urlfetch.Client(c).Post(sandboxURL, "application/json", bytes.NewReader(reqJ))
+ if err != nil {
+ return fmt.Errorf("making request: %v", err)
+ }
+ defer r.Body.Close()
+ if r.StatusCode != http.StatusOK {
+ b, _ := ioutil.ReadAll(r.Body)
+ return fmt.Errorf("bad status: %v body:\n%s", r.Status, b)
+ }
+ err = json.NewDecoder(r.Body).Decode(res)
+ if err != nil {
+ return fmt.Errorf("unmarshalling response: %v", err)
+ }
+ return nil
+}
+
+// flatten takes a sequence of Events and returns their contents, concatenated.
+func flatten(seq []Event) string {
+ var buf bytes.Buffer
+ for _, e := range seq {
+ buf.WriteString(e.Message)
+ }
+ return buf.String()
+}
+
+func cacheKey(body string) string {
+ h := sha1.New()
+ io.WriteString(h, body)
+ return fmt.Sprintf("prog-%x", h.Sum(nil))
+}
+
+func share(w http.ResponseWriter, r *http.Request) {
+ if !allowShare(r) {
+ http.Error(w, "Forbidden", http.StatusForbidden)
+ return
+ }
+ target, _ := url.Parse(playgroundURL)
+ p := httputil.NewSingleHostReverseProxy(target)
+ p.Transport = &urlfetch.Transport{Context: appengine.NewContext(r)}
+ p.ServeHTTP(w, r)
+}
+
+func allowShare(r *http.Request) bool {
+ if appengine.IsDevAppServer() {
+ return true
+ }
+ switch r.Header.Get("X-AppEngine-Country") {
+ case "", "ZZ", "CN":
+ return false
+ }
+ return true
+}
diff --git a/go/src/golang.org/x/tools/godoc/redirect/redirect.go b/go/src/golang.org/x/tools/godoc/redirect/redirect.go
index 77d927d..b07d97f 100644
--- a/go/src/golang.org/x/tools/godoc/redirect/redirect.go
+++ b/go/src/golang.org/x/tools/godoc/redirect/redirect.go
@@ -36,6 +36,7 @@
mux.HandleFunc("/src/pkg/", srcPkgHandler)
mux.HandleFunc("/cl/", clHandler)
mux.HandleFunc("/change/", changeHandler)
+ mux.HandleFunc("/design/", designHandler)
}
func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) {
@@ -98,6 +99,7 @@
"/issues": "https://github.com/golang/go/issues",
"/issues/new": "https://github.com/golang/go/issues/new",
"/play": "http://play.golang.org",
+ "/design": "https://github.com/golang/proposal/tree/master/design",
// In Go 1.2 the references page is part of /doc/.
"/ref": "/doc/#references",
@@ -140,7 +142,11 @@
func Handler(target string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- http.Redirect(w, r, target, http.StatusMovedPermanently)
+ url := target
+ if qs := r.URL.RawQuery; qs != "" {
+ url += "?" + qs
+ }
+ http.Redirect(w, r, url, http.StatusMovedPermanently)
})
}
@@ -230,3 +236,15 @@
}
http.Redirect(w, r, target, http.StatusFound)
}
+
+func designHandler(w http.ResponseWriter, r *http.Request) {
+ const prefix = "/design/"
+ if p := r.URL.Path; p == prefix {
+ // redirect /prefix/ to /prefix
+ http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
+ return
+ }
+ name := r.URL.Path[len(prefix):]
+ target := "https://github.com/golang/proposal/blob/master/design/" + name + ".md"
+ http.Redirect(w, r, target, http.StatusFound)
+}
diff --git a/go/src/golang.org/x/tools/godoc/redirect/redirect_test.go b/go/src/golang.org/x/tools/godoc/redirect/redirect_test.go
new file mode 100644
index 0000000..5e8045a
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/redirect/redirect_test.go
@@ -0,0 +1,101 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package redirect
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+type redirectResult struct {
+ status int
+ path string
+}
+
+func errorResult(status int) redirectResult {
+ return redirectResult{status, ""}
+}
+
+func TestRedirects(t *testing.T) {
+ var tests = map[string]redirectResult{
+ "/build": {301, "http://build.golang.org"},
+ "/ref": {301, "/doc/#references"},
+ "/doc/mem": {301, "/ref/mem"},
+ "/doc/spec": {301, "/ref/spec"},
+ "/tour": {301, "http://tour.golang.org"},
+ "/foo": errorResult(404),
+
+ "/pkg/asn1": {301, "/pkg/encoding/asn1/"},
+ "/pkg/template/parse": {301, "/pkg/text/template/parse/"},
+
+ "/src/pkg/foo": {301, "/src/foo"},
+
+ "/cmd/gofix": {301, "/cmd/fix/"},
+
+ // git commits (/change)
+ // TODO: mercurial tags and LoadChangeMap.
+ "/change": {301, "https://go.googlesource.com/go"},
+ "/change/a": {302, "https://go.googlesource.com/go/+/a"},
+
+ "/issue": {301, "https://github.com/golang/go/issues"},
+ "/issue?": {301, "https://github.com/golang/go/issues"},
+ "/issue/1": {302, "https://github.com/golang/go/issues/1"},
+ "/issue/new": {301, "https://github.com/golang/go/issues/new"},
+ "/issue/new?a=b&c=d%20&e=f": {301, "https://github.com/golang/go/issues/new?a=b&c=d%20&e=f"},
+ "/issues": {301, "https://github.com/golang/go/issues"},
+ "/issues/1": {302, "https://github.com/golang/go/issues/1"},
+ "/issues/new": {301, "https://github.com/golang/go/issues/new"},
+ "/issues/1/2/3": errorResult(404),
+
+ "/design": {301, "https://github.com/golang/proposal/tree/master/design"},
+ "/design/": {302, "/design"},
+ "/design/123-foo": {302, "https://github.com/golang/proposal/blob/master/design/123-foo.md"},
+ "/design/text/123-foo": {302, "https://github.com/golang/proposal/blob/master/design/text/123-foo.md"},
+
+ "/cl/1": {302, "https://go-review.googlesource.com/r/1"},
+ "/cl/1/": {302, "https://go-review.googlesource.com/r/1"},
+ "/cl/267120043": {302, "https://codereview.appspot.com/267120043"},
+ "/cl/267120043/": {302, "https://codereview.appspot.com/267120043"},
+ }
+
+ mux := http.NewServeMux()
+ Register(mux)
+ ts := httptest.NewServer(mux)
+ defer ts.Close()
+
+ for path, want := range tests {
+ if want.path != "" && want.path[0] == '/' {
+ // All redirects are absolute.
+ want.path = ts.URL + want.path
+ }
+
+ req, err := http.NewRequest("GET", ts.URL+path, nil)
+ if err != nil {
+ t.Errorf("(path: %q) unexpected error: %v", path, err)
+ continue
+ }
+
+ resp, err := http.DefaultTransport.RoundTrip(req)
+ if err != nil {
+ t.Errorf("(path: %q) unexpected error: %v", path, err)
+ continue
+ }
+
+ if resp.StatusCode != want.status {
+ t.Errorf("(path: %q) got status %d, want %d", path, resp.StatusCode, want.status)
+ }
+
+ if want.status != 301 && want.status != 302 {
+ // Not a redirect. Just check status.
+ continue
+ }
+
+ out, _ := resp.Location()
+ if got := out.String(); got != want.path {
+ t.Errorf("(path: %q) got %s, want %s", path, got, want.path)
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/godoc/search.go b/go/src/golang.org/x/tools/godoc/search.go
index e126330..ff3f2be 100644
--- a/go/src/golang.org/x/tools/godoc/search.go
+++ b/go/src/golang.org/x/tools/godoc/search.go
@@ -126,6 +126,7 @@
Tabtitle: query,
Query: query,
Body: body.Bytes(),
+ Share: allowShare(r),
})
}
diff --git a/go/src/golang.org/x/tools/godoc/server.go b/go/src/golang.org/x/tools/godoc/server.go
index 2c18efb..6906df8 100644
--- a/go/src/golang.org/x/tools/godoc/server.go
+++ b/go/src/golang.org/x/tools/godoc/server.go
@@ -34,11 +34,12 @@
// handlerServer is a migration from an old godoc http Handler type.
// This should probably merge into something else.
type handlerServer struct {
- p *Presentation
- c *Corpus // copy of p.Corpus
- pattern string // url pattern; e.g. "/pkg/"
- fsRoot string // file system root to which the pattern is mapped; e.g. "/src"
- exclude []string // file system paths to exclude; e.g. "/src/cmd"
+ p *Presentation
+ c *Corpus // copy of p.Corpus
+ pattern string // url pattern; e.g. "/pkg/"
+ stripPrefix string // prefix to strip from import path; e.g. "pkg/"
+ fsRoot string // file system root to which the pattern is mapped; e.g. "/src"
+ exclude []string // file system paths to exclude; e.g. "/src/cmd"
}
func (s *handlerServer) registerWithMux(mux *http.ServeMux) {
@@ -214,9 +215,9 @@
if mode&NoFiltering != 0 {
return true
}
- if strings.Contains(path, "internal") {
+ if strings.Contains(path, "internal") || strings.Contains(path, "vendor") {
for _, c := range strings.Split(filepath.Clean(path), string(os.PathSeparator)) {
- if c == "internal" {
+ if c == "internal" || c == "vendor" {
return false
}
}
@@ -235,7 +236,7 @@
return
}
- relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):])
+ relpath := pathpkg.Clean(r.URL.Path[len(h.stripPrefix)+1:])
abspath := pathpkg.Join(h.fsRoot, relpath)
mode := h.p.GetPageInfoMode(r)
if relpath == builtinPkgPath {
@@ -300,11 +301,13 @@
info.TypeInfoIndex[ti.Name] = i
}
+ info.Share = allowShare(r)
h.p.ServePage(w, Page{
Title: title,
Tabtitle: tabtitle,
Subtitle: subtitle,
Body: applyTemplate(h.p.PackageHTML, "packageHTML", info),
+ Share: info.Share,
})
}
@@ -546,6 +549,7 @@
Title: title + " " + relpath,
Tabtitle: relpath,
Body: buf.Bytes(),
+ Share: allowShare(r),
})
}
@@ -606,6 +610,7 @@
Title: "Directory " + relpath,
Tabtitle: relpath,
Body: applyTemplate(p.DirlistHTML, "dirlistHTML", list),
+ Share: allowShare(r),
})
}
@@ -631,6 +636,12 @@
log.Printf("decoding metadata %s: %v", relpath, err)
}
+ page := Page{
+ Title: meta.Title,
+ Subtitle: meta.Subtitle,
+ Share: allowShare(r),
+ }
+
// evaluate as template if indicated
if meta.Template {
tmpl, err := template.New("main").Funcs(p.TemplateFuncs()).Parse(string(src))
@@ -640,7 +651,7 @@
return
}
var buf bytes.Buffer
- if err := tmpl.Execute(&buf, nil); err != nil {
+ if err := tmpl.Execute(&buf, page); err != nil {
log.Printf("executing template %s: %v", relpath, err)
p.ServeError(w, r, relpath, err)
return
@@ -655,11 +666,8 @@
src = buf.Bytes()
}
- p.ServePage(w, Page{
- Title: meta.Title,
- Subtitle: meta.Subtitle,
- Body: src,
- })
+ page.Body = src
+ p.ServePage(w, page)
}
func (p *Presentation) ServeFile(w http.ResponseWriter, r *http.Request) {
diff --git a/go/src/golang.org/x/tools/godoc/short/short.go b/go/src/golang.org/x/tools/godoc/short/short.go
new file mode 100644
index 0000000..44d3c93
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/short/short.go
@@ -0,0 +1,173 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+// Package short implements a simple URL shortener, serving an administrative
+// interface at /s and shortened urls from /s/key.
+// It is designed to run only on the instance of godoc that serves golang.org.
+package short
+
+// TODO(adg): collect statistics on URL visits
+
+import (
+ "errors"
+ "fmt"
+ "html/template"
+ "net/http"
+ "net/url"
+ "regexp"
+
+ "golang.org/x/net/context"
+
+ "google.golang.org/appengine"
+ "google.golang.org/appengine/datastore"
+ "google.golang.org/appengine/log"
+ "google.golang.org/appengine/memcache"
+ "google.golang.org/appengine/user"
+)
+
+const (
+ prefix = "/s"
+ kind = "Link"
+ baseURL = "https://golang.org" + prefix
+)
+
+// Link represents a short link.
+type Link struct {
+ Key, Target string
+}
+
+var validKey = regexp.MustCompile(`^[a-zA-Z0-9-_.]+$`)
+
+func RegisterHandlers(mux *http.ServeMux) {
+ mux.HandleFunc(prefix, adminHandler)
+ mux.HandleFunc(prefix+"/", linkHandler)
+}
+
+// linkHandler services requests to short URLs.
+// http://golang.org/s/key
+// It consults memcache and datastore for the Link for key.
+// It then sends a redirects or an error message.
+func linkHandler(w http.ResponseWriter, r *http.Request) {
+ c := appengine.NewContext(r)
+
+ key := r.URL.Path[len(prefix)+1:]
+ if !validKey.MatchString(key) {
+ http.Error(w, "not found", http.StatusNotFound)
+ return
+ }
+
+ var link Link
+ _, err := memcache.JSON.Get(c, cacheKey(key), &link)
+ if err != nil {
+ k := datastore.NewKey(c, kind, key, 0, nil)
+ err = datastore.Get(c, k, &link)
+ switch err {
+ case datastore.ErrNoSuchEntity:
+ http.Error(w, "not found", http.StatusNotFound)
+ return
+ default: // != nil
+ log.Errorf(c, "%q: %v", key, err)
+ http.Error(w, "internal server error", http.StatusInternalServerError)
+ return
+ case nil:
+ item := &memcache.Item{
+ Key: cacheKey(key),
+ Object: &link,
+ }
+ if err := memcache.JSON.Set(c, item); err != nil {
+ log.Warningf(c, "%q: %v", key, err)
+ }
+ }
+ }
+
+ http.Redirect(w, r, link.Target, http.StatusFound)
+}
+
+var adminTemplate = template.Must(template.New("admin").Parse(templateHTML))
+
+// adminHandler serves an administrative interface.
+func adminHandler(w http.ResponseWriter, r *http.Request) {
+ c := appengine.NewContext(r)
+
+ if !user.IsAdmin(c) {
+ http.Error(w, "forbidden", http.StatusForbidden)
+ return
+ }
+
+ var newLink *Link
+ var doErr error
+ if r.Method == "POST" {
+ key := r.FormValue("key")
+ switch r.FormValue("do") {
+ case "Add":
+ newLink = &Link{key, r.FormValue("target")}
+ doErr = putLink(c, newLink)
+ case "Delete":
+ k := datastore.NewKey(c, kind, key, 0, nil)
+ doErr = datastore.Delete(c, k)
+ default:
+ http.Error(w, "unknown action", http.StatusBadRequest)
+ }
+ err := memcache.Delete(c, cacheKey(key))
+ if err != nil && err != memcache.ErrCacheMiss {
+ log.Warningf(c, "%q: %v", key, err)
+ }
+ }
+
+ var links []*Link
+ _, err := datastore.NewQuery(kind).Order("Key").GetAll(c, &links)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ log.Errorf(c, "%v", err)
+ return
+ }
+
+ // Put the new link in the list if it's not there already.
+ // (Eventual consistency means that it might not show up
+ // immediately, which might be confusing for the user.)
+ if newLink != nil && doErr == nil {
+ found := false
+ for i := range links {
+ if links[i].Key == newLink.Key {
+ found = true
+ break
+ }
+ }
+ if !found {
+ links = append([]*Link{newLink}, links...)
+ }
+ newLink = nil
+ }
+
+ var data = struct {
+ BaseURL string
+ Prefix string
+ Links []*Link
+ New *Link
+ Error error
+ }{baseURL, prefix, links, newLink, doErr}
+ if err := adminTemplate.Execute(w, &data); err != nil {
+ log.Criticalf(c, "adminTemplate: %v", err)
+ }
+}
+
+// putLink validates the provided link and puts it into the datastore.
+func putLink(c context.Context, link *Link) error {
+ if !validKey.MatchString(link.Key) {
+ return errors.New("invalid key; must match " + validKey.String())
+ }
+ if _, err := url.Parse(link.Target); err != nil {
+ return fmt.Errorf("bad target: %v", err)
+ }
+ k := datastore.NewKey(c, kind, link.Key, 0, nil)
+ _, err := datastore.Put(c, k, link)
+ return err
+}
+
+// cacheKey returns a short URL key as a memcache key.
+func cacheKey(key string) string {
+ return "link-" + key
+}
diff --git a/go/src/golang.org/x/tools/godoc/short/tmpl.go b/go/src/golang.org/x/tools/godoc/short/tmpl.go
new file mode 100644
index 0000000..95e4c2a
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/short/tmpl.go
@@ -0,0 +1,121 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package short
+
+const templateHTML = `
+<!doctype HTML>
+<html>
+<head>
+<title>golang.org URL shortener</title>
+<style>
+body {
+ background: white;
+}
+input {
+ border: 1px solid #ccc;
+}
+input[type=text] {
+ width: 400px;
+}
+input, td, th {
+ color: #333;
+ font-family: Georgia, Times New Roman, serif;
+}
+input, td {
+ font-size: 14pt;
+}
+th {
+ font-size: 16pt;
+ text-align: left;
+ padding-top: 10px;
+}
+.autoselect {
+ border: none;
+}
+.error {
+ color: #900;
+}
+table {
+ margin-left: auto;
+ margin-right: auto;
+}
+</style>
+</head>
+<body>
+
+<table>
+
+{{with .Error}}
+<tr>
+ <th colspan="3">Error</th>
+</tr>
+<tr>
+ <td class="error" colspan="3">{{.}}</td>
+</tr>
+{{end}}
+
+<tr>
+ <th>Key</th>
+ <th>Target</th>
+ <th></th>
+</tr>
+
+<form method="POST" action="{{.Prefix}}">
+<tr>
+ <td><input type="text" name="key"{{with .New}} value="{{.Key}}"{{end}}></td>
+ <td><input type="text" name="target"{{with .New}} value="{{.Target}}"{{end}}></td>
+ <td><input type="submit" name="do" value="Add">
+</tr>
+</form>
+
+{{with .Links}}
+<tr>
+ <th>Short Link</th>
+ <th> </th>
+ <th> </th>
+</tr>
+{{range .}}
+<tr>
+ <td><input class="autoselect" type="text" orig="{{$.BaseURL}}/{{.Key}}" value="{{$.BaseURL}}/{{.Key}}"></td>
+ <td><input class="autoselect" type="text" orig="{{.Target}}" value="{{.Target}}"></td>
+ <td>
+ <form method="POST" action="{{$.Prefix}}">
+ <input type="hidden" name="key" value="{{.Key}}">
+ <input type="submit" name="do" value="Delete" class="delete">
+ </form>
+ </td>
+</tr>
+{{end}}
+{{end}}
+
+</table>
+
+</body>
+<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
+<script type="text/javascript">window.jQuery || document.write(unescape("%3Cscript src='/doc/jquery.js' type='text/javascript'%3E%3C/script%3E"));</script>
+<script>
+$(document).ready(function() {
+ $('.autoselect').each(function() {
+ $(this).click(function() {
+ $(this).select();
+ });
+ $(this).change(function() {
+ $(this).val($(this).attr('orig'));
+ });
+ });
+ $('.delete').click(function(e) {
+ var link = $(this).closest('tr').find('input').first().val();
+ var ok = confirm('Delete this link?\n' + link);
+ if (!ok) {
+ e.preventDefault();
+ return false;
+ }
+ });
+});
+</script>
+</html>
+`
diff --git a/go/src/golang.org/x/tools/godoc/static/example.html b/go/src/golang.org/x/tools/godoc/static/example.html
index cda2a84..4f4e09e 100644
--- a/go/src/golang.org/x/tools/godoc/static/example.html
+++ b/go/src/golang.org/x/tools/godoc/static/example.html
@@ -13,7 +13,9 @@
<div class="buttons">
<a class="run" title="Run this code [shift-enter]">Run</a>
<a class="fmt" title="Format this code">Format</a>
+ {{if $.Share}}
<a class="share" title="Share this code">Share</a>
+ {{end}}
</div>
</div>
{{else}}
diff --git a/go/src/golang.org/x/tools/godoc/static/godoc.html b/go/src/golang.org/x/tools/godoc/static/godoc.html
index 6d6d1b6..4605057 100644
--- a/go/src/golang.org/x/tools/godoc/static/godoc.html
+++ b/go/src/golang.org/x/tools/godoc/static/godoc.html
@@ -55,7 +55,9 @@
<div class="buttons">
<a class="run" title="Run this code [shift-enter]">Run</a>
<a class="fmt" title="Format this code">Format</a>
+ {{if $.Share}}
<a class="share" title="Share this code">Share</a>
+ {{end}}
</div>
</div>
{{end}}
diff --git a/go/src/golang.org/x/tools/godoc/static/godocs.js b/go/src/golang.org/x/tools/godoc/static/godocs.js
index 47d1de9..ec9f37a 100644
--- a/go/src/golang.org/x/tools/godoc/static/godocs.js
+++ b/go/src/golang.org/x/tools/godoc/static/godocs.js
@@ -248,7 +248,7 @@
function personalizeInstallInstructions() {
var prefix = '?download=';
var s = window.location.search;
- if (!s.startsWith(prefix)) {
+ if (s.indexOf(prefix) != 0) {
// No 'download' query string; bail.
return;
}
@@ -273,6 +273,8 @@
}
if (os != 'windows') {
$('#windowsInstructions').hide();
+ $('.testUnix').show();
+ $('.testWindows').hide();
} else {
if (ext != 'msi') {
$('#windowsInstallerInstructions').hide();
@@ -280,6 +282,8 @@
if (ext != 'zip') {
$('#windowsZipInstructions').hide();
}
+ $('.testUnix').hide();
+ $('.testWindows').show();
}
var download = "https://storage.googleapis.com/golang/" + filename;
diff --git a/go/src/golang.org/x/tools/godoc/static/makestatic.go b/go/src/golang.org/x/tools/godoc/static/makestatic.go
index f5e3272..f48313c 100644
--- a/go/src/golang.org/x/tools/godoc/static/makestatic.go
+++ b/go/src/golang.org/x/tools/godoc/static/makestatic.go
@@ -84,7 +84,7 @@
}
defer f.Close()
w := bufio.NewWriter(f)
- fmt.Fprintf(w, "%v\n\npackage static\n\n", warning)
+ fmt.Fprintf(w, "%v\npackage static\n\n", warning)
fmt.Fprintf(w, "var Files = map[string]string{\n")
for _, fn := range files {
b, err := ioutil.ReadFile(fn)
@@ -118,4 +118,4 @@
return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1)
}
-const warning = "// DO NOT EDIT ** This file was generated by \"go generate\" ** DO NOT EDIT //"
+const warning = "// Code generated by \"makestatic\"; DO NOT EDIT\n"
diff --git a/go/src/golang.org/x/tools/godoc/static/static.go b/go/src/golang.org/x/tools/godoc/static/static.go
index 3009e24..3517441 100644
--- a/go/src/golang.org/x/tools/godoc/static/static.go
+++ b/go/src/golang.org/x/tools/godoc/static/static.go
@@ -1,4 +1,4 @@
-// DO NOT EDIT ** This file was generated by "go generate" ** DO NOT EDIT //
+// Code generated by "makestatic"; DO NOT EDIT
package static
@@ -441,7 +441,9 @@
<div class="buttons">
<a class="run" title="Run this code [shift-enter]">Run</a>
<a class="fmt" title="Format this code">Format</a>
+ {{if $.Share}}
<a class="share" title="Share this code">Share</a>
+ {{end}}
</div>
</div>
{{else}}
@@ -513,7 +515,9 @@
<div class="buttons">
<a class="run" title="Run this code [shift-enter]">Run</a>
<a class="fmt" title="Format this code">Format</a>
+ {{if $.Share}}
<a class="share" title="Share this code">Share</a>
+ {{end}}
</div>
</div>
{{end}}
@@ -813,7 +817,7 @@
function personalizeInstallInstructions() {
var prefix = '?download=';
var s = window.location.search;
- if (!s.startsWith(prefix)) {
+ if (s.indexOf(prefix) != 0) {
// No 'download' query string; bail.
return;
}
@@ -838,6 +842,8 @@
}
if (os != 'windows') {
$('#windowsInstructions').hide();
+ $('.testUnix').show();
+ $('.testWindows').hide();
} else {
if (ext != 'msi') {
$('#windowsInstallerInstructions').hide();
@@ -845,6 +851,8 @@
if (ext != 'zip') {
$('#windowsZipInstructions').hide();
}
+ $('.testUnix').hide();
+ $('.testWindows').show();
}
var download = "https://storage.googleapis.com/golang/" + filename;
@@ -2808,7 +2816,7 @@
.exampleHeading .text:hover {
text-decoration: underline;
}
-p {
+p, li {
max-width: 800px;
word-wrap: break-word;
}
@@ -2870,10 +2878,7 @@
margin: 20px;
}
dd {
- margin: 0;
-}
-dd.indent {
- margin: 0 20px;
+ margin: 0 0 0 20px;
}
dl,
dd {
diff --git a/go/src/golang.org/x/tools/godoc/static/style.css b/go/src/golang.org/x/tools/godoc/static/style.css
index 4d2b07a..8ea0852 100644
--- a/go/src/golang.org/x/tools/godoc/static/style.css
+++ b/go/src/golang.org/x/tools/godoc/static/style.css
@@ -42,7 +42,7 @@
.exampleHeading .text:hover {
text-decoration: underline;
}
-p {
+p, li {
max-width: 800px;
word-wrap: break-word;
}
@@ -104,10 +104,7 @@
margin: 20px;
}
dd {
- margin: 0;
-}
-dd.indent {
- margin: 0 20px;
+ margin: 0 0 0 20px;
}
dl,
dd {
diff --git a/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs.go b/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs.go
index 87eaf8d..ca69d8c 100644
--- a/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs.go
+++ b/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs.go
@@ -86,21 +86,35 @@
return fs.ReadCloser.Close()
}
-func zipPath(name string) string {
+func zipPath(name string) (string, error) {
name = path.Clean(name)
if !path.IsAbs(name) {
- panic(fmt.Sprintf("stat: not an absolute path: %s", name))
+ return "", fmt.Errorf("stat: not an absolute path: %s", name)
}
- return name[1:] // strip leading '/'
+ return name[1:], nil // strip leading '/'
+}
+
+func isRoot(abspath string) bool {
+ return path.Clean(abspath) == "/"
}
func (fs *zipFS) stat(abspath string) (int, zipFI, error) {
- i, exact := fs.list.lookup(abspath)
- if i < 0 {
- // abspath has leading '/' stripped - print it explicitly
- return -1, zipFI{}, fmt.Errorf("file not found: /%s", abspath)
+ if isRoot(abspath) {
+ return 0, zipFI{
+ name: "",
+ file: nil,
+ }, nil
}
- _, name := path.Split(abspath)
+ zippath, err := zipPath(abspath)
+ if err != nil {
+ return 0, zipFI{}, err
+ }
+ i, exact := fs.list.lookup(zippath)
+ if i < 0 {
+ // zippath has leading '/' stripped - print it explicitly
+ return -1, zipFI{}, fmt.Errorf("file not found: /%s", zippath)
+ }
+ _, name := path.Split(zippath)
var file *zip.File
if exact {
file = fs.list[i] // exact match found - must be a file
@@ -109,7 +123,7 @@
}
func (fs *zipFS) Open(abspath string) (vfs.ReadSeekCloser, error) {
- _, fi, err := fs.stat(zipPath(abspath))
+ _, fi, err := fs.stat(abspath)
if err != nil {
return nil, err
}
@@ -142,18 +156,17 @@
}
func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) {
- _, fi, err := fs.stat(zipPath(abspath))
+ _, fi, err := fs.stat(abspath)
return fi, err
}
func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) {
- _, fi, err := fs.stat(zipPath(abspath))
+ _, fi, err := fs.stat(abspath)
return fi, err
}
func (fs *zipFS) ReadDir(abspath string) ([]os.FileInfo, error) {
- path := zipPath(abspath)
- i, fi, err := fs.stat(path)
+ i, fi, err := fs.stat(abspath)
if err != nil {
return nil, err
}
@@ -162,7 +175,21 @@
}
var list []os.FileInfo
- dirname := path + "/"
+
+ // make dirname the prefix that file names must start with to be considered
+ // in this directory. we must special case the root directory because, per
+ // the spec of this package, zip file entries MUST NOT start with /, so we
+ // should not append /, as we would in every other case.
+ var dirname string
+ if isRoot(abspath) {
+ dirname = ""
+ } else {
+ zippath, err := zipPath(abspath)
+ if err != nil {
+ return nil, err
+ }
+ dirname = zippath + "/"
+ }
prevname := ""
for _, e := range fs.list[i:] {
if !strings.HasPrefix(e.Name, dirname) {
diff --git a/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs_test.go b/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs_test.go
new file mode 100644
index 0000000..57efb49
--- /dev/null
+++ b/go/src/golang.org/x/tools/godoc/vfs/zipfs/zipfs_test.go
@@ -0,0 +1,179 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.package zipfs
+package zipfs
+
+import (
+ "archive/zip"
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "reflect"
+ "testing"
+
+ "golang.org/x/tools/godoc/vfs"
+)
+
+var (
+
+ // files to use to build zip used by zipfs in testing; maps path : contents
+ files = map[string]string{"foo": "foo", "bar/baz": "baz", "a/b/c": "c"}
+
+ // expected info for each entry in a file system described by files
+ tests = []struct {
+ Path string
+ IsDir bool
+ IsRegular bool
+ Name string
+ Contents string
+ Files map[string]bool
+ }{
+ {"/", true, false, "", "", map[string]bool{"foo": true, "bar": true, "a": true}},
+ {"//", true, false, "", "", map[string]bool{"foo": true, "bar": true, "a": true}},
+ {"/foo", false, true, "foo", "foo", nil},
+ {"/foo/", false, true, "foo", "foo", nil},
+ {"/foo//", false, true, "foo", "foo", nil},
+ {"/bar", true, false, "bar", "", map[string]bool{"baz": true}},
+ {"/bar/", true, false, "bar", "", map[string]bool{"baz": true}},
+ {"/bar/baz", false, true, "baz", "baz", nil},
+ {"//bar//baz", false, true, "baz", "baz", nil},
+ {"/a/b", true, false, "b", "", map[string]bool{"c": true}},
+ }
+
+ // to be initialized in setup()
+ fs vfs.FileSystem
+ statFuncs []statFunc
+)
+
+type statFunc struct {
+ Name string
+ Func func(string) (os.FileInfo, error)
+}
+
+func TestMain(t *testing.M) {
+ if err := setup(); err != nil {
+ fmt.Fprintf(os.Stderr, "Error setting up zipfs testing state: %v.\n", err)
+ os.Exit(1)
+ }
+ os.Exit(t.Run())
+}
+
+// setups state each of the tests uses
+func setup() error {
+ // create zipfs
+ b := new(bytes.Buffer)
+ zw := zip.NewWriter(b)
+ for file, contents := range files {
+ w, err := zw.Create(file)
+ if err != nil {
+ return err
+ }
+ _, err = io.WriteString(w, contents)
+ if err != nil {
+ return err
+ }
+ }
+ zw.Close()
+ zr, err := zip.NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
+ if err != nil {
+ return err
+ }
+ rc := &zip.ReadCloser{
+ Reader: *zr,
+ }
+ fs = New(rc, "foo")
+
+ // pull out different stat functions
+ statFuncs = []statFunc{
+ {"Stat", fs.Stat},
+ {"Lstat", fs.Lstat},
+ }
+
+ return nil
+}
+
+func TestZipFSReadDir(t *testing.T) {
+ for _, test := range tests {
+ if test.IsDir {
+ infos, err := fs.ReadDir(test.Path)
+ if err != nil {
+ t.Errorf("Failed to read directory %v\n", test.Path)
+ continue
+ }
+ got := make(map[string]bool)
+ for _, info := range infos {
+ got[info.Name()] = true
+ }
+ if want := test.Files; !reflect.DeepEqual(got, want) {
+ t.Errorf("ReadDir %v got %v\nwanted %v\n", test.Path, got, want)
+ }
+ }
+ }
+}
+
+func TestZipFSStatFuncs(t *testing.T) {
+ for _, test := range tests {
+ for _, statFunc := range statFuncs {
+
+ // test can stat
+ info, err := statFunc.Func(test.Path)
+ if err != nil {
+ t.Errorf("Unexpected error using %v for %v: %v\n", statFunc.Name, test.Path, err)
+ continue
+ }
+
+ // test info.Name()
+ if got, want := info.Name(), test.Name; got != want {
+ t.Errorf("Using %v for %v info.Name() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
+ }
+ // test info.IsDir()
+ if got, want := info.IsDir(), test.IsDir; got != want {
+ t.Errorf("Using %v for %v info.IsDir() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
+ }
+ // test info.Mode().IsDir()
+ if got, want := info.Mode().IsDir(), test.IsDir; got != want {
+ t.Errorf("Using %v for %v info.Mode().IsDir() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
+ }
+ // test info.Mode().IsRegular()
+ if got, want := info.Mode().IsRegular(), test.IsRegular; got != want {
+ t.Errorf("Using %v for %v info.Mode().IsRegular() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
+ }
+ // test info.Size()
+ if test.IsRegular {
+ if got, want := info.Size(), int64(len(test.Contents)); got != want {
+ t.Errorf("Using %v for %v inf.Size() got %v wanted %v", statFunc.Name, test.Path, got, want)
+ }
+ }
+ }
+ }
+}
+
+func TestZipFSOpenSeek(t *testing.T) {
+ for _, test := range tests {
+ if test.IsRegular {
+
+ // test Open()
+ f, err := fs.Open(test.Path)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer f.Close()
+
+ // test Seek() multiple times
+ for i := 0; i < 3; i++ {
+ all, err := ioutil.ReadAll(f)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if got, want := string(all), test.Contents; got != want {
+ t.Errorf("File contents for %v got %v wanted %v\n", test.Path, got, want)
+ }
+ f.Seek(0, 0)
+ }
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/imports/mkstdlib.go b/go/src/golang.org/x/tools/imports/mkstdlib.go
index c43d325..e2dca76 100644
--- a/go/src/golang.org/x/tools/imports/mkstdlib.go
+++ b/go/src/golang.org/x/tools/imports/mkstdlib.go
@@ -46,6 +46,9 @@
mustOpen(api("go1.txt")),
mustOpen(api("go1.1.txt")),
mustOpen(api("go1.2.txt")),
+ mustOpen(api("go1.3.txt")),
+ mustOpen(api("go1.4.txt")),
+ mustOpen(api("go1.5.txt")),
)
sc := bufio.NewScanner(f)
fullImport := map[string]string{} // "zip.NewReader" => "archive/zip"
diff --git a/go/src/golang.org/x/tools/imports/sortimports.go b/go/src/golang.org/x/tools/imports/sortimports.go
index 68b3dc4..653afc5 100644
--- a/go/src/golang.org/x/tools/imports/sortimports.go
+++ b/go/src/golang.org/x/tools/imports/sortimports.go
@@ -1,5 +1,3 @@
-// +build go1.2
-
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/go/src/golang.org/x/tools/imports/sortimports_compat.go b/go/src/golang.org/x/tools/imports/sortimports_compat.go
deleted file mode 100644
index 295f237..0000000
--- a/go/src/golang.org/x/tools/imports/sortimports_compat.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// +build !go1.2
-
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package imports
-
-import "go/ast"
-
-// Go 1.1 users don't get fancy package grouping.
-// But this is still gofmt-compliant:
-
-var sortImports = ast.SortImports
diff --git a/go/src/golang.org/x/tools/imports/zstdlib.go b/go/src/golang.org/x/tools/imports/zstdlib.go
index 6cdc033..39eb3c7 100644
--- a/go/src/golang.org/x/tools/imports/zstdlib.go
+++ b/go/src/golang.org/x/tools/imports/zstdlib.go
@@ -3,2169 +3,2551 @@
package imports
var stdlib = map[string]string{
- "adler32.Checksum": "hash/adler32",
- "adler32.New": "hash/adler32",
- "adler32.Size": "hash/adler32",
- "aes.BlockSize": "crypto/aes",
- "aes.KeySizeError": "crypto/aes",
- "aes.NewCipher": "crypto/aes",
- "ascii85.CorruptInputError": "encoding/ascii85",
- "ascii85.Decode": "encoding/ascii85",
- "ascii85.Encode": "encoding/ascii85",
- "ascii85.MaxEncodedLen": "encoding/ascii85",
- "ascii85.NewDecoder": "encoding/ascii85",
- "ascii85.NewEncoder": "encoding/ascii85",
- "asn1.BitString": "encoding/asn1",
- "asn1.Enumerated": "encoding/asn1",
- "asn1.Flag": "encoding/asn1",
- "asn1.Marshal": "encoding/asn1",
- "asn1.ObjectIdentifier": "encoding/asn1",
- "asn1.RawContent": "encoding/asn1",
- "asn1.RawValue": "encoding/asn1",
- "asn1.StructuralError": "encoding/asn1",
- "asn1.SyntaxError": "encoding/asn1",
- "asn1.Unmarshal": "encoding/asn1",
- "asn1.UnmarshalWithParams": "encoding/asn1",
- "ast.ArrayType": "go/ast",
- "ast.AssignStmt": "go/ast",
- "ast.Bad": "go/ast",
- "ast.BadDecl": "go/ast",
- "ast.BadExpr": "go/ast",
- "ast.BadStmt": "go/ast",
- "ast.BasicLit": "go/ast",
- "ast.BinaryExpr": "go/ast",
- "ast.BlockStmt": "go/ast",
- "ast.BranchStmt": "go/ast",
- "ast.CallExpr": "go/ast",
- "ast.CaseClause": "go/ast",
- "ast.ChanDir": "go/ast",
- "ast.ChanType": "go/ast",
- "ast.CommClause": "go/ast",
- "ast.Comment": "go/ast",
- "ast.CommentGroup": "go/ast",
- "ast.CommentMap": "go/ast",
- "ast.CompositeLit": "go/ast",
- "ast.Con": "go/ast",
- "ast.DeclStmt": "go/ast",
- "ast.DeferStmt": "go/ast",
- "ast.Ellipsis": "go/ast",
- "ast.EmptyStmt": "go/ast",
- "ast.ExprStmt": "go/ast",
- "ast.Field": "go/ast",
- "ast.FieldFilter": "go/ast",
- "ast.FieldList": "go/ast",
- "ast.File": "go/ast",
- "ast.FileExports": "go/ast",
- "ast.Filter": "go/ast",
- "ast.FilterDecl": "go/ast",
- "ast.FilterFile": "go/ast",
- "ast.FilterFuncDuplicates": "go/ast",
- "ast.FilterImportDuplicates": "go/ast",
- "ast.FilterPackage": "go/ast",
- "ast.FilterUnassociatedComments": "go/ast",
- "ast.ForStmt": "go/ast",
- "ast.Fprint": "go/ast",
- "ast.Fun": "go/ast",
- "ast.FuncDecl": "go/ast",
- "ast.FuncLit": "go/ast",
- "ast.FuncType": "go/ast",
- "ast.GenDecl": "go/ast",
- "ast.GoStmt": "go/ast",
- "ast.Ident": "go/ast",
- "ast.IfStmt": "go/ast",
- "ast.ImportSpec": "go/ast",
- "ast.Importer": "go/ast",
- "ast.IncDecStmt": "go/ast",
- "ast.IndexExpr": "go/ast",
- "ast.Inspect": "go/ast",
- "ast.InterfaceType": "go/ast",
- "ast.IsExported": "go/ast",
- "ast.KeyValueExpr": "go/ast",
- "ast.LabeledStmt": "go/ast",
- "ast.Lbl": "go/ast",
- "ast.MapType": "go/ast",
- "ast.MergeMode": "go/ast",
- "ast.MergePackageFiles": "go/ast",
- "ast.NewCommentMap": "go/ast",
- "ast.NewIdent": "go/ast",
- "ast.NewObj": "go/ast",
- "ast.NewPackage": "go/ast",
- "ast.NewScope": "go/ast",
- "ast.Node": "go/ast",
- "ast.NotNilFilter": "go/ast",
- "ast.ObjKind": "go/ast",
- "ast.Object": "go/ast",
- "ast.Package": "go/ast",
- "ast.PackageExports": "go/ast",
- "ast.ParenExpr": "go/ast",
- "ast.Pkg": "go/ast",
- "ast.Print": "go/ast",
- "ast.RECV": "go/ast",
- "ast.RangeStmt": "go/ast",
- "ast.ReturnStmt": "go/ast",
- "ast.SEND": "go/ast",
- "ast.Scope": "go/ast",
- "ast.SelectStmt": "go/ast",
- "ast.SelectorExpr": "go/ast",
- "ast.SendStmt": "go/ast",
- "ast.SliceExpr": "go/ast",
- "ast.SortImports": "go/ast",
- "ast.StarExpr": "go/ast",
- "ast.StructType": "go/ast",
- "ast.SwitchStmt": "go/ast",
- "ast.Typ": "go/ast",
- "ast.TypeAssertExpr": "go/ast",
- "ast.TypeSpec": "go/ast",
- "ast.TypeSwitchStmt": "go/ast",
- "ast.UnaryExpr": "go/ast",
- "ast.ValueSpec": "go/ast",
- "ast.Var": "go/ast",
- "ast.Visitor": "go/ast",
- "ast.Walk": "go/ast",
- "atomic.AddInt32": "sync/atomic",
- "atomic.AddInt64": "sync/atomic",
- "atomic.AddUint32": "sync/atomic",
- "atomic.AddUint64": "sync/atomic",
- "atomic.AddUintptr": "sync/atomic",
- "atomic.CompareAndSwapInt32": "sync/atomic",
- "atomic.CompareAndSwapInt64": "sync/atomic",
- "atomic.CompareAndSwapPointer": "sync/atomic",
- "atomic.CompareAndSwapUint32": "sync/atomic",
- "atomic.CompareAndSwapUint64": "sync/atomic",
- "atomic.CompareAndSwapUintptr": "sync/atomic",
- "atomic.LoadInt32": "sync/atomic",
- "atomic.LoadInt64": "sync/atomic",
- "atomic.LoadPointer": "sync/atomic",
- "atomic.LoadUint32": "sync/atomic",
- "atomic.LoadUint64": "sync/atomic",
- "atomic.LoadUintptr": "sync/atomic",
- "atomic.StoreInt32": "sync/atomic",
- "atomic.StoreInt64": "sync/atomic",
- "atomic.StorePointer": "sync/atomic",
- "atomic.StoreUint32": "sync/atomic",
- "atomic.StoreUint64": "sync/atomic",
- "atomic.StoreUintptr": "sync/atomic",
- "atomic.SwapInt32": "sync/atomic",
- "atomic.SwapInt64": "sync/atomic",
- "atomic.SwapPointer": "sync/atomic",
- "atomic.SwapUint32": "sync/atomic",
- "atomic.SwapUint64": "sync/atomic",
- "atomic.SwapUintptr": "sync/atomic",
- "base32.CorruptInputError": "encoding/base32",
- "base32.Encoding": "encoding/base32",
- "base32.HexEncoding": "encoding/base32",
- "base32.NewDecoder": "encoding/base32",
- "base32.NewEncoder": "encoding/base32",
- "base32.NewEncoding": "encoding/base32",
- "base32.StdEncoding": "encoding/base32",
- "base64.CorruptInputError": "encoding/base64",
- "base64.Encoding": "encoding/base64",
- "base64.NewDecoder": "encoding/base64",
- "base64.NewEncoder": "encoding/base64",
- "base64.NewEncoding": "encoding/base64",
- "base64.StdEncoding": "encoding/base64",
- "base64.URLEncoding": "encoding/base64",
- "big.Int": "math/big",
- "big.MaxBase": "math/big",
- "big.NewInt": "math/big",
- "big.NewRat": "math/big",
- "big.Rat": "math/big",
- "big.Word": "math/big",
- "binary.BigEndian": "encoding/binary",
- "binary.ByteOrder": "encoding/binary",
- "binary.LittleEndian": "encoding/binary",
- "binary.MaxVarintLen16": "encoding/binary",
- "binary.MaxVarintLen32": "encoding/binary",
- "binary.MaxVarintLen64": "encoding/binary",
- "binary.PutUvarint": "encoding/binary",
- "binary.PutVarint": "encoding/binary",
- "binary.Read": "encoding/binary",
- "binary.ReadUvarint": "encoding/binary",
- "binary.ReadVarint": "encoding/binary",
- "binary.Size": "encoding/binary",
- "binary.Uvarint": "encoding/binary",
- "binary.Varint": "encoding/binary",
- "binary.Write": "encoding/binary",
- "bufio.ErrAdvanceTooFar": "bufio",
- "bufio.ErrBufferFull": "bufio",
- "bufio.ErrInvalidUnreadByte": "bufio",
- "bufio.ErrInvalidUnreadRune": "bufio",
- "bufio.ErrNegativeAdvance": "bufio",
- "bufio.ErrNegativeCount": "bufio",
- "bufio.ErrTooLong": "bufio",
- "bufio.MaxScanTokenSize": "bufio",
- "bufio.NewReadWriter": "bufio",
- "bufio.NewReader": "bufio",
- "bufio.NewReaderSize": "bufio",
- "bufio.NewScanner": "bufio",
- "bufio.NewWriter": "bufio",
- "bufio.NewWriterSize": "bufio",
- "bufio.ReadWriter": "bufio",
- "bufio.Reader": "bufio",
- "bufio.ScanBytes": "bufio",
- "bufio.ScanLines": "bufio",
- "bufio.ScanRunes": "bufio",
- "bufio.ScanWords": "bufio",
- "bufio.Scanner": "bufio",
- "bufio.SplitFunc": "bufio",
- "bufio.Writer": "bufio",
- "build.AllowBinary": "go/build",
- "build.ArchChar": "go/build",
- "build.Context": "go/build",
- "build.Default": "go/build",
- "build.FindOnly": "go/build",
- "build.Import": "go/build",
- "build.ImportDir": "go/build",
- "build.ImportMode": "go/build",
- "build.IsLocalImport": "go/build",
- "build.NoGoError": "go/build",
- "build.Package": "go/build",
- "build.ToolDir": "go/build",
- "bytes.Buffer": "bytes",
- "bytes.Compare": "bytes",
- "bytes.Contains": "bytes",
- "bytes.Count": "bytes",
- "bytes.Equal": "bytes",
- "bytes.EqualFold": "bytes",
- "bytes.ErrTooLarge": "bytes",
- "bytes.Fields": "bytes",
- "bytes.FieldsFunc": "bytes",
- "bytes.HasPrefix": "bytes",
- "bytes.HasSuffix": "bytes",
- "bytes.Index": "bytes",
- "bytes.IndexAny": "bytes",
- "bytes.IndexByte": "bytes",
- "bytes.IndexFunc": "bytes",
- "bytes.IndexRune": "bytes",
- "bytes.Join": "bytes",
- "bytes.LastIndex": "bytes",
- "bytes.LastIndexAny": "bytes",
- "bytes.LastIndexFunc": "bytes",
- "bytes.Map": "bytes",
- "bytes.MinRead": "bytes",
- "bytes.NewBuffer": "bytes",
- "bytes.NewBufferString": "bytes",
- "bytes.NewReader": "bytes",
- "bytes.Reader": "bytes",
- "bytes.Repeat": "bytes",
- "bytes.Replace": "bytes",
- "bytes.Runes": "bytes",
- "bytes.Split": "bytes",
- "bytes.SplitAfter": "bytes",
- "bytes.SplitAfterN": "bytes",
- "bytes.SplitN": "bytes",
- "bytes.Title": "bytes",
- "bytes.ToLower": "bytes",
- "bytes.ToLowerSpecial": "bytes",
- "bytes.ToTitle": "bytes",
- "bytes.ToTitleSpecial": "bytes",
- "bytes.ToUpper": "bytes",
- "bytes.ToUpperSpecial": "bytes",
- "bytes.Trim": "bytes",
- "bytes.TrimFunc": "bytes",
- "bytes.TrimLeft": "bytes",
- "bytes.TrimLeftFunc": "bytes",
- "bytes.TrimPrefix": "bytes",
- "bytes.TrimRight": "bytes",
- "bytes.TrimRightFunc": "bytes",
- "bytes.TrimSpace": "bytes",
- "bytes.TrimSuffix": "bytes",
- "bzip2.NewReader": "compress/bzip2",
- "bzip2.StructuralError": "compress/bzip2",
- "cgi.Handler": "net/http/cgi",
- "cgi.Request": "net/http/cgi",
- "cgi.RequestFromMap": "net/http/cgi",
- "cgi.Serve": "net/http/cgi",
- "cipher.AEAD": "crypto/cipher",
- "cipher.Block": "crypto/cipher",
- "cipher.BlockMode": "crypto/cipher",
- "cipher.NewCBCDecrypter": "crypto/cipher",
- "cipher.NewCBCEncrypter": "crypto/cipher",
- "cipher.NewCFBDecrypter": "crypto/cipher",
- "cipher.NewCFBEncrypter": "crypto/cipher",
- "cipher.NewCTR": "crypto/cipher",
- "cipher.NewGCM": "crypto/cipher",
- "cipher.NewOFB": "crypto/cipher",
- "cipher.Stream": "crypto/cipher",
- "cipher.StreamReader": "crypto/cipher",
- "cipher.StreamWriter": "crypto/cipher",
- "cmplx.Abs": "math/cmplx",
- "cmplx.Acos": "math/cmplx",
- "cmplx.Acosh": "math/cmplx",
- "cmplx.Asin": "math/cmplx",
- "cmplx.Asinh": "math/cmplx",
- "cmplx.Atan": "math/cmplx",
- "cmplx.Atanh": "math/cmplx",
- "cmplx.Conj": "math/cmplx",
- "cmplx.Cos": "math/cmplx",
- "cmplx.Cosh": "math/cmplx",
- "cmplx.Cot": "math/cmplx",
- "cmplx.Exp": "math/cmplx",
- "cmplx.Inf": "math/cmplx",
- "cmplx.IsInf": "math/cmplx",
- "cmplx.IsNaN": "math/cmplx",
- "cmplx.Log": "math/cmplx",
- "cmplx.Log10": "math/cmplx",
- "cmplx.NaN": "math/cmplx",
- "cmplx.Phase": "math/cmplx",
- "cmplx.Polar": "math/cmplx",
- "cmplx.Pow": "math/cmplx",
- "cmplx.Rect": "math/cmplx",
- "cmplx.Sin": "math/cmplx",
- "cmplx.Sinh": "math/cmplx",
- "cmplx.Sqrt": "math/cmplx",
- "cmplx.Tan": "math/cmplx",
- "cmplx.Tanh": "math/cmplx",
- "color.Alpha": "image/color",
- "color.Alpha16": "image/color",
- "color.Alpha16Model": "image/color",
- "color.AlphaModel": "image/color",
- "color.Black": "image/color",
- "color.Color": "image/color",
- "color.Gray": "image/color",
- "color.Gray16": "image/color",
- "color.Gray16Model": "image/color",
- "color.GrayModel": "image/color",
- "color.Model": "image/color",
- "color.ModelFunc": "image/color",
- "color.NRGBA": "image/color",
- "color.NRGBA64": "image/color",
- "color.NRGBA64Model": "image/color",
- "color.NRGBAModel": "image/color",
- "color.Opaque": "image/color",
- "color.Palette": "image/color",
- "color.RGBA": "image/color",
- "color.RGBA64": "image/color",
- "color.RGBA64Model": "image/color",
- "color.RGBAModel": "image/color",
- "color.RGBToYCbCr": "image/color",
- "color.Transparent": "image/color",
- "color.White": "image/color",
- "color.YCbCr": "image/color",
- "color.YCbCrModel": "image/color",
- "color.YCbCrToRGB": "image/color",
- "cookiejar.Jar": "net/http/cookiejar",
- "cookiejar.New": "net/http/cookiejar",
- "cookiejar.Options": "net/http/cookiejar",
- "cookiejar.PublicSuffixList": "net/http/cookiejar",
- "crc32.Castagnoli": "hash/crc32",
- "crc32.Checksum": "hash/crc32",
- "crc32.ChecksumIEEE": "hash/crc32",
- "crc32.IEEE": "hash/crc32",
- "crc32.IEEETable": "hash/crc32",
- "crc32.Koopman": "hash/crc32",
- "crc32.MakeTable": "hash/crc32",
- "crc32.New": "hash/crc32",
- "crc32.NewIEEE": "hash/crc32",
- "crc32.Size": "hash/crc32",
- "crc32.Table": "hash/crc32",
- "crc32.Update": "hash/crc32",
- "crc64.Checksum": "hash/crc64",
- "crc64.ECMA": "hash/crc64",
- "crc64.ISO": "hash/crc64",
- "crc64.MakeTable": "hash/crc64",
- "crc64.New": "hash/crc64",
- "crc64.Size": "hash/crc64",
- "crc64.Table": "hash/crc64",
- "crc64.Update": "hash/crc64",
- "crypto.Hash": "crypto",
- "crypto.MD4": "crypto",
- "crypto.MD5": "crypto",
- "crypto.MD5SHA1": "crypto",
- "crypto.PrivateKey": "crypto",
- "crypto.PublicKey": "crypto",
- "crypto.RIPEMD160": "crypto",
- "crypto.RegisterHash": "crypto",
- "crypto.SHA1": "crypto",
- "crypto.SHA224": "crypto",
- "crypto.SHA256": "crypto",
- "crypto.SHA384": "crypto",
- "crypto.SHA512": "crypto",
- "csv.ErrBareQuote": "encoding/csv",
- "csv.ErrFieldCount": "encoding/csv",
- "csv.ErrQuote": "encoding/csv",
- "csv.ErrTrailingComma": "encoding/csv",
- "csv.NewReader": "encoding/csv",
- "csv.NewWriter": "encoding/csv",
- "csv.ParseError": "encoding/csv",
- "csv.Reader": "encoding/csv",
- "csv.Writer": "encoding/csv",
- "debug.FreeOSMemory": "runtime/debug",
- "debug.GCStats": "runtime/debug",
- "debug.PrintStack": "runtime/debug",
- "debug.ReadGCStats": "runtime/debug",
- "debug.SetGCPercent": "runtime/debug",
- "debug.SetMaxStack": "runtime/debug",
- "debug.SetMaxThreads": "runtime/debug",
- "debug.Stack": "runtime/debug",
- "des.BlockSize": "crypto/des",
- "des.KeySizeError": "crypto/des",
- "des.NewCipher": "crypto/des",
- "des.NewTripleDESCipher": "crypto/des",
- "doc.AllDecls": "go/doc",
- "doc.AllMethods": "go/doc",
- "doc.Example": "go/doc",
- "doc.Examples": "go/doc",
- "doc.Filter": "go/doc",
- "doc.Func": "go/doc",
- "doc.IllegalPrefixes": "go/doc",
- "doc.Mode": "go/doc",
- "doc.New": "go/doc",
- "doc.Note": "go/doc",
- "doc.Package": "go/doc",
- "doc.Synopsis": "go/doc",
- "doc.ToHTML": "go/doc",
- "doc.ToText": "go/doc",
- "doc.Type": "go/doc",
- "doc.Value": "go/doc",
- "draw.Draw": "image/draw",
- "draw.DrawMask": "image/draw",
- "draw.Drawer": "image/draw",
- "draw.FloydSteinberg": "image/draw",
- "draw.Image": "image/draw",
- "draw.Op": "image/draw",
- "draw.Over": "image/draw",
- "draw.Quantizer": "image/draw",
- "draw.Src": "image/draw",
- "driver.Bool": "database/sql/driver",
- "driver.ColumnConverter": "database/sql/driver",
- "driver.Conn": "database/sql/driver",
- "driver.DefaultParameterConverter": "database/sql/driver",
- "driver.Driver": "database/sql/driver",
- "driver.ErrBadConn": "database/sql/driver",
- "driver.ErrSkip": "database/sql/driver",
- "driver.Execer": "database/sql/driver",
- "driver.Int32": "database/sql/driver",
- "driver.IsScanValue": "database/sql/driver",
- "driver.IsValue": "database/sql/driver",
- "driver.NotNull": "database/sql/driver",
- "driver.Null": "database/sql/driver",
- "driver.Queryer": "database/sql/driver",
- "driver.Result": "database/sql/driver",
- "driver.ResultNoRows": "database/sql/driver",
- "driver.Rows": "database/sql/driver",
- "driver.RowsAffected": "database/sql/driver",
- "driver.Stmt": "database/sql/driver",
- "driver.String": "database/sql/driver",
- "driver.Tx": "database/sql/driver",
- "driver.Value": "database/sql/driver",
- "driver.ValueConverter": "database/sql/driver",
- "driver.Valuer": "database/sql/driver",
- "dsa.ErrInvalidPublicKey": "crypto/dsa",
- "dsa.GenerateKey": "crypto/dsa",
- "dsa.GenerateParameters": "crypto/dsa",
- "dsa.L1024N160": "crypto/dsa",
- "dsa.L2048N224": "crypto/dsa",
- "dsa.L2048N256": "crypto/dsa",
- "dsa.L3072N256": "crypto/dsa",
- "dsa.ParameterSizes": "crypto/dsa",
- "dsa.Parameters": "crypto/dsa",
- "dsa.PrivateKey": "crypto/dsa",
- "dsa.PublicKey": "crypto/dsa",
- "dsa.Sign": "crypto/dsa",
- "dsa.Verify": "crypto/dsa",
- "dwarf.AddrType": "debug/dwarf",
- "dwarf.ArrayType": "debug/dwarf",
- "dwarf.Attr": "debug/dwarf",
- "dwarf.AttrAbstractOrigin": "debug/dwarf",
- "dwarf.AttrAccessibility": "debug/dwarf",
- "dwarf.AttrAddrClass": "debug/dwarf",
- "dwarf.AttrAllocated": "debug/dwarf",
- "dwarf.AttrArtificial": "debug/dwarf",
- "dwarf.AttrAssociated": "debug/dwarf",
- "dwarf.AttrBaseTypes": "debug/dwarf",
- "dwarf.AttrBitOffset": "debug/dwarf",
- "dwarf.AttrBitSize": "debug/dwarf",
- "dwarf.AttrByteSize": "debug/dwarf",
- "dwarf.AttrCallColumn": "debug/dwarf",
- "dwarf.AttrCallFile": "debug/dwarf",
- "dwarf.AttrCallLine": "debug/dwarf",
- "dwarf.AttrCalling": "debug/dwarf",
- "dwarf.AttrCommonRef": "debug/dwarf",
- "dwarf.AttrCompDir": "debug/dwarf",
- "dwarf.AttrConstValue": "debug/dwarf",
- "dwarf.AttrContainingType": "debug/dwarf",
- "dwarf.AttrCount": "debug/dwarf",
- "dwarf.AttrDataLocation": "debug/dwarf",
- "dwarf.AttrDataMemberLoc": "debug/dwarf",
- "dwarf.AttrDeclColumn": "debug/dwarf",
- "dwarf.AttrDeclFile": "debug/dwarf",
- "dwarf.AttrDeclLine": "debug/dwarf",
- "dwarf.AttrDeclaration": "debug/dwarf",
- "dwarf.AttrDefaultValue": "debug/dwarf",
- "dwarf.AttrDescription": "debug/dwarf",
- "dwarf.AttrDiscr": "debug/dwarf",
- "dwarf.AttrDiscrList": "debug/dwarf",
- "dwarf.AttrDiscrValue": "debug/dwarf",
- "dwarf.AttrEncoding": "debug/dwarf",
- "dwarf.AttrEntrypc": "debug/dwarf",
- "dwarf.AttrExtension": "debug/dwarf",
- "dwarf.AttrExternal": "debug/dwarf",
- "dwarf.AttrFrameBase": "debug/dwarf",
- "dwarf.AttrFriend": "debug/dwarf",
- "dwarf.AttrHighpc": "debug/dwarf",
- "dwarf.AttrIdentifierCase": "debug/dwarf",
- "dwarf.AttrImport": "debug/dwarf",
- "dwarf.AttrInline": "debug/dwarf",
- "dwarf.AttrIsOptional": "debug/dwarf",
- "dwarf.AttrLanguage": "debug/dwarf",
- "dwarf.AttrLocation": "debug/dwarf",
- "dwarf.AttrLowerBound": "debug/dwarf",
- "dwarf.AttrLowpc": "debug/dwarf",
- "dwarf.AttrMacroInfo": "debug/dwarf",
- "dwarf.AttrName": "debug/dwarf",
- "dwarf.AttrNamelistItem": "debug/dwarf",
- "dwarf.AttrOrdering": "debug/dwarf",
- "dwarf.AttrPriority": "debug/dwarf",
- "dwarf.AttrProducer": "debug/dwarf",
- "dwarf.AttrPrototyped": "debug/dwarf",
- "dwarf.AttrRanges": "debug/dwarf",
- "dwarf.AttrReturnAddr": "debug/dwarf",
- "dwarf.AttrSegment": "debug/dwarf",
- "dwarf.AttrSibling": "debug/dwarf",
- "dwarf.AttrSpecification": "debug/dwarf",
- "dwarf.AttrStartScope": "debug/dwarf",
- "dwarf.AttrStaticLink": "debug/dwarf",
- "dwarf.AttrStmtList": "debug/dwarf",
- "dwarf.AttrStride": "debug/dwarf",
- "dwarf.AttrStrideSize": "debug/dwarf",
- "dwarf.AttrStringLength": "debug/dwarf",
- "dwarf.AttrTrampoline": "debug/dwarf",
- "dwarf.AttrType": "debug/dwarf",
- "dwarf.AttrUpperBound": "debug/dwarf",
- "dwarf.AttrUseLocation": "debug/dwarf",
- "dwarf.AttrUseUTF8": "debug/dwarf",
- "dwarf.AttrVarParam": "debug/dwarf",
- "dwarf.AttrVirtuality": "debug/dwarf",
- "dwarf.AttrVisibility": "debug/dwarf",
- "dwarf.AttrVtableElemLoc": "debug/dwarf",
- "dwarf.BasicType": "debug/dwarf",
- "dwarf.BoolType": "debug/dwarf",
- "dwarf.CharType": "debug/dwarf",
- "dwarf.CommonType": "debug/dwarf",
- "dwarf.ComplexType": "debug/dwarf",
- "dwarf.Data": "debug/dwarf",
- "dwarf.DecodeError": "debug/dwarf",
- "dwarf.DotDotDotType": "debug/dwarf",
- "dwarf.Entry": "debug/dwarf",
- "dwarf.EnumType": "debug/dwarf",
- "dwarf.EnumValue": "debug/dwarf",
- "dwarf.Field": "debug/dwarf",
- "dwarf.FloatType": "debug/dwarf",
- "dwarf.FuncType": "debug/dwarf",
- "dwarf.IntType": "debug/dwarf",
- "dwarf.New": "debug/dwarf",
- "dwarf.Offset": "debug/dwarf",
- "dwarf.PtrType": "debug/dwarf",
- "dwarf.QualType": "debug/dwarf",
- "dwarf.Reader": "debug/dwarf",
- "dwarf.StructField": "debug/dwarf",
- "dwarf.StructType": "debug/dwarf",
- "dwarf.Tag": "debug/dwarf",
- "dwarf.TagAccessDeclaration": "debug/dwarf",
- "dwarf.TagArrayType": "debug/dwarf",
- "dwarf.TagBaseType": "debug/dwarf",
- "dwarf.TagCatchDwarfBlock": "debug/dwarf",
- "dwarf.TagClassType": "debug/dwarf",
- "dwarf.TagCommonDwarfBlock": "debug/dwarf",
- "dwarf.TagCommonInclusion": "debug/dwarf",
- "dwarf.TagCompileUnit": "debug/dwarf",
- "dwarf.TagConstType": "debug/dwarf",
- "dwarf.TagConstant": "debug/dwarf",
- "dwarf.TagDwarfProcedure": "debug/dwarf",
- "dwarf.TagEntryPoint": "debug/dwarf",
- "dwarf.TagEnumerationType": "debug/dwarf",
- "dwarf.TagEnumerator": "debug/dwarf",
- "dwarf.TagFileType": "debug/dwarf",
- "dwarf.TagFormalParameter": "debug/dwarf",
- "dwarf.TagFriend": "debug/dwarf",
- "dwarf.TagImportedDeclaration": "debug/dwarf",
- "dwarf.TagImportedModule": "debug/dwarf",
- "dwarf.TagImportedUnit": "debug/dwarf",
- "dwarf.TagInheritance": "debug/dwarf",
- "dwarf.TagInlinedSubroutine": "debug/dwarf",
- "dwarf.TagInterfaceType": "debug/dwarf",
- "dwarf.TagLabel": "debug/dwarf",
- "dwarf.TagLexDwarfBlock": "debug/dwarf",
- "dwarf.TagMember": "debug/dwarf",
- "dwarf.TagModule": "debug/dwarf",
- "dwarf.TagMutableType": "debug/dwarf",
- "dwarf.TagNamelist": "debug/dwarf",
- "dwarf.TagNamelistItem": "debug/dwarf",
- "dwarf.TagNamespace": "debug/dwarf",
- "dwarf.TagPackedType": "debug/dwarf",
- "dwarf.TagPartialUnit": "debug/dwarf",
- "dwarf.TagPointerType": "debug/dwarf",
- "dwarf.TagPtrToMemberType": "debug/dwarf",
- "dwarf.TagReferenceType": "debug/dwarf",
- "dwarf.TagRestrictType": "debug/dwarf",
- "dwarf.TagSetType": "debug/dwarf",
- "dwarf.TagStringType": "debug/dwarf",
- "dwarf.TagStructType": "debug/dwarf",
- "dwarf.TagSubprogram": "debug/dwarf",
- "dwarf.TagSubrangeType": "debug/dwarf",
- "dwarf.TagSubroutineType": "debug/dwarf",
- "dwarf.TagTemplateTypeParameter": "debug/dwarf",
- "dwarf.TagTemplateValueParameter": "debug/dwarf",
- "dwarf.TagThrownType": "debug/dwarf",
- "dwarf.TagTryDwarfBlock": "debug/dwarf",
- "dwarf.TagTypedef": "debug/dwarf",
- "dwarf.TagUnionType": "debug/dwarf",
- "dwarf.TagUnspecifiedParameters": "debug/dwarf",
- "dwarf.TagUnspecifiedType": "debug/dwarf",
- "dwarf.TagVariable": "debug/dwarf",
- "dwarf.TagVariant": "debug/dwarf",
- "dwarf.TagVariantPart": "debug/dwarf",
- "dwarf.TagVolatileType": "debug/dwarf",
- "dwarf.TagWithStmt": "debug/dwarf",
- "dwarf.Type": "debug/dwarf",
- "dwarf.TypedefType": "debug/dwarf",
- "dwarf.UcharType": "debug/dwarf",
- "dwarf.UintType": "debug/dwarf",
- "dwarf.VoidType": "debug/dwarf",
- "ecdsa.GenerateKey": "crypto/ecdsa",
- "ecdsa.PrivateKey": "crypto/ecdsa",
- "ecdsa.PublicKey": "crypto/ecdsa",
- "ecdsa.Sign": "crypto/ecdsa",
- "ecdsa.Verify": "crypto/ecdsa",
- "elf.ARM_MAGIC_TRAMP_NUMBER": "debug/elf",
- "elf.Class": "debug/elf",
- "elf.DF_BIND_NOW": "debug/elf",
- "elf.DF_ORIGIN": "debug/elf",
- "elf.DF_STATIC_TLS": "debug/elf",
- "elf.DF_SYMBOLIC": "debug/elf",
- "elf.DF_TEXTREL": "debug/elf",
- "elf.DT_BIND_NOW": "debug/elf",
- "elf.DT_DEBUG": "debug/elf",
- "elf.DT_ENCODING": "debug/elf",
- "elf.DT_FINI": "debug/elf",
- "elf.DT_FINI_ARRAY": "debug/elf",
- "elf.DT_FINI_ARRAYSZ": "debug/elf",
- "elf.DT_FLAGS": "debug/elf",
- "elf.DT_HASH": "debug/elf",
- "elf.DT_HIOS": "debug/elf",
- "elf.DT_HIPROC": "debug/elf",
- "elf.DT_INIT": "debug/elf",
- "elf.DT_INIT_ARRAY": "debug/elf",
- "elf.DT_INIT_ARRAYSZ": "debug/elf",
- "elf.DT_JMPREL": "debug/elf",
- "elf.DT_LOOS": "debug/elf",
- "elf.DT_LOPROC": "debug/elf",
- "elf.DT_NEEDED": "debug/elf",
- "elf.DT_NULL": "debug/elf",
- "elf.DT_PLTGOT": "debug/elf",
- "elf.DT_PLTREL": "debug/elf",
- "elf.DT_PLTRELSZ": "debug/elf",
- "elf.DT_PREINIT_ARRAY": "debug/elf",
- "elf.DT_PREINIT_ARRAYSZ": "debug/elf",
- "elf.DT_REL": "debug/elf",
- "elf.DT_RELA": "debug/elf",
- "elf.DT_RELAENT": "debug/elf",
- "elf.DT_RELASZ": "debug/elf",
- "elf.DT_RELENT": "debug/elf",
- "elf.DT_RELSZ": "debug/elf",
- "elf.DT_RPATH": "debug/elf",
- "elf.DT_RUNPATH": "debug/elf",
- "elf.DT_SONAME": "debug/elf",
- "elf.DT_STRSZ": "debug/elf",
- "elf.DT_STRTAB": "debug/elf",
- "elf.DT_SYMBOLIC": "debug/elf",
- "elf.DT_SYMENT": "debug/elf",
- "elf.DT_SYMTAB": "debug/elf",
- "elf.DT_TEXTREL": "debug/elf",
- "elf.DT_VERNEED": "debug/elf",
- "elf.DT_VERNEEDNUM": "debug/elf",
- "elf.DT_VERSYM": "debug/elf",
- "elf.Data": "debug/elf",
- "elf.Dyn32": "debug/elf",
- "elf.Dyn64": "debug/elf",
- "elf.DynFlag": "debug/elf",
- "elf.DynTag": "debug/elf",
- "elf.EI_ABIVERSION": "debug/elf",
- "elf.EI_CLASS": "debug/elf",
- "elf.EI_DATA": "debug/elf",
- "elf.EI_NIDENT": "debug/elf",
- "elf.EI_OSABI": "debug/elf",
- "elf.EI_PAD": "debug/elf",
- "elf.EI_VERSION": "debug/elf",
- "elf.ELFCLASS32": "debug/elf",
- "elf.ELFCLASS64": "debug/elf",
- "elf.ELFCLASSNONE": "debug/elf",
- "elf.ELFDATA2LSB": "debug/elf",
- "elf.ELFDATA2MSB": "debug/elf",
- "elf.ELFDATANONE": "debug/elf",
- "elf.ELFMAG": "debug/elf",
- "elf.ELFOSABI_86OPEN": "debug/elf",
- "elf.ELFOSABI_AIX": "debug/elf",
- "elf.ELFOSABI_ARM": "debug/elf",
- "elf.ELFOSABI_FREEBSD": "debug/elf",
- "elf.ELFOSABI_HPUX": "debug/elf",
- "elf.ELFOSABI_HURD": "debug/elf",
- "elf.ELFOSABI_IRIX": "debug/elf",
- "elf.ELFOSABI_LINUX": "debug/elf",
- "elf.ELFOSABI_MODESTO": "debug/elf",
- "elf.ELFOSABI_NETBSD": "debug/elf",
- "elf.ELFOSABI_NONE": "debug/elf",
- "elf.ELFOSABI_NSK": "debug/elf",
- "elf.ELFOSABI_OPENBSD": "debug/elf",
- "elf.ELFOSABI_OPENVMS": "debug/elf",
- "elf.ELFOSABI_SOLARIS": "debug/elf",
- "elf.ELFOSABI_STANDALONE": "debug/elf",
- "elf.ELFOSABI_TRU64": "debug/elf",
- "elf.EM_386": "debug/elf",
- "elf.EM_486": "debug/elf",
- "elf.EM_68HC12": "debug/elf",
- "elf.EM_68K": "debug/elf",
- "elf.EM_860": "debug/elf",
- "elf.EM_88K": "debug/elf",
- "elf.EM_960": "debug/elf",
- "elf.EM_ALPHA": "debug/elf",
- "elf.EM_ALPHA_STD": "debug/elf",
- "elf.EM_ARC": "debug/elf",
- "elf.EM_ARM": "debug/elf",
- "elf.EM_COLDFIRE": "debug/elf",
- "elf.EM_FR20": "debug/elf",
- "elf.EM_H8S": "debug/elf",
- "elf.EM_H8_300": "debug/elf",
- "elf.EM_H8_300H": "debug/elf",
- "elf.EM_H8_500": "debug/elf",
- "elf.EM_IA_64": "debug/elf",
- "elf.EM_M32": "debug/elf",
- "elf.EM_ME16": "debug/elf",
- "elf.EM_MIPS": "debug/elf",
- "elf.EM_MIPS_RS3_LE": "debug/elf",
- "elf.EM_MIPS_RS4_BE": "debug/elf",
- "elf.EM_MIPS_X": "debug/elf",
- "elf.EM_MMA": "debug/elf",
- "elf.EM_NCPU": "debug/elf",
- "elf.EM_NDR1": "debug/elf",
- "elf.EM_NONE": "debug/elf",
- "elf.EM_PARISC": "debug/elf",
- "elf.EM_PCP": "debug/elf",
- "elf.EM_PPC": "debug/elf",
- "elf.EM_PPC64": "debug/elf",
- "elf.EM_RCE": "debug/elf",
- "elf.EM_RH32": "debug/elf",
- "elf.EM_S370": "debug/elf",
- "elf.EM_S390": "debug/elf",
- "elf.EM_SH": "debug/elf",
- "elf.EM_SPARC": "debug/elf",
- "elf.EM_SPARC32PLUS": "debug/elf",
- "elf.EM_SPARCV9": "debug/elf",
- "elf.EM_ST100": "debug/elf",
- "elf.EM_STARCORE": "debug/elf",
- "elf.EM_TINYJ": "debug/elf",
- "elf.EM_TRICORE": "debug/elf",
- "elf.EM_V800": "debug/elf",
- "elf.EM_VPP500": "debug/elf",
- "elf.EM_X86_64": "debug/elf",
- "elf.ET_CORE": "debug/elf",
- "elf.ET_DYN": "debug/elf",
- "elf.ET_EXEC": "debug/elf",
- "elf.ET_HIOS": "debug/elf",
- "elf.ET_HIPROC": "debug/elf",
- "elf.ET_LOOS": "debug/elf",
- "elf.ET_LOPROC": "debug/elf",
- "elf.ET_NONE": "debug/elf",
- "elf.ET_REL": "debug/elf",
- "elf.EV_CURRENT": "debug/elf",
- "elf.EV_NONE": "debug/elf",
- "elf.File": "debug/elf",
- "elf.FileHeader": "debug/elf",
- "elf.FormatError": "debug/elf",
- "elf.Header32": "debug/elf",
- "elf.Header64": "debug/elf",
- "elf.ImportedSymbol": "debug/elf",
- "elf.Machine": "debug/elf",
- "elf.NT_FPREGSET": "debug/elf",
- "elf.NT_PRPSINFO": "debug/elf",
- "elf.NT_PRSTATUS": "debug/elf",
- "elf.NType": "debug/elf",
- "elf.NewFile": "debug/elf",
- "elf.OSABI": "debug/elf",
- "elf.Open": "debug/elf",
- "elf.PF_MASKOS": "debug/elf",
- "elf.PF_MASKPROC": "debug/elf",
- "elf.PF_R": "debug/elf",
- "elf.PF_W": "debug/elf",
- "elf.PF_X": "debug/elf",
- "elf.PT_DYNAMIC": "debug/elf",
- "elf.PT_HIOS": "debug/elf",
- "elf.PT_HIPROC": "debug/elf",
- "elf.PT_INTERP": "debug/elf",
- "elf.PT_LOAD": "debug/elf",
- "elf.PT_LOOS": "debug/elf",
- "elf.PT_LOPROC": "debug/elf",
- "elf.PT_NOTE": "debug/elf",
- "elf.PT_NULL": "debug/elf",
- "elf.PT_PHDR": "debug/elf",
- "elf.PT_SHLIB": "debug/elf",
- "elf.PT_TLS": "debug/elf",
- "elf.Prog": "debug/elf",
- "elf.Prog32": "debug/elf",
- "elf.Prog64": "debug/elf",
- "elf.ProgFlag": "debug/elf",
- "elf.ProgHeader": "debug/elf",
- "elf.ProgType": "debug/elf",
- "elf.R_386": "debug/elf",
- "elf.R_386_32": "debug/elf",
- "elf.R_386_COPY": "debug/elf",
- "elf.R_386_GLOB_DAT": "debug/elf",
- "elf.R_386_GOT32": "debug/elf",
- "elf.R_386_GOTOFF": "debug/elf",
- "elf.R_386_GOTPC": "debug/elf",
- "elf.R_386_JMP_SLOT": "debug/elf",
- "elf.R_386_NONE": "debug/elf",
- "elf.R_386_PC32": "debug/elf",
- "elf.R_386_PLT32": "debug/elf",
- "elf.R_386_RELATIVE": "debug/elf",
- "elf.R_386_TLS_DTPMOD32": "debug/elf",
- "elf.R_386_TLS_DTPOFF32": "debug/elf",
- "elf.R_386_TLS_GD": "debug/elf",
- "elf.R_386_TLS_GD_32": "debug/elf",
- "elf.R_386_TLS_GD_CALL": "debug/elf",
- "elf.R_386_TLS_GD_POP": "debug/elf",
- "elf.R_386_TLS_GD_PUSH": "debug/elf",
- "elf.R_386_TLS_GOTIE": "debug/elf",
- "elf.R_386_TLS_IE": "debug/elf",
- "elf.R_386_TLS_IE_32": "debug/elf",
- "elf.R_386_TLS_LDM": "debug/elf",
- "elf.R_386_TLS_LDM_32": "debug/elf",
- "elf.R_386_TLS_LDM_CALL": "debug/elf",
- "elf.R_386_TLS_LDM_POP": "debug/elf",
- "elf.R_386_TLS_LDM_PUSH": "debug/elf",
- "elf.R_386_TLS_LDO_32": "debug/elf",
- "elf.R_386_TLS_LE": "debug/elf",
- "elf.R_386_TLS_LE_32": "debug/elf",
- "elf.R_386_TLS_TPOFF": "debug/elf",
- "elf.R_386_TLS_TPOFF32": "debug/elf",
- "elf.R_ALPHA": "debug/elf",
- "elf.R_ALPHA_BRADDR": "debug/elf",
- "elf.R_ALPHA_COPY": "debug/elf",
- "elf.R_ALPHA_GLOB_DAT": "debug/elf",
- "elf.R_ALPHA_GPDISP": "debug/elf",
- "elf.R_ALPHA_GPREL32": "debug/elf",
- "elf.R_ALPHA_GPRELHIGH": "debug/elf",
- "elf.R_ALPHA_GPRELLOW": "debug/elf",
- "elf.R_ALPHA_GPVALUE": "debug/elf",
- "elf.R_ALPHA_HINT": "debug/elf",
- "elf.R_ALPHA_IMMED_BR_HI32": "debug/elf",
- "elf.R_ALPHA_IMMED_GP_16": "debug/elf",
- "elf.R_ALPHA_IMMED_GP_HI32": "debug/elf",
- "elf.R_ALPHA_IMMED_LO32": "debug/elf",
- "elf.R_ALPHA_IMMED_SCN_HI32": "debug/elf",
- "elf.R_ALPHA_JMP_SLOT": "debug/elf",
- "elf.R_ALPHA_LITERAL": "debug/elf",
- "elf.R_ALPHA_LITUSE": "debug/elf",
- "elf.R_ALPHA_NONE": "debug/elf",
- "elf.R_ALPHA_OP_PRSHIFT": "debug/elf",
- "elf.R_ALPHA_OP_PSUB": "debug/elf",
- "elf.R_ALPHA_OP_PUSH": "debug/elf",
- "elf.R_ALPHA_OP_STORE": "debug/elf",
- "elf.R_ALPHA_REFLONG": "debug/elf",
- "elf.R_ALPHA_REFQUAD": "debug/elf",
- "elf.R_ALPHA_RELATIVE": "debug/elf",
- "elf.R_ALPHA_SREL16": "debug/elf",
- "elf.R_ALPHA_SREL32": "debug/elf",
- "elf.R_ALPHA_SREL64": "debug/elf",
- "elf.R_ARM": "debug/elf",
- "elf.R_ARM_ABS12": "debug/elf",
- "elf.R_ARM_ABS16": "debug/elf",
- "elf.R_ARM_ABS32": "debug/elf",
- "elf.R_ARM_ABS8": "debug/elf",
- "elf.R_ARM_AMP_VCALL9": "debug/elf",
- "elf.R_ARM_COPY": "debug/elf",
- "elf.R_ARM_GLOB_DAT": "debug/elf",
- "elf.R_ARM_GNU_VTENTRY": "debug/elf",
- "elf.R_ARM_GNU_VTINHERIT": "debug/elf",
- "elf.R_ARM_GOT32": "debug/elf",
- "elf.R_ARM_GOTOFF": "debug/elf",
- "elf.R_ARM_GOTPC": "debug/elf",
- "elf.R_ARM_JUMP_SLOT": "debug/elf",
- "elf.R_ARM_NONE": "debug/elf",
- "elf.R_ARM_PC13": "debug/elf",
- "elf.R_ARM_PC24": "debug/elf",
- "elf.R_ARM_PLT32": "debug/elf",
- "elf.R_ARM_RABS32": "debug/elf",
- "elf.R_ARM_RBASE": "debug/elf",
- "elf.R_ARM_REL32": "debug/elf",
- "elf.R_ARM_RELATIVE": "debug/elf",
- "elf.R_ARM_RPC24": "debug/elf",
- "elf.R_ARM_RREL32": "debug/elf",
- "elf.R_ARM_RSBREL32": "debug/elf",
- "elf.R_ARM_SBREL32": "debug/elf",
- "elf.R_ARM_SWI24": "debug/elf",
- "elf.R_ARM_THM_ABS5": "debug/elf",
- "elf.R_ARM_THM_PC22": "debug/elf",
- "elf.R_ARM_THM_PC8": "debug/elf",
- "elf.R_ARM_THM_RPC22": "debug/elf",
- "elf.R_ARM_THM_SWI8": "debug/elf",
- "elf.R_ARM_THM_XPC22": "debug/elf",
- "elf.R_ARM_XPC25": "debug/elf",
- "elf.R_INFO": "debug/elf",
- "elf.R_INFO32": "debug/elf",
- "elf.R_PPC": "debug/elf",
- "elf.R_PPC_ADDR14": "debug/elf",
- "elf.R_PPC_ADDR14_BRNTAKEN": "debug/elf",
- "elf.R_PPC_ADDR14_BRTAKEN": "debug/elf",
- "elf.R_PPC_ADDR16": "debug/elf",
- "elf.R_PPC_ADDR16_HA": "debug/elf",
- "elf.R_PPC_ADDR16_HI": "debug/elf",
- "elf.R_PPC_ADDR16_LO": "debug/elf",
- "elf.R_PPC_ADDR24": "debug/elf",
- "elf.R_PPC_ADDR32": "debug/elf",
- "elf.R_PPC_COPY": "debug/elf",
- "elf.R_PPC_DTPMOD32": "debug/elf",
- "elf.R_PPC_DTPREL16": "debug/elf",
- "elf.R_PPC_DTPREL16_HA": "debug/elf",
- "elf.R_PPC_DTPREL16_HI": "debug/elf",
- "elf.R_PPC_DTPREL16_LO": "debug/elf",
- "elf.R_PPC_DTPREL32": "debug/elf",
- "elf.R_PPC_EMB_BIT_FLD": "debug/elf",
- "elf.R_PPC_EMB_MRKREF": "debug/elf",
- "elf.R_PPC_EMB_NADDR16": "debug/elf",
- "elf.R_PPC_EMB_NADDR16_HA": "debug/elf",
- "elf.R_PPC_EMB_NADDR16_HI": "debug/elf",
- "elf.R_PPC_EMB_NADDR16_LO": "debug/elf",
- "elf.R_PPC_EMB_NADDR32": "debug/elf",
- "elf.R_PPC_EMB_RELSDA": "debug/elf",
- "elf.R_PPC_EMB_RELSEC16": "debug/elf",
- "elf.R_PPC_EMB_RELST_HA": "debug/elf",
- "elf.R_PPC_EMB_RELST_HI": "debug/elf",
- "elf.R_PPC_EMB_RELST_LO": "debug/elf",
- "elf.R_PPC_EMB_SDA21": "debug/elf",
- "elf.R_PPC_EMB_SDA2I16": "debug/elf",
- "elf.R_PPC_EMB_SDA2REL": "debug/elf",
- "elf.R_PPC_EMB_SDAI16": "debug/elf",
- "elf.R_PPC_GLOB_DAT": "debug/elf",
- "elf.R_PPC_GOT16": "debug/elf",
- "elf.R_PPC_GOT16_HA": "debug/elf",
- "elf.R_PPC_GOT16_HI": "debug/elf",
- "elf.R_PPC_GOT16_LO": "debug/elf",
- "elf.R_PPC_GOT_TLSGD16": "debug/elf",
- "elf.R_PPC_GOT_TLSGD16_HA": "debug/elf",
- "elf.R_PPC_GOT_TLSGD16_HI": "debug/elf",
- "elf.R_PPC_GOT_TLSGD16_LO": "debug/elf",
- "elf.R_PPC_GOT_TLSLD16": "debug/elf",
- "elf.R_PPC_GOT_TLSLD16_HA": "debug/elf",
- "elf.R_PPC_GOT_TLSLD16_HI": "debug/elf",
- "elf.R_PPC_GOT_TLSLD16_LO": "debug/elf",
- "elf.R_PPC_GOT_TPREL16": "debug/elf",
- "elf.R_PPC_GOT_TPREL16_HA": "debug/elf",
- "elf.R_PPC_GOT_TPREL16_HI": "debug/elf",
- "elf.R_PPC_GOT_TPREL16_LO": "debug/elf",
- "elf.R_PPC_JMP_SLOT": "debug/elf",
- "elf.R_PPC_LOCAL24PC": "debug/elf",
- "elf.R_PPC_NONE": "debug/elf",
- "elf.R_PPC_PLT16_HA": "debug/elf",
- "elf.R_PPC_PLT16_HI": "debug/elf",
- "elf.R_PPC_PLT16_LO": "debug/elf",
- "elf.R_PPC_PLT32": "debug/elf",
- "elf.R_PPC_PLTREL24": "debug/elf",
- "elf.R_PPC_PLTREL32": "debug/elf",
- "elf.R_PPC_REL14": "debug/elf",
- "elf.R_PPC_REL14_BRNTAKEN": "debug/elf",
- "elf.R_PPC_REL14_BRTAKEN": "debug/elf",
- "elf.R_PPC_REL24": "debug/elf",
- "elf.R_PPC_REL32": "debug/elf",
- "elf.R_PPC_RELATIVE": "debug/elf",
- "elf.R_PPC_SDAREL16": "debug/elf",
- "elf.R_PPC_SECTOFF": "debug/elf",
- "elf.R_PPC_SECTOFF_HA": "debug/elf",
- "elf.R_PPC_SECTOFF_HI": "debug/elf",
- "elf.R_PPC_SECTOFF_LO": "debug/elf",
- "elf.R_PPC_TLS": "debug/elf",
- "elf.R_PPC_TPREL16": "debug/elf",
- "elf.R_PPC_TPREL16_HA": "debug/elf",
- "elf.R_PPC_TPREL16_HI": "debug/elf",
- "elf.R_PPC_TPREL16_LO": "debug/elf",
- "elf.R_PPC_TPREL32": "debug/elf",
- "elf.R_PPC_UADDR16": "debug/elf",
- "elf.R_PPC_UADDR32": "debug/elf",
- "elf.R_SPARC": "debug/elf",
- "elf.R_SPARC_10": "debug/elf",
- "elf.R_SPARC_11": "debug/elf",
- "elf.R_SPARC_13": "debug/elf",
- "elf.R_SPARC_16": "debug/elf",
- "elf.R_SPARC_22": "debug/elf",
- "elf.R_SPARC_32": "debug/elf",
- "elf.R_SPARC_5": "debug/elf",
- "elf.R_SPARC_6": "debug/elf",
- "elf.R_SPARC_64": "debug/elf",
- "elf.R_SPARC_7": "debug/elf",
- "elf.R_SPARC_8": "debug/elf",
- "elf.R_SPARC_COPY": "debug/elf",
- "elf.R_SPARC_DISP16": "debug/elf",
- "elf.R_SPARC_DISP32": "debug/elf",
- "elf.R_SPARC_DISP64": "debug/elf",
- "elf.R_SPARC_DISP8": "debug/elf",
- "elf.R_SPARC_GLOB_DAT": "debug/elf",
- "elf.R_SPARC_GLOB_JMP": "debug/elf",
- "elf.R_SPARC_GOT10": "debug/elf",
- "elf.R_SPARC_GOT13": "debug/elf",
- "elf.R_SPARC_GOT22": "debug/elf",
- "elf.R_SPARC_H44": "debug/elf",
- "elf.R_SPARC_HH22": "debug/elf",
- "elf.R_SPARC_HI22": "debug/elf",
- "elf.R_SPARC_HIPLT22": "debug/elf",
- "elf.R_SPARC_HIX22": "debug/elf",
- "elf.R_SPARC_HM10": "debug/elf",
- "elf.R_SPARC_JMP_SLOT": "debug/elf",
- "elf.R_SPARC_L44": "debug/elf",
- "elf.R_SPARC_LM22": "debug/elf",
- "elf.R_SPARC_LO10": "debug/elf",
- "elf.R_SPARC_LOPLT10": "debug/elf",
- "elf.R_SPARC_LOX10": "debug/elf",
- "elf.R_SPARC_M44": "debug/elf",
- "elf.R_SPARC_NONE": "debug/elf",
- "elf.R_SPARC_OLO10": "debug/elf",
- "elf.R_SPARC_PC10": "debug/elf",
- "elf.R_SPARC_PC22": "debug/elf",
- "elf.R_SPARC_PCPLT10": "debug/elf",
- "elf.R_SPARC_PCPLT22": "debug/elf",
- "elf.R_SPARC_PCPLT32": "debug/elf",
- "elf.R_SPARC_PC_HH22": "debug/elf",
- "elf.R_SPARC_PC_HM10": "debug/elf",
- "elf.R_SPARC_PC_LM22": "debug/elf",
- "elf.R_SPARC_PLT32": "debug/elf",
- "elf.R_SPARC_PLT64": "debug/elf",
- "elf.R_SPARC_REGISTER": "debug/elf",
- "elf.R_SPARC_RELATIVE": "debug/elf",
- "elf.R_SPARC_UA16": "debug/elf",
- "elf.R_SPARC_UA32": "debug/elf",
- "elf.R_SPARC_UA64": "debug/elf",
- "elf.R_SPARC_WDISP16": "debug/elf",
- "elf.R_SPARC_WDISP19": "debug/elf",
- "elf.R_SPARC_WDISP22": "debug/elf",
- "elf.R_SPARC_WDISP30": "debug/elf",
- "elf.R_SPARC_WPLT30": "debug/elf",
- "elf.R_SYM32": "debug/elf",
- "elf.R_SYM64": "debug/elf",
- "elf.R_TYPE32": "debug/elf",
- "elf.R_TYPE64": "debug/elf",
- "elf.R_X86_64": "debug/elf",
- "elf.R_X86_64_16": "debug/elf",
- "elf.R_X86_64_32": "debug/elf",
- "elf.R_X86_64_32S": "debug/elf",
- "elf.R_X86_64_64": "debug/elf",
- "elf.R_X86_64_8": "debug/elf",
- "elf.R_X86_64_COPY": "debug/elf",
- "elf.R_X86_64_DTPMOD64": "debug/elf",
- "elf.R_X86_64_DTPOFF32": "debug/elf",
- "elf.R_X86_64_DTPOFF64": "debug/elf",
- "elf.R_X86_64_GLOB_DAT": "debug/elf",
- "elf.R_X86_64_GOT32": "debug/elf",
- "elf.R_X86_64_GOTPCREL": "debug/elf",
- "elf.R_X86_64_GOTTPOFF": "debug/elf",
- "elf.R_X86_64_JMP_SLOT": "debug/elf",
- "elf.R_X86_64_NONE": "debug/elf",
- "elf.R_X86_64_PC16": "debug/elf",
- "elf.R_X86_64_PC32": "debug/elf",
- "elf.R_X86_64_PC8": "debug/elf",
- "elf.R_X86_64_PLT32": "debug/elf",
- "elf.R_X86_64_RELATIVE": "debug/elf",
- "elf.R_X86_64_TLSGD": "debug/elf",
- "elf.R_X86_64_TLSLD": "debug/elf",
- "elf.R_X86_64_TPOFF32": "debug/elf",
- "elf.R_X86_64_TPOFF64": "debug/elf",
- "elf.Rel32": "debug/elf",
- "elf.Rel64": "debug/elf",
- "elf.Rela32": "debug/elf",
- "elf.Rela64": "debug/elf",
- "elf.SHF_ALLOC": "debug/elf",
- "elf.SHF_EXECINSTR": "debug/elf",
- "elf.SHF_GROUP": "debug/elf",
- "elf.SHF_INFO_LINK": "debug/elf",
- "elf.SHF_LINK_ORDER": "debug/elf",
- "elf.SHF_MASKOS": "debug/elf",
- "elf.SHF_MASKPROC": "debug/elf",
- "elf.SHF_MERGE": "debug/elf",
- "elf.SHF_OS_NONCONFORMING": "debug/elf",
- "elf.SHF_STRINGS": "debug/elf",
- "elf.SHF_TLS": "debug/elf",
- "elf.SHF_WRITE": "debug/elf",
- "elf.SHN_ABS": "debug/elf",
- "elf.SHN_COMMON": "debug/elf",
- "elf.SHN_HIOS": "debug/elf",
- "elf.SHN_HIPROC": "debug/elf",
- "elf.SHN_HIRESERVE": "debug/elf",
- "elf.SHN_LOOS": "debug/elf",
- "elf.SHN_LOPROC": "debug/elf",
- "elf.SHN_LORESERVE": "debug/elf",
- "elf.SHN_UNDEF": "debug/elf",
- "elf.SHN_XINDEX": "debug/elf",
- "elf.SHT_DYNAMIC": "debug/elf",
- "elf.SHT_DYNSYM": "debug/elf",
- "elf.SHT_FINI_ARRAY": "debug/elf",
- "elf.SHT_GNU_ATTRIBUTES": "debug/elf",
- "elf.SHT_GNU_HASH": "debug/elf",
- "elf.SHT_GNU_LIBLIST": "debug/elf",
- "elf.SHT_GNU_VERDEF": "debug/elf",
- "elf.SHT_GNU_VERNEED": "debug/elf",
- "elf.SHT_GNU_VERSYM": "debug/elf",
- "elf.SHT_GROUP": "debug/elf",
- "elf.SHT_HASH": "debug/elf",
- "elf.SHT_HIOS": "debug/elf",
- "elf.SHT_HIPROC": "debug/elf",
- "elf.SHT_HIUSER": "debug/elf",
- "elf.SHT_INIT_ARRAY": "debug/elf",
- "elf.SHT_LOOS": "debug/elf",
- "elf.SHT_LOPROC": "debug/elf",
- "elf.SHT_LOUSER": "debug/elf",
- "elf.SHT_NOBITS": "debug/elf",
- "elf.SHT_NOTE": "debug/elf",
- "elf.SHT_NULL": "debug/elf",
- "elf.SHT_PREINIT_ARRAY": "debug/elf",
- "elf.SHT_PROGBITS": "debug/elf",
- "elf.SHT_REL": "debug/elf",
- "elf.SHT_RELA": "debug/elf",
- "elf.SHT_SHLIB": "debug/elf",
- "elf.SHT_STRTAB": "debug/elf",
- "elf.SHT_SYMTAB": "debug/elf",
- "elf.SHT_SYMTAB_SHNDX": "debug/elf",
- "elf.STB_GLOBAL": "debug/elf",
- "elf.STB_HIOS": "debug/elf",
- "elf.STB_HIPROC": "debug/elf",
- "elf.STB_LOCAL": "debug/elf",
- "elf.STB_LOOS": "debug/elf",
- "elf.STB_LOPROC": "debug/elf",
- "elf.STB_WEAK": "debug/elf",
- "elf.STT_COMMON": "debug/elf",
- "elf.STT_FILE": "debug/elf",
- "elf.STT_FUNC": "debug/elf",
- "elf.STT_HIOS": "debug/elf",
- "elf.STT_HIPROC": "debug/elf",
- "elf.STT_LOOS": "debug/elf",
- "elf.STT_LOPROC": "debug/elf",
- "elf.STT_NOTYPE": "debug/elf",
- "elf.STT_OBJECT": "debug/elf",
- "elf.STT_SECTION": "debug/elf",
- "elf.STT_TLS": "debug/elf",
- "elf.STV_DEFAULT": "debug/elf",
- "elf.STV_HIDDEN": "debug/elf",
- "elf.STV_INTERNAL": "debug/elf",
- "elf.STV_PROTECTED": "debug/elf",
- "elf.ST_BIND": "debug/elf",
- "elf.ST_INFO": "debug/elf",
- "elf.ST_TYPE": "debug/elf",
- "elf.ST_VISIBILITY": "debug/elf",
- "elf.Section": "debug/elf",
- "elf.Section32": "debug/elf",
- "elf.Section64": "debug/elf",
- "elf.SectionFlag": "debug/elf",
- "elf.SectionHeader": "debug/elf",
- "elf.SectionIndex": "debug/elf",
- "elf.SectionType": "debug/elf",
- "elf.Sym32": "debug/elf",
- "elf.Sym32Size": "debug/elf",
- "elf.Sym64": "debug/elf",
- "elf.Sym64Size": "debug/elf",
- "elf.SymBind": "debug/elf",
- "elf.SymType": "debug/elf",
- "elf.SymVis": "debug/elf",
- "elf.Symbol": "debug/elf",
- "elf.Type": "debug/elf",
- "elf.Version": "debug/elf",
- "elliptic.Curve": "crypto/elliptic",
- "elliptic.CurveParams": "crypto/elliptic",
- "elliptic.GenerateKey": "crypto/elliptic",
- "elliptic.Marshal": "crypto/elliptic",
- "elliptic.P224": "crypto/elliptic",
- "elliptic.P256": "crypto/elliptic",
- "elliptic.P384": "crypto/elliptic",
- "elliptic.P521": "crypto/elliptic",
- "elliptic.Unmarshal": "crypto/elliptic",
- "encoding.BinaryMarshaler": "encoding",
- "encoding.BinaryUnmarshaler": "encoding",
- "encoding.TextMarshaler": "encoding",
- "encoding.TextUnmarshaler": "encoding",
- "errors.New": "errors",
- "exec.Cmd": "os/exec",
- "exec.Command": "os/exec",
- "exec.ErrNotFound": "os/exec",
- "exec.Error": "os/exec",
- "exec.ExitError": "os/exec",
- "exec.LookPath": "os/exec",
- "expvar.Do": "expvar",
- "expvar.Float": "expvar",
- "expvar.Func": "expvar",
- "expvar.Get": "expvar",
- "expvar.Int": "expvar",
- "expvar.KeyValue": "expvar",
- "expvar.Map": "expvar",
- "expvar.NewFloat": "expvar",
- "expvar.NewInt": "expvar",
- "expvar.NewMap": "expvar",
- "expvar.NewString": "expvar",
- "expvar.Publish": "expvar",
- "expvar.String": "expvar",
- "expvar.Var": "expvar",
- "fcgi.Serve": "net/http/fcgi",
- "filepath.Abs": "path/filepath",
- "filepath.Base": "path/filepath",
- "filepath.Clean": "path/filepath",
- "filepath.Dir": "path/filepath",
- "filepath.ErrBadPattern": "path/filepath",
- "filepath.EvalSymlinks": "path/filepath",
- "filepath.Ext": "path/filepath",
- "filepath.FromSlash": "path/filepath",
- "filepath.Glob": "path/filepath",
- "filepath.HasPrefix": "path/filepath",
- "filepath.IsAbs": "path/filepath",
- "filepath.Join": "path/filepath",
- "filepath.ListSeparator": "path/filepath",
- "filepath.Match": "path/filepath",
- "filepath.Rel": "path/filepath",
- "filepath.Separator": "path/filepath",
- "filepath.SkipDir": "path/filepath",
- "filepath.Split": "path/filepath",
- "filepath.SplitList": "path/filepath",
- "filepath.ToSlash": "path/filepath",
- "filepath.VolumeName": "path/filepath",
- "filepath.Walk": "path/filepath",
- "filepath.WalkFunc": "path/filepath",
- "flag.Arg": "flag",
- "flag.Args": "flag",
- "flag.Bool": "flag",
- "flag.BoolVar": "flag",
- "flag.CommandLine": "flag",
- "flag.ContinueOnError": "flag",
- "flag.Duration": "flag",
- "flag.DurationVar": "flag",
- "flag.ErrHelp": "flag",
- "flag.ErrorHandling": "flag",
- "flag.ExitOnError": "flag",
- "flag.Flag": "flag",
- "flag.FlagSet": "flag",
- "flag.Float64": "flag",
- "flag.Float64Var": "flag",
- "flag.Getter": "flag",
- "flag.Int": "flag",
- "flag.Int64": "flag",
- "flag.Int64Var": "flag",
- "flag.IntVar": "flag",
- "flag.Lookup": "flag",
- "flag.NArg": "flag",
- "flag.NFlag": "flag",
- "flag.NewFlagSet": "flag",
- "flag.PanicOnError": "flag",
- "flag.Parse": "flag",
- "flag.Parsed": "flag",
- "flag.PrintDefaults": "flag",
- "flag.Set": "flag",
- "flag.String": "flag",
- "flag.StringVar": "flag",
- "flag.Uint": "flag",
- "flag.Uint64": "flag",
- "flag.Uint64Var": "flag",
- "flag.UintVar": "flag",
- "flag.Usage": "flag",
- "flag.Value": "flag",
- "flag.Var": "flag",
- "flag.Visit": "flag",
- "flag.VisitAll": "flag",
- "flate.BestCompression": "compress/flate",
- "flate.BestSpeed": "compress/flate",
- "flate.CorruptInputError": "compress/flate",
- "flate.DefaultCompression": "compress/flate",
- "flate.InternalError": "compress/flate",
- "flate.NewReader": "compress/flate",
- "flate.NewReaderDict": "compress/flate",
- "flate.NewWriter": "compress/flate",
- "flate.NewWriterDict": "compress/flate",
- "flate.NoCompression": "compress/flate",
- "flate.ReadError": "compress/flate",
- "flate.Reader": "compress/flate",
- "flate.WriteError": "compress/flate",
- "flate.Writer": "compress/flate",
- "fmt.Errorf": "fmt",
- "fmt.Formatter": "fmt",
- "fmt.Fprint": "fmt",
- "fmt.Fprintf": "fmt",
- "fmt.Fprintln": "fmt",
- "fmt.Fscan": "fmt",
- "fmt.Fscanf": "fmt",
- "fmt.Fscanln": "fmt",
- "fmt.GoStringer": "fmt",
- "fmt.Print": "fmt",
- "fmt.Printf": "fmt",
- "fmt.Println": "fmt",
- "fmt.Scan": "fmt",
- "fmt.ScanState": "fmt",
- "fmt.Scanf": "fmt",
- "fmt.Scanln": "fmt",
- "fmt.Scanner": "fmt",
- "fmt.Sprint": "fmt",
- "fmt.Sprintf": "fmt",
- "fmt.Sprintln": "fmt",
- "fmt.Sscan": "fmt",
- "fmt.Sscanf": "fmt",
- "fmt.Sscanln": "fmt",
- "fmt.State": "fmt",
- "fmt.Stringer": "fmt",
- "fnv.New32": "hash/fnv",
- "fnv.New32a": "hash/fnv",
- "fnv.New64": "hash/fnv",
- "fnv.New64a": "hash/fnv",
- "format.Node": "go/format",
- "format.Source": "go/format",
- "gif.Decode": "image/gif",
- "gif.DecodeAll": "image/gif",
- "gif.DecodeConfig": "image/gif",
- "gif.Encode": "image/gif",
- "gif.EncodeAll": "image/gif",
- "gif.GIF": "image/gif",
- "gif.Options": "image/gif",
- "gob.CommonType": "encoding/gob",
- "gob.Decoder": "encoding/gob",
- "gob.Encoder": "encoding/gob",
- "gob.GobDecoder": "encoding/gob",
- "gob.GobEncoder": "encoding/gob",
- "gob.NewDecoder": "encoding/gob",
- "gob.NewEncoder": "encoding/gob",
- "gob.Register": "encoding/gob",
- "gob.RegisterName": "encoding/gob",
- "gosym.DecodingError": "debug/gosym",
- "gosym.Func": "debug/gosym",
- "gosym.LineTable": "debug/gosym",
- "gosym.NewLineTable": "debug/gosym",
- "gosym.NewTable": "debug/gosym",
- "gosym.Obj": "debug/gosym",
- "gosym.Sym": "debug/gosym",
- "gosym.Table": "debug/gosym",
- "gosym.UnknownFileError": "debug/gosym",
- "gosym.UnknownLineError": "debug/gosym",
- "gzip.BestCompression": "compress/gzip",
- "gzip.BestSpeed": "compress/gzip",
- "gzip.DefaultCompression": "compress/gzip",
- "gzip.ErrChecksum": "compress/gzip",
- "gzip.ErrHeader": "compress/gzip",
- "gzip.Header": "compress/gzip",
- "gzip.NewReader": "compress/gzip",
- "gzip.NewWriter": "compress/gzip",
- "gzip.NewWriterLevel": "compress/gzip",
- "gzip.NoCompression": "compress/gzip",
- "gzip.Reader": "compress/gzip",
- "gzip.Writer": "compress/gzip",
- "hash.Hash": "hash",
- "hash.Hash32": "hash",
- "hash.Hash64": "hash",
- "heap.Fix": "container/heap",
- "heap.Init": "container/heap",
- "heap.Interface": "container/heap",
- "heap.Pop": "container/heap",
- "heap.Push": "container/heap",
- "heap.Remove": "container/heap",
- "hex.Decode": "encoding/hex",
- "hex.DecodeString": "encoding/hex",
- "hex.DecodedLen": "encoding/hex",
- "hex.Dump": "encoding/hex",
- "hex.Dumper": "encoding/hex",
- "hex.Encode": "encoding/hex",
- "hex.EncodeToString": "encoding/hex",
- "hex.EncodedLen": "encoding/hex",
- "hex.ErrLength": "encoding/hex",
- "hex.InvalidByteError": "encoding/hex",
- "hmac.Equal": "crypto/hmac",
- "hmac.New": "crypto/hmac",
- "html.EscapeString": "html",
- "html.UnescapeString": "html",
- "http.CanonicalHeaderKey": "net/http",
- "http.Client": "net/http",
- "http.CloseNotifier": "net/http",
- "http.Cookie": "net/http",
- "http.CookieJar": "net/http",
- "http.DefaultClient": "net/http",
- "http.DefaultMaxHeaderBytes": "net/http",
- "http.DefaultMaxIdleConnsPerHost": "net/http",
- "http.DefaultServeMux": "net/http",
- "http.DefaultTransport": "net/http",
- "http.DetectContentType": "net/http",
- "http.Dir": "net/http",
- "http.ErrBodyNotAllowed": "net/http",
- "http.ErrBodyReadAfterClose": "net/http",
- "http.ErrContentLength": "net/http",
- "http.ErrHandlerTimeout": "net/http",
- "http.ErrHeaderTooLong": "net/http",
- "http.ErrHijacked": "net/http",
- "http.ErrLineTooLong": "net/http",
- "http.ErrMissingBoundary": "net/http",
- "http.ErrMissingContentLength": "net/http",
- "http.ErrMissingFile": "net/http",
- "http.ErrNoCookie": "net/http",
- "http.ErrNoLocation": "net/http",
- "http.ErrNotMultipart": "net/http",
- "http.ErrNotSupported": "net/http",
- "http.ErrShortBody": "net/http",
- "http.ErrUnexpectedTrailer": "net/http",
- "http.ErrWriteAfterFlush": "net/http",
- "http.Error": "net/http",
- "http.File": "net/http",
- "http.FileServer": "net/http",
- "http.FileSystem": "net/http",
- "http.Flusher": "net/http",
- "http.Get": "net/http",
- "http.Handle": "net/http",
- "http.HandleFunc": "net/http",
- "http.Handler": "net/http",
- "http.HandlerFunc": "net/http",
- "http.Head": "net/http",
- "http.Header": "net/http",
- "http.Hijacker": "net/http",
- "http.ListenAndServe": "net/http",
- "http.ListenAndServeTLS": "net/http",
- "http.MaxBytesReader": "net/http",
- "http.NewFileTransport": "net/http",
- "http.NewRequest": "net/http",
- "http.NewServeMux": "net/http",
- "http.NotFound": "net/http",
- "http.NotFoundHandler": "net/http",
- "http.ParseHTTPVersion": "net/http",
- "http.ParseTime": "net/http",
- "http.Post": "net/http",
- "http.PostForm": "net/http",
- "http.ProtocolError": "net/http",
- "http.ProxyFromEnvironment": "net/http",
- "http.ProxyURL": "net/http",
- "http.ReadRequest": "net/http",
- "http.ReadResponse": "net/http",
- "http.Redirect": "net/http",
- "http.RedirectHandler": "net/http",
- "http.Request": "net/http",
- "http.Response": "net/http",
- "http.ResponseWriter": "net/http",
- "http.RoundTripper": "net/http",
- "http.Serve": "net/http",
- "http.ServeContent": "net/http",
- "http.ServeFile": "net/http",
- "http.ServeMux": "net/http",
- "http.Server": "net/http",
- "http.SetCookie": "net/http",
- "http.StatusAccepted": "net/http",
- "http.StatusBadGateway": "net/http",
- "http.StatusBadRequest": "net/http",
- "http.StatusConflict": "net/http",
- "http.StatusContinue": "net/http",
- "http.StatusCreated": "net/http",
- "http.StatusExpectationFailed": "net/http",
- "http.StatusForbidden": "net/http",
- "http.StatusFound": "net/http",
- "http.StatusGatewayTimeout": "net/http",
- "http.StatusGone": "net/http",
- "http.StatusHTTPVersionNotSupported": "net/http",
- "http.StatusInternalServerError": "net/http",
- "http.StatusLengthRequired": "net/http",
- "http.StatusMethodNotAllowed": "net/http",
- "http.StatusMovedPermanently": "net/http",
- "http.StatusMultipleChoices": "net/http",
- "http.StatusNoContent": "net/http",
- "http.StatusNonAuthoritativeInfo": "net/http",
- "http.StatusNotAcceptable": "net/http",
- "http.StatusNotFound": "net/http",
- "http.StatusNotImplemented": "net/http",
- "http.StatusNotModified": "net/http",
- "http.StatusOK": "net/http",
- "http.StatusPartialContent": "net/http",
- "http.StatusPaymentRequired": "net/http",
- "http.StatusPreconditionFailed": "net/http",
- "http.StatusProxyAuthRequired": "net/http",
- "http.StatusRequestEntityTooLarge": "net/http",
- "http.StatusRequestTimeout": "net/http",
- "http.StatusRequestURITooLong": "net/http",
- "http.StatusRequestedRangeNotSatisfiable": "net/http",
- "http.StatusResetContent": "net/http",
- "http.StatusSeeOther": "net/http",
- "http.StatusServiceUnavailable": "net/http",
- "http.StatusSwitchingProtocols": "net/http",
- "http.StatusTeapot": "net/http",
- "http.StatusTemporaryRedirect": "net/http",
- "http.StatusText": "net/http",
- "http.StatusUnauthorized": "net/http",
- "http.StatusUnsupportedMediaType": "net/http",
- "http.StatusUseProxy": "net/http",
- "http.StripPrefix": "net/http",
- "http.TimeFormat": "net/http",
- "http.TimeoutHandler": "net/http",
- "http.Transport": "net/http",
- "httptest.DefaultRemoteAddr": "net/http/httptest",
- "httptest.NewRecorder": "net/http/httptest",
- "httptest.NewServer": "net/http/httptest",
- "httptest.NewTLSServer": "net/http/httptest",
- "httptest.NewUnstartedServer": "net/http/httptest",
- "httptest.ResponseRecorder": "net/http/httptest",
- "httptest.Server": "net/http/httptest",
- "httputil.ClientConn": "net/http/httputil",
- "httputil.DumpRequest": "net/http/httputil",
- "httputil.DumpRequestOut": "net/http/httputil",
- "httputil.DumpResponse": "net/http/httputil",
- "httputil.ErrClosed": "net/http/httputil",
- "httputil.ErrLineTooLong": "net/http/httputil",
- "httputil.ErrPersistEOF": "net/http/httputil",
- "httputil.ErrPipeline": "net/http/httputil",
- "httputil.NewChunkedReader": "net/http/httputil",
- "httputil.NewChunkedWriter": "net/http/httputil",
- "httputil.NewClientConn": "net/http/httputil",
- "httputil.NewProxyClientConn": "net/http/httputil",
- "httputil.NewServerConn": "net/http/httputil",
- "httputil.NewSingleHostReverseProxy": "net/http/httputil",
- "httputil.ReverseProxy": "net/http/httputil",
- "httputil.ServerConn": "net/http/httputil",
- "image.Alpha": "image",
- "image.Alpha16": "image",
- "image.Black": "image",
- "image.Config": "image",
- "image.Decode": "image",
- "image.DecodeConfig": "image",
- "image.ErrFormat": "image",
- "image.Gray": "image",
- "image.Gray16": "image",
- "image.Image": "image",
- "image.NRGBA": "image",
- "image.NRGBA64": "image",
- "image.NewAlpha": "image",
- "image.NewAlpha16": "image",
- "image.NewGray": "image",
- "image.NewGray16": "image",
- "image.NewNRGBA": "image",
- "image.NewNRGBA64": "image",
- "image.NewPaletted": "image",
- "image.NewRGBA": "image",
- "image.NewRGBA64": "image",
- "image.NewUniform": "image",
- "image.NewYCbCr": "image",
- "image.Opaque": "image",
- "image.Paletted": "image",
- "image.PalettedImage": "image",
- "image.Point": "image",
- "image.Pt": "image",
- "image.RGBA": "image",
- "image.RGBA64": "image",
- "image.Rect": "image",
- "image.Rectangle": "image",
- "image.RegisterFormat": "image",
- "image.Transparent": "image",
- "image.Uniform": "image",
- "image.White": "image",
- "image.YCbCr": "image",
- "image.YCbCrSubsampleRatio": "image",
- "image.YCbCrSubsampleRatio420": "image",
- "image.YCbCrSubsampleRatio422": "image",
- "image.YCbCrSubsampleRatio440": "image",
- "image.YCbCrSubsampleRatio444": "image",
- "image.ZP": "image",
- "image.ZR": "image",
- "io.ByteReader": "io",
- "io.ByteScanner": "io",
- "io.ByteWriter": "io",
- "io.Closer": "io",
- "io.Copy": "io",
- "io.CopyN": "io",
- "io.EOF": "io",
- "io.ErrClosedPipe": "io",
- "io.ErrNoProgress": "io",
- "io.ErrShortBuffer": "io",
- "io.ErrShortWrite": "io",
- "io.ErrUnexpectedEOF": "io",
- "io.LimitReader": "io",
- "io.LimitedReader": "io",
- "io.MultiReader": "io",
- "io.MultiWriter": "io",
- "io.NewSectionReader": "io",
- "io.Pipe": "io",
- "io.PipeReader": "io",
- "io.PipeWriter": "io",
- "io.ReadAtLeast": "io",
- "io.ReadCloser": "io",
- "io.ReadFull": "io",
- "io.ReadSeeker": "io",
- "io.ReadWriteCloser": "io",
- "io.ReadWriteSeeker": "io",
- "io.ReadWriter": "io",
- "io.Reader": "io",
- "io.ReaderAt": "io",
- "io.ReaderFrom": "io",
- "io.RuneReader": "io",
- "io.RuneScanner": "io",
- "io.SectionReader": "io",
- "io.Seeker": "io",
- "io.TeeReader": "io",
- "io.WriteCloser": "io",
- "io.WriteSeeker": "io",
- "io.WriteString": "io",
- "io.Writer": "io",
- "io.WriterAt": "io",
- "io.WriterTo": "io",
- "iotest.DataErrReader": "testing/iotest",
- "iotest.ErrTimeout": "testing/iotest",
- "iotest.HalfReader": "testing/iotest",
- "iotest.NewReadLogger": "testing/iotest",
- "iotest.NewWriteLogger": "testing/iotest",
- "iotest.OneByteReader": "testing/iotest",
- "iotest.TimeoutReader": "testing/iotest",
- "iotest.TruncateWriter": "testing/iotest",
- "ioutil.Discard": "io/ioutil",
- "ioutil.NopCloser": "io/ioutil",
- "ioutil.ReadAll": "io/ioutil",
- "ioutil.ReadDir": "io/ioutil",
- "ioutil.ReadFile": "io/ioutil",
- "ioutil.TempDir": "io/ioutil",
- "ioutil.TempFile": "io/ioutil",
- "ioutil.WriteFile": "io/ioutil",
- "jpeg.Decode": "image/jpeg",
- "jpeg.DecodeConfig": "image/jpeg",
- "jpeg.DefaultQuality": "image/jpeg",
- "jpeg.Encode": "image/jpeg",
- "jpeg.FormatError": "image/jpeg",
- "jpeg.Options": "image/jpeg",
- "jpeg.Reader": "image/jpeg",
- "jpeg.UnsupportedError": "image/jpeg",
- "json.Compact": "encoding/json",
- "json.Decoder": "encoding/json",
- "json.Encoder": "encoding/json",
- "json.HTMLEscape": "encoding/json",
- "json.Indent": "encoding/json",
- "json.InvalidUTF8Error": "encoding/json",
- "json.InvalidUnmarshalError": "encoding/json",
- "json.Marshal": "encoding/json",
- "json.MarshalIndent": "encoding/json",
- "json.Marshaler": "encoding/json",
- "json.MarshalerError": "encoding/json",
- "json.NewDecoder": "encoding/json",
- "json.NewEncoder": "encoding/json",
- "json.Number": "encoding/json",
- "json.RawMessage": "encoding/json",
- "json.SyntaxError": "encoding/json",
- "json.Unmarshal": "encoding/json",
- "json.UnmarshalFieldError": "encoding/json",
- "json.UnmarshalTypeError": "encoding/json",
- "json.Unmarshaler": "encoding/json",
- "json.UnsupportedTypeError": "encoding/json",
- "json.UnsupportedValueError": "encoding/json",
- "jsonrpc.Dial": "net/rpc/jsonrpc",
- "jsonrpc.NewClient": "net/rpc/jsonrpc",
- "jsonrpc.NewClientCodec": "net/rpc/jsonrpc",
- "jsonrpc.NewServerCodec": "net/rpc/jsonrpc",
- "jsonrpc.ServeConn": "net/rpc/jsonrpc",
- "list.Element": "container/list",
- "list.List": "container/list",
- "list.New": "container/list",
- "log.Fatal": "log",
- "log.Fatalf": "log",
- "log.Fatalln": "log",
- "log.Flags": "log",
- "log.Ldate": "log",
- "log.Llongfile": "log",
- "log.Lmicroseconds": "log",
- "log.Logger": "log",
- "log.Lshortfile": "log",
- "log.LstdFlags": "log",
- "log.Ltime": "log",
- "log.New": "log",
- "log.Panic": "log",
- "log.Panicf": "log",
- "log.Panicln": "log",
- "log.Prefix": "log",
- "log.Print": "log",
- "log.Printf": "log",
- "log.Println": "log",
- "log.SetFlags": "log",
- "log.SetOutput": "log",
- "log.SetPrefix": "log",
- "lzw.LSB": "compress/lzw",
- "lzw.MSB": "compress/lzw",
- "lzw.NewReader": "compress/lzw",
- "lzw.NewWriter": "compress/lzw",
- "lzw.Order": "compress/lzw",
- "macho.Cpu": "debug/macho",
- "macho.Cpu386": "debug/macho",
- "macho.CpuAmd64": "debug/macho",
- "macho.Dylib": "debug/macho",
- "macho.DylibCmd": "debug/macho",
- "macho.Dysymtab": "debug/macho",
- "macho.DysymtabCmd": "debug/macho",
- "macho.File": "debug/macho",
- "macho.FileHeader": "debug/macho",
- "macho.FormatError": "debug/macho",
- "macho.Load": "debug/macho",
- "macho.LoadBytes": "debug/macho",
- "macho.LoadCmd": "debug/macho",
- "macho.LoadCmdDylib": "debug/macho",
- "macho.LoadCmdDylinker": "debug/macho",
- "macho.LoadCmdDysymtab": "debug/macho",
- "macho.LoadCmdSegment": "debug/macho",
- "macho.LoadCmdSegment64": "debug/macho",
- "macho.LoadCmdSymtab": "debug/macho",
- "macho.LoadCmdThread": "debug/macho",
- "macho.LoadCmdUnixThread": "debug/macho",
- "macho.Magic32": "debug/macho",
- "macho.Magic64": "debug/macho",
- "macho.NewFile": "debug/macho",
- "macho.Nlist32": "debug/macho",
- "macho.Nlist64": "debug/macho",
- "macho.Open": "debug/macho",
- "macho.Regs386": "debug/macho",
- "macho.RegsAMD64": "debug/macho",
- "macho.Section": "debug/macho",
- "macho.Section32": "debug/macho",
- "macho.Section64": "debug/macho",
- "macho.SectionHeader": "debug/macho",
- "macho.Segment": "debug/macho",
- "macho.Segment32": "debug/macho",
- "macho.Segment64": "debug/macho",
- "macho.SegmentHeader": "debug/macho",
- "macho.Symbol": "debug/macho",
- "macho.Symtab": "debug/macho",
- "macho.SymtabCmd": "debug/macho",
- "macho.Thread": "debug/macho",
- "macho.Type": "debug/macho",
- "macho.TypeExec": "debug/macho",
- "macho.TypeObj": "debug/macho",
- "mail.Address": "net/mail",
- "mail.ErrHeaderNotPresent": "net/mail",
- "mail.Header": "net/mail",
- "mail.Message": "net/mail",
- "mail.ParseAddress": "net/mail",
- "mail.ParseAddressList": "net/mail",
- "mail.ReadMessage": "net/mail",
- "math.Abs": "math",
- "math.Acos": "math",
- "math.Acosh": "math",
- "math.Asin": "math",
- "math.Asinh": "math",
- "math.Atan": "math",
- "math.Atan2": "math",
- "math.Atanh": "math",
- "math.Cbrt": "math",
- "math.Ceil": "math",
- "math.Copysign": "math",
- "math.Cos": "math",
- "math.Cosh": "math",
- "math.Dim": "math",
- "math.E": "math",
- "math.Erf": "math",
- "math.Erfc": "math",
- "math.Exp": "math",
- "math.Exp2": "math",
- "math.Expm1": "math",
- "math.Float32bits": "math",
- "math.Float32frombits": "math",
- "math.Float64bits": "math",
- "math.Float64frombits": "math",
- "math.Floor": "math",
- "math.Frexp": "math",
- "math.Gamma": "math",
- "math.Hypot": "math",
- "math.Ilogb": "math",
- "math.Inf": "math",
- "math.IsInf": "math",
- "math.IsNaN": "math",
- "math.J0": "math",
- "math.J1": "math",
- "math.Jn": "math",
- "math.Ldexp": "math",
- "math.Lgamma": "math",
- "math.Ln10": "math",
- "math.Ln2": "math",
- "math.Log": "math",
- "math.Log10": "math",
- "math.Log10E": "math",
- "math.Log1p": "math",
- "math.Log2": "math",
- "math.Log2E": "math",
- "math.Logb": "math",
- "math.Max": "math",
- "math.MaxFloat32": "math",
- "math.MaxFloat64": "math",
- "math.MaxInt16": "math",
- "math.MaxInt32": "math",
- "math.MaxInt64": "math",
- "math.MaxInt8": "math",
- "math.MaxUint16": "math",
- "math.MaxUint32": "math",
- "math.MaxUint64": "math",
- "math.MaxUint8": "math",
- "math.Min": "math",
- "math.MinInt16": "math",
- "math.MinInt32": "math",
- "math.MinInt64": "math",
- "math.MinInt8": "math",
- "math.Mod": "math",
- "math.Modf": "math",
- "math.NaN": "math",
- "math.Nextafter": "math",
- "math.Phi": "math",
- "math.Pi": "math",
- "math.Pow": "math",
- "math.Pow10": "math",
- "math.Remainder": "math",
- "math.Signbit": "math",
- "math.Sin": "math",
- "math.Sincos": "math",
- "math.Sinh": "math",
- "math.SmallestNonzeroFloat32": "math",
- "math.SmallestNonzeroFloat64": "math",
- "math.Sqrt": "math",
- "math.Sqrt2": "math",
- "math.SqrtE": "math",
- "math.SqrtPhi": "math",
- "math.SqrtPi": "math",
- "math.Tan": "math",
- "math.Tanh": "math",
- "math.Trunc": "math",
- "math.Y0": "math",
- "math.Y1": "math",
- "math.Yn": "math",
- "md5.BlockSize": "crypto/md5",
- "md5.New": "crypto/md5",
- "md5.Size": "crypto/md5",
- "md5.Sum": "crypto/md5",
- "mime.AddExtensionType": "mime",
- "mime.FormatMediaType": "mime",
- "mime.ParseMediaType": "mime",
- "mime.TypeByExtension": "mime",
- "multipart.File": "mime/multipart",
- "multipart.FileHeader": "mime/multipart",
- "multipart.Form": "mime/multipart",
- "multipart.NewReader": "mime/multipart",
- "multipart.NewWriter": "mime/multipart",
- "multipart.Part": "mime/multipart",
- "multipart.Reader": "mime/multipart",
- "multipart.Writer": "mime/multipart",
- "net.Addr": "net",
- "net.AddrError": "net",
- "net.CIDRMask": "net",
- "net.Conn": "net",
- "net.DNSConfigError": "net",
- "net.DNSError": "net",
- "net.Dial": "net",
- "net.DialIP": "net",
- "net.DialTCP": "net",
- "net.DialTimeout": "net",
- "net.DialUDP": "net",
- "net.DialUnix": "net",
- "net.Dialer": "net",
- "net.ErrWriteToConnected": "net",
- "net.Error": "net",
- "net.FileConn": "net",
- "net.FileListener": "net",
- "net.FilePacketConn": "net",
- "net.FlagBroadcast": "net",
- "net.FlagLoopback": "net",
- "net.FlagMulticast": "net",
- "net.FlagPointToPoint": "net",
- "net.FlagUp": "net",
- "net.Flags": "net",
- "net.HardwareAddr": "net",
- "net.IP": "net",
- "net.IPAddr": "net",
- "net.IPConn": "net",
- "net.IPMask": "net",
- "net.IPNet": "net",
- "net.IPv4": "net",
- "net.IPv4Mask": "net",
- "net.IPv4allrouter": "net",
- "net.IPv4allsys": "net",
- "net.IPv4bcast": "net",
- "net.IPv4len": "net",
- "net.IPv4zero": "net",
- "net.IPv6interfacelocalallnodes": "net",
- "net.IPv6len": "net",
- "net.IPv6linklocalallnodes": "net",
- "net.IPv6linklocalallrouters": "net",
- "net.IPv6loopback": "net",
- "net.IPv6unspecified": "net",
- "net.IPv6zero": "net",
- "net.Interface": "net",
- "net.InterfaceAddrs": "net",
- "net.InterfaceByIndex": "net",
- "net.InterfaceByName": "net",
- "net.Interfaces": "net",
- "net.InvalidAddrError": "net",
- "net.JoinHostPort": "net",
- "net.Listen": "net",
- "net.ListenIP": "net",
- "net.ListenMulticastUDP": "net",
- "net.ListenPacket": "net",
- "net.ListenTCP": "net",
- "net.ListenUDP": "net",
- "net.ListenUnix": "net",
- "net.ListenUnixgram": "net",
- "net.Listener": "net",
- "net.LookupAddr": "net",
- "net.LookupCNAME": "net",
- "net.LookupHost": "net",
- "net.LookupIP": "net",
- "net.LookupMX": "net",
- "net.LookupNS": "net",
- "net.LookupPort": "net",
- "net.LookupSRV": "net",
- "net.LookupTXT": "net",
- "net.MX": "net",
- "net.NS": "net",
- "net.OpError": "net",
- "net.PacketConn": "net",
- "net.ParseCIDR": "net",
- "net.ParseError": "net",
- "net.ParseIP": "net",
- "net.ParseMAC": "net",
- "net.Pipe": "net",
- "net.ResolveIPAddr": "net",
- "net.ResolveTCPAddr": "net",
- "net.ResolveUDPAddr": "net",
- "net.ResolveUnixAddr": "net",
- "net.SRV": "net",
- "net.SplitHostPort": "net",
- "net.TCPAddr": "net",
- "net.TCPConn": "net",
- "net.TCPListener": "net",
- "net.UDPAddr": "net",
- "net.UDPConn": "net",
- "net.UnixAddr": "net",
- "net.UnixConn": "net",
- "net.UnixListener": "net",
- "net.UnknownNetworkError": "net",
- "os.Args": "os",
- "os.Chdir": "os",
- "os.Chmod": "os",
- "os.Chown": "os",
- "os.Chtimes": "os",
- "os.Clearenv": "os",
- "os.Create": "os",
- "os.DevNull": "os",
- "os.Environ": "os",
- "os.ErrExist": "os",
- "os.ErrInvalid": "os",
- "os.ErrNotExist": "os",
- "os.ErrPermission": "os",
- "os.Exit": "os",
- "os.Expand": "os",
- "os.ExpandEnv": "os",
- "os.File": "os",
- "os.FileInfo": "os",
- "os.FileMode": "os",
- "os.FindProcess": "os",
- "os.Getegid": "os",
- "os.Getenv": "os",
- "os.Geteuid": "os",
- "os.Getgid": "os",
- "os.Getgroups": "os",
- "os.Getpagesize": "os",
- "os.Getpid": "os",
- "os.Getppid": "os",
- "os.Getuid": "os",
- "os.Getwd": "os",
- "os.Hostname": "os",
- "os.Interrupt": "os",
- "os.IsExist": "os",
- "os.IsNotExist": "os",
- "os.IsPathSeparator": "os",
- "os.IsPermission": "os",
- "os.Kill": "os",
- "os.Lchown": "os",
- "os.Link": "os",
- "os.LinkError": "os",
- "os.Lstat": "os",
- "os.Mkdir": "os",
- "os.MkdirAll": "os",
- "os.ModeAppend": "os",
- "os.ModeCharDevice": "os",
- "os.ModeDevice": "os",
- "os.ModeDir": "os",
- "os.ModeExclusive": "os",
- "os.ModeNamedPipe": "os",
- "os.ModePerm": "os",
- "os.ModeSetgid": "os",
- "os.ModeSetuid": "os",
- "os.ModeSocket": "os",
- "os.ModeSticky": "os",
- "os.ModeSymlink": "os",
- "os.ModeTemporary": "os",
- "os.ModeType": "os",
- "os.NewFile": "os",
- "os.NewSyscallError": "os",
- "os.O_APPEND": "os",
- "os.O_CREATE": "os",
- "os.O_EXCL": "os",
- "os.O_RDONLY": "os",
- "os.O_RDWR": "os",
- "os.O_SYNC": "os",
- "os.O_TRUNC": "os",
- "os.O_WRONLY": "os",
- "os.Open": "os",
- "os.OpenFile": "os",
- "os.PathError": "os",
- "os.PathListSeparator": "os",
- "os.PathSeparator": "os",
- "os.Pipe": "os",
- "os.ProcAttr": "os",
- "os.Process": "os",
- "os.ProcessState": "os",
- "os.Readlink": "os",
- "os.Remove": "os",
- "os.RemoveAll": "os",
- "os.Rename": "os",
- "os.SEEK_CUR": "os",
- "os.SEEK_END": "os",
- "os.SEEK_SET": "os",
- "os.SameFile": "os",
- "os.Setenv": "os",
- "os.Signal": "os",
- "os.StartProcess": "os",
- "os.Stat": "os",
- "os.Stderr": "os",
- "os.Stdin": "os",
- "os.Stdout": "os",
- "os.Symlink": "os",
- "os.SyscallError": "os",
- "os.TempDir": "os",
- "os.Truncate": "os",
- "palette.Plan9": "image/color/palette",
- "palette.WebSafe": "image/color/palette",
- "parse.ActionNode": "text/template/parse",
- "parse.BoolNode": "text/template/parse",
- "parse.BranchNode": "text/template/parse",
- "parse.ChainNode": "text/template/parse",
- "parse.CommandNode": "text/template/parse",
- "parse.DotNode": "text/template/parse",
- "parse.FieldNode": "text/template/parse",
- "parse.IdentifierNode": "text/template/parse",
- "parse.IfNode": "text/template/parse",
- "parse.IsEmptyTree": "text/template/parse",
- "parse.ListNode": "text/template/parse",
- "parse.New": "text/template/parse",
- "parse.NewIdentifier": "text/template/parse",
- "parse.NilNode": "text/template/parse",
- "parse.Node": "text/template/parse",
- "parse.NodeAction": "text/template/parse",
- "parse.NodeBool": "text/template/parse",
- "parse.NodeChain": "text/template/parse",
- "parse.NodeCommand": "text/template/parse",
- "parse.NodeDot": "text/template/parse",
- "parse.NodeField": "text/template/parse",
- "parse.NodeIdentifier": "text/template/parse",
- "parse.NodeIf": "text/template/parse",
- "parse.NodeList": "text/template/parse",
- "parse.NodeNil": "text/template/parse",
- "parse.NodeNumber": "text/template/parse",
- "parse.NodePipe": "text/template/parse",
- "parse.NodeRange": "text/template/parse",
- "parse.NodeString": "text/template/parse",
- "parse.NodeTemplate": "text/template/parse",
- "parse.NodeText": "text/template/parse",
- "parse.NodeType": "text/template/parse",
- "parse.NodeVariable": "text/template/parse",
- "parse.NodeWith": "text/template/parse",
- "parse.NumberNode": "text/template/parse",
- "parse.Parse": "text/template/parse",
- "parse.PipeNode": "text/template/parse",
- "parse.Pos": "text/template/parse",
- "parse.RangeNode": "text/template/parse",
- "parse.StringNode": "text/template/parse",
- "parse.TemplateNode": "text/template/parse",
- "parse.TextNode": "text/template/parse",
- "parse.Tree": "text/template/parse",
- "parse.VariableNode": "text/template/parse",
- "parse.WithNode": "text/template/parse",
- "parser.AllErrors": "go/parser",
- "parser.DeclarationErrors": "go/parser",
- "parser.ImportsOnly": "go/parser",
- "parser.Mode": "go/parser",
- "parser.PackageClauseOnly": "go/parser",
- "parser.ParseComments": "go/parser",
- "parser.ParseDir": "go/parser",
- "parser.ParseExpr": "go/parser",
- "parser.ParseFile": "go/parser",
- "parser.SpuriousErrors": "go/parser",
- "parser.Trace": "go/parser",
- "path.Base": "path",
- "path.Clean": "path",
- "path.Dir": "path",
- "path.ErrBadPattern": "path",
- "path.Ext": "path",
- "path.IsAbs": "path",
- "path.Join": "path",
- "path.Match": "path",
- "path.Split": "path",
- "pe.COFFSymbol": "debug/pe",
- "pe.COFFSymbolSize": "debug/pe",
- "pe.File": "debug/pe",
- "pe.FileHeader": "debug/pe",
- "pe.FormatError": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_AM33": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_AMD64": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_ARM": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_EBC": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_I386": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_IA64": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_M32R": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_MIPS16": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_MIPSFPU": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_MIPSFPU16": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_POWERPC": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_POWERPCFP": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_R4000": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_SH3": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_SH3DSP": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_SH4": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_SH5": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_THUMB": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_UNKNOWN": "debug/pe",
- "pe.IMAGE_FILE_MACHINE_WCEMIPSV2": "debug/pe",
- "pe.ImportDirectory": "debug/pe",
- "pe.NewFile": "debug/pe",
- "pe.Open": "debug/pe",
- "pe.Section": "debug/pe",
- "pe.SectionHeader": "debug/pe",
- "pe.SectionHeader32": "debug/pe",
- "pe.Symbol": "debug/pe",
- "pem.Block": "encoding/pem",
- "pem.Decode": "encoding/pem",
- "pem.Encode": "encoding/pem",
- "pem.EncodeToMemory": "encoding/pem",
- "pkix.AlgorithmIdentifier": "crypto/x509/pkix",
- "pkix.AttributeTypeAndValue": "crypto/x509/pkix",
- "pkix.CertificateList": "crypto/x509/pkix",
- "pkix.Extension": "crypto/x509/pkix",
- "pkix.Name": "crypto/x509/pkix",
- "pkix.RDNSequence": "crypto/x509/pkix",
- "pkix.RelativeDistinguishedNameSET": "crypto/x509/pkix",
- "pkix.RevokedCertificate": "crypto/x509/pkix",
- "pkix.TBSCertificateList": "crypto/x509/pkix",
- "png.Decode": "image/png",
- "png.DecodeConfig": "image/png",
- "png.Encode": "image/png",
- "png.FormatError": "image/png",
- "png.UnsupportedError": "image/png",
- "pprof.Cmdline": "net/http/pprof",
- "pprof.Handler": "net/http/pprof",
- "pprof.Index": "net/http/pprof",
- "pprof.Lookup": "runtime/pprof",
- "pprof.NewProfile": "runtime/pprof",
+ "adler32.Checksum": "hash/adler32",
+ "adler32.New": "hash/adler32",
+ "adler32.Size": "hash/adler32",
+ "aes.BlockSize": "crypto/aes",
+ "aes.KeySizeError": "crypto/aes",
+ "aes.NewCipher": "crypto/aes",
+ "ascii85.CorruptInputError": "encoding/ascii85",
+ "ascii85.Decode": "encoding/ascii85",
+ "ascii85.Encode": "encoding/ascii85",
+ "ascii85.MaxEncodedLen": "encoding/ascii85",
+ "ascii85.NewDecoder": "encoding/ascii85",
+ "ascii85.NewEncoder": "encoding/ascii85",
+ "asn1.BitString": "encoding/asn1",
+ "asn1.Enumerated": "encoding/asn1",
+ "asn1.Flag": "encoding/asn1",
+ "asn1.Marshal": "encoding/asn1",
+ "asn1.ObjectIdentifier": "encoding/asn1",
+ "asn1.RawContent": "encoding/asn1",
+ "asn1.RawValue": "encoding/asn1",
+ "asn1.StructuralError": "encoding/asn1",
+ "asn1.SyntaxError": "encoding/asn1",
+ "asn1.Unmarshal": "encoding/asn1",
+ "asn1.UnmarshalWithParams": "encoding/asn1",
+ "ast.ArrayType": "go/ast",
+ "ast.AssignStmt": "go/ast",
+ "ast.Bad": "go/ast",
+ "ast.BadDecl": "go/ast",
+ "ast.BadExpr": "go/ast",
+ "ast.BadStmt": "go/ast",
+ "ast.BasicLit": "go/ast",
+ "ast.BinaryExpr": "go/ast",
+ "ast.BlockStmt": "go/ast",
+ "ast.BranchStmt": "go/ast",
+ "ast.CallExpr": "go/ast",
+ "ast.CaseClause": "go/ast",
+ "ast.ChanDir": "go/ast",
+ "ast.ChanType": "go/ast",
+ "ast.CommClause": "go/ast",
+ "ast.Comment": "go/ast",
+ "ast.CommentGroup": "go/ast",
+ "ast.CommentMap": "go/ast",
+ "ast.CompositeLit": "go/ast",
+ "ast.Con": "go/ast",
+ "ast.DeclStmt": "go/ast",
+ "ast.DeferStmt": "go/ast",
+ "ast.Ellipsis": "go/ast",
+ "ast.EmptyStmt": "go/ast",
+ "ast.ExprStmt": "go/ast",
+ "ast.Field": "go/ast",
+ "ast.FieldFilter": "go/ast",
+ "ast.FieldList": "go/ast",
+ "ast.File": "go/ast",
+ "ast.FileExports": "go/ast",
+ "ast.Filter": "go/ast",
+ "ast.FilterDecl": "go/ast",
+ "ast.FilterFile": "go/ast",
+ "ast.FilterFuncDuplicates": "go/ast",
+ "ast.FilterImportDuplicates": "go/ast",
+ "ast.FilterPackage": "go/ast",
+ "ast.FilterUnassociatedComments": "go/ast",
+ "ast.ForStmt": "go/ast",
+ "ast.Fprint": "go/ast",
+ "ast.Fun": "go/ast",
+ "ast.FuncDecl": "go/ast",
+ "ast.FuncLit": "go/ast",
+ "ast.FuncType": "go/ast",
+ "ast.GenDecl": "go/ast",
+ "ast.GoStmt": "go/ast",
+ "ast.Ident": "go/ast",
+ "ast.IfStmt": "go/ast",
+ "ast.ImportSpec": "go/ast",
+ "ast.Importer": "go/ast",
+ "ast.IncDecStmt": "go/ast",
+ "ast.IndexExpr": "go/ast",
+ "ast.Inspect": "go/ast",
+ "ast.InterfaceType": "go/ast",
+ "ast.IsExported": "go/ast",
+ "ast.KeyValueExpr": "go/ast",
+ "ast.LabeledStmt": "go/ast",
+ "ast.Lbl": "go/ast",
+ "ast.MapType": "go/ast",
+ "ast.MergeMode": "go/ast",
+ "ast.MergePackageFiles": "go/ast",
+ "ast.NewCommentMap": "go/ast",
+ "ast.NewIdent": "go/ast",
+ "ast.NewObj": "go/ast",
+ "ast.NewPackage": "go/ast",
+ "ast.NewScope": "go/ast",
+ "ast.Node": "go/ast",
+ "ast.NotNilFilter": "go/ast",
+ "ast.ObjKind": "go/ast",
+ "ast.Object": "go/ast",
+ "ast.Package": "go/ast",
+ "ast.PackageExports": "go/ast",
+ "ast.ParenExpr": "go/ast",
+ "ast.Pkg": "go/ast",
+ "ast.Print": "go/ast",
+ "ast.RECV": "go/ast",
+ "ast.RangeStmt": "go/ast",
+ "ast.ReturnStmt": "go/ast",
+ "ast.SEND": "go/ast",
+ "ast.Scope": "go/ast",
+ "ast.SelectStmt": "go/ast",
+ "ast.SelectorExpr": "go/ast",
+ "ast.SendStmt": "go/ast",
+ "ast.SliceExpr": "go/ast",
+ "ast.SortImports": "go/ast",
+ "ast.StarExpr": "go/ast",
+ "ast.StructType": "go/ast",
+ "ast.SwitchStmt": "go/ast",
+ "ast.Typ": "go/ast",
+ "ast.TypeAssertExpr": "go/ast",
+ "ast.TypeSpec": "go/ast",
+ "ast.TypeSwitchStmt": "go/ast",
+ "ast.UnaryExpr": "go/ast",
+ "ast.ValueSpec": "go/ast",
+ "ast.Var": "go/ast",
+ "ast.Visitor": "go/ast",
+ "ast.Walk": "go/ast",
+ "atomic.AddInt32": "sync/atomic",
+ "atomic.AddInt64": "sync/atomic",
+ "atomic.AddUint32": "sync/atomic",
+ "atomic.AddUint64": "sync/atomic",
+ "atomic.AddUintptr": "sync/atomic",
+ "atomic.CompareAndSwapInt32": "sync/atomic",
+ "atomic.CompareAndSwapInt64": "sync/atomic",
+ "atomic.CompareAndSwapPointer": "sync/atomic",
+ "atomic.CompareAndSwapUint32": "sync/atomic",
+ "atomic.CompareAndSwapUint64": "sync/atomic",
+ "atomic.CompareAndSwapUintptr": "sync/atomic",
+ "atomic.LoadInt32": "sync/atomic",
+ "atomic.LoadInt64": "sync/atomic",
+ "atomic.LoadPointer": "sync/atomic",
+ "atomic.LoadUint32": "sync/atomic",
+ "atomic.LoadUint64": "sync/atomic",
+ "atomic.LoadUintptr": "sync/atomic",
+ "atomic.StoreInt32": "sync/atomic",
+ "atomic.StoreInt64": "sync/atomic",
+ "atomic.StorePointer": "sync/atomic",
+ "atomic.StoreUint32": "sync/atomic",
+ "atomic.StoreUint64": "sync/atomic",
+ "atomic.StoreUintptr": "sync/atomic",
+ "atomic.SwapInt32": "sync/atomic",
+ "atomic.SwapInt64": "sync/atomic",
+ "atomic.SwapPointer": "sync/atomic",
+ "atomic.SwapUint32": "sync/atomic",
+ "atomic.SwapUint64": "sync/atomic",
+ "atomic.SwapUintptr": "sync/atomic",
+ "atomic.Value": "sync/atomic",
+ "base32.CorruptInputError": "encoding/base32",
+ "base32.Encoding": "encoding/base32",
+ "base32.HexEncoding": "encoding/base32",
+ "base32.NewDecoder": "encoding/base32",
+ "base32.NewEncoder": "encoding/base32",
+ "base32.NewEncoding": "encoding/base32",
+ "base32.StdEncoding": "encoding/base32",
+ "base64.CorruptInputError": "encoding/base64",
+ "base64.Encoding": "encoding/base64",
+ "base64.NewDecoder": "encoding/base64",
+ "base64.NewEncoder": "encoding/base64",
+ "base64.NewEncoding": "encoding/base64",
+ "base64.NoPadding": "encoding/base64",
+ "base64.RawStdEncoding": "encoding/base64",
+ "base64.RawURLEncoding": "encoding/base64",
+ "base64.StdEncoding": "encoding/base64",
+ "base64.StdPadding": "encoding/base64",
+ "base64.URLEncoding": "encoding/base64",
+ "big.Above": "math/big",
+ "big.Accuracy": "math/big",
+ "big.AwayFromZero": "math/big",
+ "big.Below": "math/big",
+ "big.ErrNaN": "math/big",
+ "big.Exact": "math/big",
+ "big.Float": "math/big",
+ "big.Int": "math/big",
+ "big.Jacobi": "math/big",
+ "big.MaxBase": "math/big",
+ "big.MaxExp": "math/big",
+ "big.MaxPrec": "math/big",
+ "big.MinExp": "math/big",
+ "big.NewFloat": "math/big",
+ "big.NewInt": "math/big",
+ "big.NewRat": "math/big",
+ "big.ParseFloat": "math/big",
+ "big.Rat": "math/big",
+ "big.RoundingMode": "math/big",
+ "big.ToNearestAway": "math/big",
+ "big.ToNearestEven": "math/big",
+ "big.ToNegativeInf": "math/big",
+ "big.ToPositiveInf": "math/big",
+ "big.ToZero": "math/big",
+ "big.Word": "math/big",
+ "binary.BigEndian": "encoding/binary",
+ "binary.ByteOrder": "encoding/binary",
+ "binary.LittleEndian": "encoding/binary",
+ "binary.MaxVarintLen16": "encoding/binary",
+ "binary.MaxVarintLen32": "encoding/binary",
+ "binary.MaxVarintLen64": "encoding/binary",
+ "binary.PutUvarint": "encoding/binary",
+ "binary.PutVarint": "encoding/binary",
+ "binary.Read": "encoding/binary",
+ "binary.ReadUvarint": "encoding/binary",
+ "binary.ReadVarint": "encoding/binary",
+ "binary.Size": "encoding/binary",
+ "binary.Uvarint": "encoding/binary",
+ "binary.Varint": "encoding/binary",
+ "binary.Write": "encoding/binary",
+ "bufio.ErrAdvanceTooFar": "bufio",
+ "bufio.ErrBufferFull": "bufio",
+ "bufio.ErrInvalidUnreadByte": "bufio",
+ "bufio.ErrInvalidUnreadRune": "bufio",
+ "bufio.ErrNegativeAdvance": "bufio",
+ "bufio.ErrNegativeCount": "bufio",
+ "bufio.ErrTooLong": "bufio",
+ "bufio.MaxScanTokenSize": "bufio",
+ "bufio.NewReadWriter": "bufio",
+ "bufio.NewReader": "bufio",
+ "bufio.NewReaderSize": "bufio",
+ "bufio.NewScanner": "bufio",
+ "bufio.NewWriter": "bufio",
+ "bufio.NewWriterSize": "bufio",
+ "bufio.ReadWriter": "bufio",
+ "bufio.Reader": "bufio",
+ "bufio.ScanBytes": "bufio",
+ "bufio.ScanLines": "bufio",
+ "bufio.ScanRunes": "bufio",
+ "bufio.ScanWords": "bufio",
+ "bufio.Scanner": "bufio",
+ "bufio.SplitFunc": "bufio",
+ "bufio.Writer": "bufio",
+ "build.AllowBinary": "go/build",
+ "build.ArchChar": "go/build",
+ "build.Context": "go/build",
+ "build.Default": "go/build",
+ "build.FindOnly": "go/build",
+ "build.Import": "go/build",
+ "build.ImportComment": "go/build",
+ "build.ImportDir": "go/build",
+ "build.ImportMode": "go/build",
+ "build.IsLocalImport": "go/build",
+ "build.MultiplePackageError": "go/build",
+ "build.NoGoError": "go/build",
+ "build.Package": "go/build",
+ "build.ToolDir": "go/build",
+ "bytes.Buffer": "bytes",
+ "bytes.Compare": "bytes",
+ "bytes.Contains": "bytes",
+ "bytes.Count": "bytes",
+ "bytes.Equal": "bytes",
+ "bytes.EqualFold": "bytes",
+ "bytes.ErrTooLarge": "bytes",
+ "bytes.Fields": "bytes",
+ "bytes.FieldsFunc": "bytes",
+ "bytes.HasPrefix": "bytes",
+ "bytes.HasSuffix": "bytes",
+ "bytes.Index": "bytes",
+ "bytes.IndexAny": "bytes",
+ "bytes.IndexByte": "bytes",
+ "bytes.IndexFunc": "bytes",
+ "bytes.IndexRune": "bytes",
+ "bytes.Join": "bytes",
+ "bytes.LastIndex": "bytes",
+ "bytes.LastIndexAny": "bytes",
+ "bytes.LastIndexByte": "bytes",
+ "bytes.LastIndexFunc": "bytes",
+ "bytes.Map": "bytes",
+ "bytes.MinRead": "bytes",
+ "bytes.NewBuffer": "bytes",
+ "bytes.NewBufferString": "bytes",
+ "bytes.NewReader": "bytes",
+ "bytes.Reader": "bytes",
+ "bytes.Repeat": "bytes",
+ "bytes.Replace": "bytes",
+ "bytes.Runes": "bytes",
+ "bytes.Split": "bytes",
+ "bytes.SplitAfter": "bytes",
+ "bytes.SplitAfterN": "bytes",
+ "bytes.SplitN": "bytes",
+ "bytes.Title": "bytes",
+ "bytes.ToLower": "bytes",
+ "bytes.ToLowerSpecial": "bytes",
+ "bytes.ToTitle": "bytes",
+ "bytes.ToTitleSpecial": "bytes",
+ "bytes.ToUpper": "bytes",
+ "bytes.ToUpperSpecial": "bytes",
+ "bytes.Trim": "bytes",
+ "bytes.TrimFunc": "bytes",
+ "bytes.TrimLeft": "bytes",
+ "bytes.TrimLeftFunc": "bytes",
+ "bytes.TrimPrefix": "bytes",
+ "bytes.TrimRight": "bytes",
+ "bytes.TrimRightFunc": "bytes",
+ "bytes.TrimSpace": "bytes",
+ "bytes.TrimSuffix": "bytes",
+ "bzip2.NewReader": "compress/bzip2",
+ "bzip2.StructuralError": "compress/bzip2",
+ "cgi.Handler": "net/http/cgi",
+ "cgi.Request": "net/http/cgi",
+ "cgi.RequestFromMap": "net/http/cgi",
+ "cgi.Serve": "net/http/cgi",
+ "cipher.AEAD": "crypto/cipher",
+ "cipher.Block": "crypto/cipher",
+ "cipher.BlockMode": "crypto/cipher",
+ "cipher.NewCBCDecrypter": "crypto/cipher",
+ "cipher.NewCBCEncrypter": "crypto/cipher",
+ "cipher.NewCFBDecrypter": "crypto/cipher",
+ "cipher.NewCFBEncrypter": "crypto/cipher",
+ "cipher.NewCTR": "crypto/cipher",
+ "cipher.NewGCM": "crypto/cipher",
+ "cipher.NewGCMWithNonceSize": "crypto/cipher",
+ "cipher.NewOFB": "crypto/cipher",
+ "cipher.Stream": "crypto/cipher",
+ "cipher.StreamReader": "crypto/cipher",
+ "cipher.StreamWriter": "crypto/cipher",
+ "cmplx.Abs": "math/cmplx",
+ "cmplx.Acos": "math/cmplx",
+ "cmplx.Acosh": "math/cmplx",
+ "cmplx.Asin": "math/cmplx",
+ "cmplx.Asinh": "math/cmplx",
+ "cmplx.Atan": "math/cmplx",
+ "cmplx.Atanh": "math/cmplx",
+ "cmplx.Conj": "math/cmplx",
+ "cmplx.Cos": "math/cmplx",
+ "cmplx.Cosh": "math/cmplx",
+ "cmplx.Cot": "math/cmplx",
+ "cmplx.Exp": "math/cmplx",
+ "cmplx.Inf": "math/cmplx",
+ "cmplx.IsInf": "math/cmplx",
+ "cmplx.IsNaN": "math/cmplx",
+ "cmplx.Log": "math/cmplx",
+ "cmplx.Log10": "math/cmplx",
+ "cmplx.NaN": "math/cmplx",
+ "cmplx.Phase": "math/cmplx",
+ "cmplx.Polar": "math/cmplx",
+ "cmplx.Pow": "math/cmplx",
+ "cmplx.Rect": "math/cmplx",
+ "cmplx.Sin": "math/cmplx",
+ "cmplx.Sinh": "math/cmplx",
+ "cmplx.Sqrt": "math/cmplx",
+ "cmplx.Tan": "math/cmplx",
+ "cmplx.Tanh": "math/cmplx",
+ "color.Alpha": "image/color",
+ "color.Alpha16": "image/color",
+ "color.Alpha16Model": "image/color",
+ "color.AlphaModel": "image/color",
+ "color.Black": "image/color",
+ "color.CMYK": "image/color",
+ "color.CMYKModel": "image/color",
+ "color.CMYKToRGB": "image/color",
+ "color.Color": "image/color",
+ "color.Gray": "image/color",
+ "color.Gray16": "image/color",
+ "color.Gray16Model": "image/color",
+ "color.GrayModel": "image/color",
+ "color.Model": "image/color",
+ "color.ModelFunc": "image/color",
+ "color.NRGBA": "image/color",
+ "color.NRGBA64": "image/color",
+ "color.NRGBA64Model": "image/color",
+ "color.NRGBAModel": "image/color",
+ "color.Opaque": "image/color",
+ "color.Palette": "image/color",
+ "color.RGBA": "image/color",
+ "color.RGBA64": "image/color",
+ "color.RGBA64Model": "image/color",
+ "color.RGBAModel": "image/color",
+ "color.RGBToCMYK": "image/color",
+ "color.RGBToYCbCr": "image/color",
+ "color.Transparent": "image/color",
+ "color.White": "image/color",
+ "color.YCbCr": "image/color",
+ "color.YCbCrModel": "image/color",
+ "color.YCbCrToRGB": "image/color",
+ "constant.BinaryOp": "go/constant",
+ "constant.BitLen": "go/constant",
+ "constant.Bool": "go/constant",
+ "constant.BoolVal": "go/constant",
+ "constant.Bytes": "go/constant",
+ "constant.Compare": "go/constant",
+ "constant.Complex": "go/constant",
+ "constant.Denom": "go/constant",
+ "constant.Float": "go/constant",
+ "constant.Float32Val": "go/constant",
+ "constant.Float64Val": "go/constant",
+ "constant.Imag": "go/constant",
+ "constant.Int": "go/constant",
+ "constant.Int64Val": "go/constant",
+ "constant.Kind": "go/constant",
+ "constant.MakeBool": "go/constant",
+ "constant.MakeFloat64": "go/constant",
+ "constant.MakeFromBytes": "go/constant",
+ "constant.MakeFromLiteral": "go/constant",
+ "constant.MakeImag": "go/constant",
+ "constant.MakeInt64": "go/constant",
+ "constant.MakeString": "go/constant",
+ "constant.MakeUint64": "go/constant",
+ "constant.MakeUnknown": "go/constant",
+ "constant.Num": "go/constant",
+ "constant.Real": "go/constant",
+ "constant.Shift": "go/constant",
+ "constant.Sign": "go/constant",
+ "constant.String": "go/constant",
+ "constant.StringVal": "go/constant",
+ "constant.Uint64Val": "go/constant",
+ "constant.UnaryOp": "go/constant",
+ "constant.Unknown": "go/constant",
+ "cookiejar.Jar": "net/http/cookiejar",
+ "cookiejar.New": "net/http/cookiejar",
+ "cookiejar.Options": "net/http/cookiejar",
+ "cookiejar.PublicSuffixList": "net/http/cookiejar",
+ "crc32.Castagnoli": "hash/crc32",
+ "crc32.Checksum": "hash/crc32",
+ "crc32.ChecksumIEEE": "hash/crc32",
+ "crc32.IEEE": "hash/crc32",
+ "crc32.IEEETable": "hash/crc32",
+ "crc32.Koopman": "hash/crc32",
+ "crc32.MakeTable": "hash/crc32",
+ "crc32.New": "hash/crc32",
+ "crc32.NewIEEE": "hash/crc32",
+ "crc32.Size": "hash/crc32",
+ "crc32.Table": "hash/crc32",
+ "crc32.Update": "hash/crc32",
+ "crc64.Checksum": "hash/crc64",
+ "crc64.ECMA": "hash/crc64",
+ "crc64.ISO": "hash/crc64",
+ "crc64.MakeTable": "hash/crc64",
+ "crc64.New": "hash/crc64",
+ "crc64.Size": "hash/crc64",
+ "crc64.Table": "hash/crc64",
+ "crc64.Update": "hash/crc64",
+ "crypto.Decrypter": "crypto",
+ "crypto.DecrypterOpts": "crypto",
+ "crypto.Hash": "crypto",
+ "crypto.MD4": "crypto",
+ "crypto.MD5": "crypto",
+ "crypto.MD5SHA1": "crypto",
+ "crypto.PrivateKey": "crypto",
+ "crypto.PublicKey": "crypto",
+ "crypto.RIPEMD160": "crypto",
+ "crypto.RegisterHash": "crypto",
+ "crypto.SHA1": "crypto",
+ "crypto.SHA224": "crypto",
+ "crypto.SHA256": "crypto",
+ "crypto.SHA384": "crypto",
+ "crypto.SHA3_224": "crypto",
+ "crypto.SHA3_256": "crypto",
+ "crypto.SHA3_384": "crypto",
+ "crypto.SHA3_512": "crypto",
+ "crypto.SHA512": "crypto",
+ "crypto.SHA512_224": "crypto",
+ "crypto.SHA512_256": "crypto",
+ "crypto.Signer": "crypto",
+ "crypto.SignerOpts": "crypto",
+ "csv.ErrBareQuote": "encoding/csv",
+ "csv.ErrFieldCount": "encoding/csv",
+ "csv.ErrQuote": "encoding/csv",
+ "csv.ErrTrailingComma": "encoding/csv",
+ "csv.NewReader": "encoding/csv",
+ "csv.NewWriter": "encoding/csv",
+ "csv.ParseError": "encoding/csv",
+ "csv.Reader": "encoding/csv",
+ "csv.Writer": "encoding/csv",
+ "debug.FreeOSMemory": "runtime/debug",
+ "debug.GCStats": "runtime/debug",
+ "debug.PrintStack": "runtime/debug",
+ "debug.ReadGCStats": "runtime/debug",
+ "debug.SetGCPercent": "runtime/debug",
+ "debug.SetMaxStack": "runtime/debug",
+ "debug.SetMaxThreads": "runtime/debug",
+ "debug.SetPanicOnFault": "runtime/debug",
+ "debug.Stack": "runtime/debug",
+ "debug.WriteHeapDump": "runtime/debug",
+ "des.BlockSize": "crypto/des",
+ "des.KeySizeError": "crypto/des",
+ "des.NewCipher": "crypto/des",
+ "des.NewTripleDESCipher": "crypto/des",
+ "doc.AllDecls": "go/doc",
+ "doc.AllMethods": "go/doc",
+ "doc.Example": "go/doc",
+ "doc.Examples": "go/doc",
+ "doc.Filter": "go/doc",
+ "doc.Func": "go/doc",
+ "doc.IllegalPrefixes": "go/doc",
+ "doc.Mode": "go/doc",
+ "doc.New": "go/doc",
+ "doc.Note": "go/doc",
+ "doc.Package": "go/doc",
+ "doc.Synopsis": "go/doc",
+ "doc.ToHTML": "go/doc",
+ "doc.ToText": "go/doc",
+ "doc.Type": "go/doc",
+ "doc.Value": "go/doc",
+ "draw.Draw": "image/draw",
+ "draw.DrawMask": "image/draw",
+ "draw.Drawer": "image/draw",
+ "draw.FloydSteinberg": "image/draw",
+ "draw.Image": "image/draw",
+ "draw.Op": "image/draw",
+ "draw.Over": "image/draw",
+ "draw.Quantizer": "image/draw",
+ "draw.Src": "image/draw",
+ "driver.Bool": "database/sql/driver",
+ "driver.ColumnConverter": "database/sql/driver",
+ "driver.Conn": "database/sql/driver",
+ "driver.DefaultParameterConverter": "database/sql/driver",
+ "driver.Driver": "database/sql/driver",
+ "driver.ErrBadConn": "database/sql/driver",
+ "driver.ErrSkip": "database/sql/driver",
+ "driver.Execer": "database/sql/driver",
+ "driver.Int32": "database/sql/driver",
+ "driver.IsScanValue": "database/sql/driver",
+ "driver.IsValue": "database/sql/driver",
+ "driver.NotNull": "database/sql/driver",
+ "driver.Null": "database/sql/driver",
+ "driver.Queryer": "database/sql/driver",
+ "driver.Result": "database/sql/driver",
+ "driver.ResultNoRows": "database/sql/driver",
+ "driver.Rows": "database/sql/driver",
+ "driver.RowsAffected": "database/sql/driver",
+ "driver.Stmt": "database/sql/driver",
+ "driver.String": "database/sql/driver",
+ "driver.Tx": "database/sql/driver",
+ "driver.Value": "database/sql/driver",
+ "driver.ValueConverter": "database/sql/driver",
+ "driver.Valuer": "database/sql/driver",
+ "dsa.ErrInvalidPublicKey": "crypto/dsa",
+ "dsa.GenerateKey": "crypto/dsa",
+ "dsa.GenerateParameters": "crypto/dsa",
+ "dsa.L1024N160": "crypto/dsa",
+ "dsa.L2048N224": "crypto/dsa",
+ "dsa.L2048N256": "crypto/dsa",
+ "dsa.L3072N256": "crypto/dsa",
+ "dsa.ParameterSizes": "crypto/dsa",
+ "dsa.Parameters": "crypto/dsa",
+ "dsa.PrivateKey": "crypto/dsa",
+ "dsa.PublicKey": "crypto/dsa",
+ "dsa.Sign": "crypto/dsa",
+ "dsa.Verify": "crypto/dsa",
+ "dwarf.AddrType": "debug/dwarf",
+ "dwarf.ArrayType": "debug/dwarf",
+ "dwarf.Attr": "debug/dwarf",
+ "dwarf.AttrAbstractOrigin": "debug/dwarf",
+ "dwarf.AttrAccessibility": "debug/dwarf",
+ "dwarf.AttrAddrClass": "debug/dwarf",
+ "dwarf.AttrAllocated": "debug/dwarf",
+ "dwarf.AttrArtificial": "debug/dwarf",
+ "dwarf.AttrAssociated": "debug/dwarf",
+ "dwarf.AttrBaseTypes": "debug/dwarf",
+ "dwarf.AttrBitOffset": "debug/dwarf",
+ "dwarf.AttrBitSize": "debug/dwarf",
+ "dwarf.AttrByteSize": "debug/dwarf",
+ "dwarf.AttrCallColumn": "debug/dwarf",
+ "dwarf.AttrCallFile": "debug/dwarf",
+ "dwarf.AttrCallLine": "debug/dwarf",
+ "dwarf.AttrCalling": "debug/dwarf",
+ "dwarf.AttrCommonRef": "debug/dwarf",
+ "dwarf.AttrCompDir": "debug/dwarf",
+ "dwarf.AttrConstValue": "debug/dwarf",
+ "dwarf.AttrContainingType": "debug/dwarf",
+ "dwarf.AttrCount": "debug/dwarf",
+ "dwarf.AttrDataLocation": "debug/dwarf",
+ "dwarf.AttrDataMemberLoc": "debug/dwarf",
+ "dwarf.AttrDeclColumn": "debug/dwarf",
+ "dwarf.AttrDeclFile": "debug/dwarf",
+ "dwarf.AttrDeclLine": "debug/dwarf",
+ "dwarf.AttrDeclaration": "debug/dwarf",
+ "dwarf.AttrDefaultValue": "debug/dwarf",
+ "dwarf.AttrDescription": "debug/dwarf",
+ "dwarf.AttrDiscr": "debug/dwarf",
+ "dwarf.AttrDiscrList": "debug/dwarf",
+ "dwarf.AttrDiscrValue": "debug/dwarf",
+ "dwarf.AttrEncoding": "debug/dwarf",
+ "dwarf.AttrEntrypc": "debug/dwarf",
+ "dwarf.AttrExtension": "debug/dwarf",
+ "dwarf.AttrExternal": "debug/dwarf",
+ "dwarf.AttrFrameBase": "debug/dwarf",
+ "dwarf.AttrFriend": "debug/dwarf",
+ "dwarf.AttrHighpc": "debug/dwarf",
+ "dwarf.AttrIdentifierCase": "debug/dwarf",
+ "dwarf.AttrImport": "debug/dwarf",
+ "dwarf.AttrInline": "debug/dwarf",
+ "dwarf.AttrIsOptional": "debug/dwarf",
+ "dwarf.AttrLanguage": "debug/dwarf",
+ "dwarf.AttrLocation": "debug/dwarf",
+ "dwarf.AttrLowerBound": "debug/dwarf",
+ "dwarf.AttrLowpc": "debug/dwarf",
+ "dwarf.AttrMacroInfo": "debug/dwarf",
+ "dwarf.AttrName": "debug/dwarf",
+ "dwarf.AttrNamelistItem": "debug/dwarf",
+ "dwarf.AttrOrdering": "debug/dwarf",
+ "dwarf.AttrPriority": "debug/dwarf",
+ "dwarf.AttrProducer": "debug/dwarf",
+ "dwarf.AttrPrototyped": "debug/dwarf",
+ "dwarf.AttrRanges": "debug/dwarf",
+ "dwarf.AttrReturnAddr": "debug/dwarf",
+ "dwarf.AttrSegment": "debug/dwarf",
+ "dwarf.AttrSibling": "debug/dwarf",
+ "dwarf.AttrSpecification": "debug/dwarf",
+ "dwarf.AttrStartScope": "debug/dwarf",
+ "dwarf.AttrStaticLink": "debug/dwarf",
+ "dwarf.AttrStmtList": "debug/dwarf",
+ "dwarf.AttrStride": "debug/dwarf",
+ "dwarf.AttrStrideSize": "debug/dwarf",
+ "dwarf.AttrStringLength": "debug/dwarf",
+ "dwarf.AttrTrampoline": "debug/dwarf",
+ "dwarf.AttrType": "debug/dwarf",
+ "dwarf.AttrUpperBound": "debug/dwarf",
+ "dwarf.AttrUseLocation": "debug/dwarf",
+ "dwarf.AttrUseUTF8": "debug/dwarf",
+ "dwarf.AttrVarParam": "debug/dwarf",
+ "dwarf.AttrVirtuality": "debug/dwarf",
+ "dwarf.AttrVisibility": "debug/dwarf",
+ "dwarf.AttrVtableElemLoc": "debug/dwarf",
+ "dwarf.BasicType": "debug/dwarf",
+ "dwarf.BoolType": "debug/dwarf",
+ "dwarf.CharType": "debug/dwarf",
+ "dwarf.Class": "debug/dwarf",
+ "dwarf.ClassAddress": "debug/dwarf",
+ "dwarf.ClassBlock": "debug/dwarf",
+ "dwarf.ClassConstant": "debug/dwarf",
+ "dwarf.ClassExprLoc": "debug/dwarf",
+ "dwarf.ClassFlag": "debug/dwarf",
+ "dwarf.ClassLinePtr": "debug/dwarf",
+ "dwarf.ClassLocListPtr": "debug/dwarf",
+ "dwarf.ClassMacPtr": "debug/dwarf",
+ "dwarf.ClassRangeListPtr": "debug/dwarf",
+ "dwarf.ClassReference": "debug/dwarf",
+ "dwarf.ClassReferenceAlt": "debug/dwarf",
+ "dwarf.ClassReferenceSig": "debug/dwarf",
+ "dwarf.ClassString": "debug/dwarf",
+ "dwarf.ClassStringAlt": "debug/dwarf",
+ "dwarf.CommonType": "debug/dwarf",
+ "dwarf.ComplexType": "debug/dwarf",
+ "dwarf.Data": "debug/dwarf",
+ "dwarf.DecodeError": "debug/dwarf",
+ "dwarf.DotDotDotType": "debug/dwarf",
+ "dwarf.Entry": "debug/dwarf",
+ "dwarf.EnumType": "debug/dwarf",
+ "dwarf.EnumValue": "debug/dwarf",
+ "dwarf.ErrUnknownPC": "debug/dwarf",
+ "dwarf.Field": "debug/dwarf",
+ "dwarf.FloatType": "debug/dwarf",
+ "dwarf.FuncType": "debug/dwarf",
+ "dwarf.IntType": "debug/dwarf",
+ "dwarf.LineEntry": "debug/dwarf",
+ "dwarf.LineFile": "debug/dwarf",
+ "dwarf.LineReader": "debug/dwarf",
+ "dwarf.LineReaderPos": "debug/dwarf",
+ "dwarf.New": "debug/dwarf",
+ "dwarf.Offset": "debug/dwarf",
+ "dwarf.PtrType": "debug/dwarf",
+ "dwarf.QualType": "debug/dwarf",
+ "dwarf.Reader": "debug/dwarf",
+ "dwarf.StructField": "debug/dwarf",
+ "dwarf.StructType": "debug/dwarf",
+ "dwarf.Tag": "debug/dwarf",
+ "dwarf.TagAccessDeclaration": "debug/dwarf",
+ "dwarf.TagArrayType": "debug/dwarf",
+ "dwarf.TagBaseType": "debug/dwarf",
+ "dwarf.TagCatchDwarfBlock": "debug/dwarf",
+ "dwarf.TagClassType": "debug/dwarf",
+ "dwarf.TagCommonDwarfBlock": "debug/dwarf",
+ "dwarf.TagCommonInclusion": "debug/dwarf",
+ "dwarf.TagCompileUnit": "debug/dwarf",
+ "dwarf.TagCondition": "debug/dwarf",
+ "dwarf.TagConstType": "debug/dwarf",
+ "dwarf.TagConstant": "debug/dwarf",
+ "dwarf.TagDwarfProcedure": "debug/dwarf",
+ "dwarf.TagEntryPoint": "debug/dwarf",
+ "dwarf.TagEnumerationType": "debug/dwarf",
+ "dwarf.TagEnumerator": "debug/dwarf",
+ "dwarf.TagFileType": "debug/dwarf",
+ "dwarf.TagFormalParameter": "debug/dwarf",
+ "dwarf.TagFriend": "debug/dwarf",
+ "dwarf.TagImportedDeclaration": "debug/dwarf",
+ "dwarf.TagImportedModule": "debug/dwarf",
+ "dwarf.TagImportedUnit": "debug/dwarf",
+ "dwarf.TagInheritance": "debug/dwarf",
+ "dwarf.TagInlinedSubroutine": "debug/dwarf",
+ "dwarf.TagInterfaceType": "debug/dwarf",
+ "dwarf.TagLabel": "debug/dwarf",
+ "dwarf.TagLexDwarfBlock": "debug/dwarf",
+ "dwarf.TagMember": "debug/dwarf",
+ "dwarf.TagModule": "debug/dwarf",
+ "dwarf.TagMutableType": "debug/dwarf",
+ "dwarf.TagNamelist": "debug/dwarf",
+ "dwarf.TagNamelistItem": "debug/dwarf",
+ "dwarf.TagNamespace": "debug/dwarf",
+ "dwarf.TagPackedType": "debug/dwarf",
+ "dwarf.TagPartialUnit": "debug/dwarf",
+ "dwarf.TagPointerType": "debug/dwarf",
+ "dwarf.TagPtrToMemberType": "debug/dwarf",
+ "dwarf.TagReferenceType": "debug/dwarf",
+ "dwarf.TagRestrictType": "debug/dwarf",
+ "dwarf.TagRvalueReferenceType": "debug/dwarf",
+ "dwarf.TagSetType": "debug/dwarf",
+ "dwarf.TagSharedType": "debug/dwarf",
+ "dwarf.TagStringType": "debug/dwarf",
+ "dwarf.TagStructType": "debug/dwarf",
+ "dwarf.TagSubprogram": "debug/dwarf",
+ "dwarf.TagSubrangeType": "debug/dwarf",
+ "dwarf.TagSubroutineType": "debug/dwarf",
+ "dwarf.TagTemplateAlias": "debug/dwarf",
+ "dwarf.TagTemplateTypeParameter": "debug/dwarf",
+ "dwarf.TagTemplateValueParameter": "debug/dwarf",
+ "dwarf.TagThrownType": "debug/dwarf",
+ "dwarf.TagTryDwarfBlock": "debug/dwarf",
+ "dwarf.TagTypeUnit": "debug/dwarf",
+ "dwarf.TagTypedef": "debug/dwarf",
+ "dwarf.TagUnionType": "debug/dwarf",
+ "dwarf.TagUnspecifiedParameters": "debug/dwarf",
+ "dwarf.TagUnspecifiedType": "debug/dwarf",
+ "dwarf.TagVariable": "debug/dwarf",
+ "dwarf.TagVariant": "debug/dwarf",
+ "dwarf.TagVariantPart": "debug/dwarf",
+ "dwarf.TagVolatileType": "debug/dwarf",
+ "dwarf.TagWithStmt": "debug/dwarf",
+ "dwarf.Type": "debug/dwarf",
+ "dwarf.TypedefType": "debug/dwarf",
+ "dwarf.UcharType": "debug/dwarf",
+ "dwarf.UintType": "debug/dwarf",
+ "dwarf.UnspecifiedType": "debug/dwarf",
+ "dwarf.VoidType": "debug/dwarf",
+ "ecdsa.GenerateKey": "crypto/ecdsa",
+ "ecdsa.PrivateKey": "crypto/ecdsa",
+ "ecdsa.PublicKey": "crypto/ecdsa",
+ "ecdsa.Sign": "crypto/ecdsa",
+ "ecdsa.Verify": "crypto/ecdsa",
+ "elf.ARM_MAGIC_TRAMP_NUMBER": "debug/elf",
+ "elf.Class": "debug/elf",
+ "elf.DF_BIND_NOW": "debug/elf",
+ "elf.DF_ORIGIN": "debug/elf",
+ "elf.DF_STATIC_TLS": "debug/elf",
+ "elf.DF_SYMBOLIC": "debug/elf",
+ "elf.DF_TEXTREL": "debug/elf",
+ "elf.DT_BIND_NOW": "debug/elf",
+ "elf.DT_DEBUG": "debug/elf",
+ "elf.DT_ENCODING": "debug/elf",
+ "elf.DT_FINI": "debug/elf",
+ "elf.DT_FINI_ARRAY": "debug/elf",
+ "elf.DT_FINI_ARRAYSZ": "debug/elf",
+ "elf.DT_FLAGS": "debug/elf",
+ "elf.DT_HASH": "debug/elf",
+ "elf.DT_HIOS": "debug/elf",
+ "elf.DT_HIPROC": "debug/elf",
+ "elf.DT_INIT": "debug/elf",
+ "elf.DT_INIT_ARRAY": "debug/elf",
+ "elf.DT_INIT_ARRAYSZ": "debug/elf",
+ "elf.DT_JMPREL": "debug/elf",
+ "elf.DT_LOOS": "debug/elf",
+ "elf.DT_LOPROC": "debug/elf",
+ "elf.DT_NEEDED": "debug/elf",
+ "elf.DT_NULL": "debug/elf",
+ "elf.DT_PLTGOT": "debug/elf",
+ "elf.DT_PLTREL": "debug/elf",
+ "elf.DT_PLTRELSZ": "debug/elf",
+ "elf.DT_PREINIT_ARRAY": "debug/elf",
+ "elf.DT_PREINIT_ARRAYSZ": "debug/elf",
+ "elf.DT_REL": "debug/elf",
+ "elf.DT_RELA": "debug/elf",
+ "elf.DT_RELAENT": "debug/elf",
+ "elf.DT_RELASZ": "debug/elf",
+ "elf.DT_RELENT": "debug/elf",
+ "elf.DT_RELSZ": "debug/elf",
+ "elf.DT_RPATH": "debug/elf",
+ "elf.DT_RUNPATH": "debug/elf",
+ "elf.DT_SONAME": "debug/elf",
+ "elf.DT_STRSZ": "debug/elf",
+ "elf.DT_STRTAB": "debug/elf",
+ "elf.DT_SYMBOLIC": "debug/elf",
+ "elf.DT_SYMENT": "debug/elf",
+ "elf.DT_SYMTAB": "debug/elf",
+ "elf.DT_TEXTREL": "debug/elf",
+ "elf.DT_VERNEED": "debug/elf",
+ "elf.DT_VERNEEDNUM": "debug/elf",
+ "elf.DT_VERSYM": "debug/elf",
+ "elf.Data": "debug/elf",
+ "elf.Dyn32": "debug/elf",
+ "elf.Dyn64": "debug/elf",
+ "elf.DynFlag": "debug/elf",
+ "elf.DynTag": "debug/elf",
+ "elf.EI_ABIVERSION": "debug/elf",
+ "elf.EI_CLASS": "debug/elf",
+ "elf.EI_DATA": "debug/elf",
+ "elf.EI_NIDENT": "debug/elf",
+ "elf.EI_OSABI": "debug/elf",
+ "elf.EI_PAD": "debug/elf",
+ "elf.EI_VERSION": "debug/elf",
+ "elf.ELFCLASS32": "debug/elf",
+ "elf.ELFCLASS64": "debug/elf",
+ "elf.ELFCLASSNONE": "debug/elf",
+ "elf.ELFDATA2LSB": "debug/elf",
+ "elf.ELFDATA2MSB": "debug/elf",
+ "elf.ELFDATANONE": "debug/elf",
+ "elf.ELFMAG": "debug/elf",
+ "elf.ELFOSABI_86OPEN": "debug/elf",
+ "elf.ELFOSABI_AIX": "debug/elf",
+ "elf.ELFOSABI_ARM": "debug/elf",
+ "elf.ELFOSABI_FREEBSD": "debug/elf",
+ "elf.ELFOSABI_HPUX": "debug/elf",
+ "elf.ELFOSABI_HURD": "debug/elf",
+ "elf.ELFOSABI_IRIX": "debug/elf",
+ "elf.ELFOSABI_LINUX": "debug/elf",
+ "elf.ELFOSABI_MODESTO": "debug/elf",
+ "elf.ELFOSABI_NETBSD": "debug/elf",
+ "elf.ELFOSABI_NONE": "debug/elf",
+ "elf.ELFOSABI_NSK": "debug/elf",
+ "elf.ELFOSABI_OPENBSD": "debug/elf",
+ "elf.ELFOSABI_OPENVMS": "debug/elf",
+ "elf.ELFOSABI_SOLARIS": "debug/elf",
+ "elf.ELFOSABI_STANDALONE": "debug/elf",
+ "elf.ELFOSABI_TRU64": "debug/elf",
+ "elf.EM_386": "debug/elf",
+ "elf.EM_486": "debug/elf",
+ "elf.EM_68HC12": "debug/elf",
+ "elf.EM_68K": "debug/elf",
+ "elf.EM_860": "debug/elf",
+ "elf.EM_88K": "debug/elf",
+ "elf.EM_960": "debug/elf",
+ "elf.EM_AARCH64": "debug/elf",
+ "elf.EM_ALPHA": "debug/elf",
+ "elf.EM_ALPHA_STD": "debug/elf",
+ "elf.EM_ARC": "debug/elf",
+ "elf.EM_ARM": "debug/elf",
+ "elf.EM_COLDFIRE": "debug/elf",
+ "elf.EM_FR20": "debug/elf",
+ "elf.EM_H8S": "debug/elf",
+ "elf.EM_H8_300": "debug/elf",
+ "elf.EM_H8_300H": "debug/elf",
+ "elf.EM_H8_500": "debug/elf",
+ "elf.EM_IA_64": "debug/elf",
+ "elf.EM_M32": "debug/elf",
+ "elf.EM_ME16": "debug/elf",
+ "elf.EM_MIPS": "debug/elf",
+ "elf.EM_MIPS_RS3_LE": "debug/elf",
+ "elf.EM_MIPS_RS4_BE": "debug/elf",
+ "elf.EM_MIPS_X": "debug/elf",
+ "elf.EM_MMA": "debug/elf",
+ "elf.EM_NCPU": "debug/elf",
+ "elf.EM_NDR1": "debug/elf",
+ "elf.EM_NONE": "debug/elf",
+ "elf.EM_PARISC": "debug/elf",
+ "elf.EM_PCP": "debug/elf",
+ "elf.EM_PPC": "debug/elf",
+ "elf.EM_PPC64": "debug/elf",
+ "elf.EM_RCE": "debug/elf",
+ "elf.EM_RH32": "debug/elf",
+ "elf.EM_S370": "debug/elf",
+ "elf.EM_S390": "debug/elf",
+ "elf.EM_SH": "debug/elf",
+ "elf.EM_SPARC": "debug/elf",
+ "elf.EM_SPARC32PLUS": "debug/elf",
+ "elf.EM_SPARCV9": "debug/elf",
+ "elf.EM_ST100": "debug/elf",
+ "elf.EM_STARCORE": "debug/elf",
+ "elf.EM_TINYJ": "debug/elf",
+ "elf.EM_TRICORE": "debug/elf",
+ "elf.EM_V800": "debug/elf",
+ "elf.EM_VPP500": "debug/elf",
+ "elf.EM_X86_64": "debug/elf",
+ "elf.ET_CORE": "debug/elf",
+ "elf.ET_DYN": "debug/elf",
+ "elf.ET_EXEC": "debug/elf",
+ "elf.ET_HIOS": "debug/elf",
+ "elf.ET_HIPROC": "debug/elf",
+ "elf.ET_LOOS": "debug/elf",
+ "elf.ET_LOPROC": "debug/elf",
+ "elf.ET_NONE": "debug/elf",
+ "elf.ET_REL": "debug/elf",
+ "elf.EV_CURRENT": "debug/elf",
+ "elf.EV_NONE": "debug/elf",
+ "elf.ErrNoSymbols": "debug/elf",
+ "elf.File": "debug/elf",
+ "elf.FileHeader": "debug/elf",
+ "elf.FormatError": "debug/elf",
+ "elf.Header32": "debug/elf",
+ "elf.Header64": "debug/elf",
+ "elf.ImportedSymbol": "debug/elf",
+ "elf.Machine": "debug/elf",
+ "elf.NT_FPREGSET": "debug/elf",
+ "elf.NT_PRPSINFO": "debug/elf",
+ "elf.NT_PRSTATUS": "debug/elf",
+ "elf.NType": "debug/elf",
+ "elf.NewFile": "debug/elf",
+ "elf.OSABI": "debug/elf",
+ "elf.Open": "debug/elf",
+ "elf.PF_MASKOS": "debug/elf",
+ "elf.PF_MASKPROC": "debug/elf",
+ "elf.PF_R": "debug/elf",
+ "elf.PF_W": "debug/elf",
+ "elf.PF_X": "debug/elf",
+ "elf.PT_DYNAMIC": "debug/elf",
+ "elf.PT_HIOS": "debug/elf",
+ "elf.PT_HIPROC": "debug/elf",
+ "elf.PT_INTERP": "debug/elf",
+ "elf.PT_LOAD": "debug/elf",
+ "elf.PT_LOOS": "debug/elf",
+ "elf.PT_LOPROC": "debug/elf",
+ "elf.PT_NOTE": "debug/elf",
+ "elf.PT_NULL": "debug/elf",
+ "elf.PT_PHDR": "debug/elf",
+ "elf.PT_SHLIB": "debug/elf",
+ "elf.PT_TLS": "debug/elf",
+ "elf.Prog": "debug/elf",
+ "elf.Prog32": "debug/elf",
+ "elf.Prog64": "debug/elf",
+ "elf.ProgFlag": "debug/elf",
+ "elf.ProgHeader": "debug/elf",
+ "elf.ProgType": "debug/elf",
+ "elf.R_386": "debug/elf",
+ "elf.R_386_32": "debug/elf",
+ "elf.R_386_COPY": "debug/elf",
+ "elf.R_386_GLOB_DAT": "debug/elf",
+ "elf.R_386_GOT32": "debug/elf",
+ "elf.R_386_GOTOFF": "debug/elf",
+ "elf.R_386_GOTPC": "debug/elf",
+ "elf.R_386_JMP_SLOT": "debug/elf",
+ "elf.R_386_NONE": "debug/elf",
+ "elf.R_386_PC32": "debug/elf",
+ "elf.R_386_PLT32": "debug/elf",
+ "elf.R_386_RELATIVE": "debug/elf",
+ "elf.R_386_TLS_DTPMOD32": "debug/elf",
+ "elf.R_386_TLS_DTPOFF32": "debug/elf",
+ "elf.R_386_TLS_GD": "debug/elf",
+ "elf.R_386_TLS_GD_32": "debug/elf",
+ "elf.R_386_TLS_GD_CALL": "debug/elf",
+ "elf.R_386_TLS_GD_POP": "debug/elf",
+ "elf.R_386_TLS_GD_PUSH": "debug/elf",
+ "elf.R_386_TLS_GOTIE": "debug/elf",
+ "elf.R_386_TLS_IE": "debug/elf",
+ "elf.R_386_TLS_IE_32": "debug/elf",
+ "elf.R_386_TLS_LDM": "debug/elf",
+ "elf.R_386_TLS_LDM_32": "debug/elf",
+ "elf.R_386_TLS_LDM_CALL": "debug/elf",
+ "elf.R_386_TLS_LDM_POP": "debug/elf",
+ "elf.R_386_TLS_LDM_PUSH": "debug/elf",
+ "elf.R_386_TLS_LDO_32": "debug/elf",
+ "elf.R_386_TLS_LE": "debug/elf",
+ "elf.R_386_TLS_LE_32": "debug/elf",
+ "elf.R_386_TLS_TPOFF": "debug/elf",
+ "elf.R_386_TLS_TPOFF32": "debug/elf",
+ "elf.R_AARCH64": "debug/elf",
+ "elf.R_AARCH64_ABS16": "debug/elf",
+ "elf.R_AARCH64_ABS32": "debug/elf",
+ "elf.R_AARCH64_ABS64": "debug/elf",
+ "elf.R_AARCH64_ADD_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_ADR_GOT_PAGE": "debug/elf",
+ "elf.R_AARCH64_ADR_PREL_LO21": "debug/elf",
+ "elf.R_AARCH64_ADR_PREL_PG_HI21": "debug/elf",
+ "elf.R_AARCH64_ADR_PREL_PG_HI21_NC": "debug/elf",
+ "elf.R_AARCH64_CALL26": "debug/elf",
+ "elf.R_AARCH64_CONDBR19": "debug/elf",
+ "elf.R_AARCH64_COPY": "debug/elf",
+ "elf.R_AARCH64_GLOB_DAT": "debug/elf",
+ "elf.R_AARCH64_GOT_LD_PREL19": "debug/elf",
+ "elf.R_AARCH64_IRELATIVE": "debug/elf",
+ "elf.R_AARCH64_JUMP26": "debug/elf",
+ "elf.R_AARCH64_JUMP_SLOT": "debug/elf",
+ "elf.R_AARCH64_LD64_GOT_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_LDST128_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_LDST16_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_LDST32_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_LDST64_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_LDST8_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_LD_PREL_LO19": "debug/elf",
+ "elf.R_AARCH64_MOVW_SABS_G0": "debug/elf",
+ "elf.R_AARCH64_MOVW_SABS_G1": "debug/elf",
+ "elf.R_AARCH64_MOVW_SABS_G2": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G0": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G0_NC": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G1": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G1_NC": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G2": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G2_NC": "debug/elf",
+ "elf.R_AARCH64_MOVW_UABS_G3": "debug/elf",
+ "elf.R_AARCH64_NONE": "debug/elf",
+ "elf.R_AARCH64_NULL": "debug/elf",
+ "elf.R_AARCH64_P32_ABS16": "debug/elf",
+ "elf.R_AARCH64_P32_ABS32": "debug/elf",
+ "elf.R_AARCH64_P32_ADD_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_ADR_GOT_PAGE": "debug/elf",
+ "elf.R_AARCH64_P32_ADR_PREL_LO21": "debug/elf",
+ "elf.R_AARCH64_P32_ADR_PREL_PG_HI21": "debug/elf",
+ "elf.R_AARCH64_P32_CALL26": "debug/elf",
+ "elf.R_AARCH64_P32_CONDBR19": "debug/elf",
+ "elf.R_AARCH64_P32_COPY": "debug/elf",
+ "elf.R_AARCH64_P32_GLOB_DAT": "debug/elf",
+ "elf.R_AARCH64_P32_GOT_LD_PREL19": "debug/elf",
+ "elf.R_AARCH64_P32_IRELATIVE": "debug/elf",
+ "elf.R_AARCH64_P32_JUMP26": "debug/elf",
+ "elf.R_AARCH64_P32_JUMP_SLOT": "debug/elf",
+ "elf.R_AARCH64_P32_LD32_GOT_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_LDST128_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_LDST16_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_LDST32_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_LDST64_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_LDST8_ABS_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_LD_PREL_LO19": "debug/elf",
+ "elf.R_AARCH64_P32_MOVW_SABS_G0": "debug/elf",
+ "elf.R_AARCH64_P32_MOVW_UABS_G0": "debug/elf",
+ "elf.R_AARCH64_P32_MOVW_UABS_G0_NC": "debug/elf",
+ "elf.R_AARCH64_P32_MOVW_UABS_G1": "debug/elf",
+ "elf.R_AARCH64_P32_PREL16": "debug/elf",
+ "elf.R_AARCH64_P32_PREL32": "debug/elf",
+ "elf.R_AARCH64_P32_RELATIVE": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC_ADD_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC_ADR_PAGE21": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC_ADR_PREL21": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC_CALL": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC_LD32_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSDESC_LD_PREL19": "debug/elf",
+ "elf.R_AARCH64_P32_TLSGD_ADD_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSGD_ADR_PAGE21": "debug/elf",
+ "elf.R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21": "debug/elf",
+ "elf.R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19": "debug/elf",
+ "elf.R_AARCH64_P32_TLSLE_ADD_TPREL_HI12": "debug/elf",
+ "elf.R_AARCH64_P32_TLSLE_ADD_TPREL_LO12": "debug/elf",
+ "elf.R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSLE_MOVW_TPREL_G0": "debug/elf",
+ "elf.R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC": "debug/elf",
+ "elf.R_AARCH64_P32_TLSLE_MOVW_TPREL_G1": "debug/elf",
+ "elf.R_AARCH64_P32_TLS_DTPMOD": "debug/elf",
+ "elf.R_AARCH64_P32_TLS_DTPREL": "debug/elf",
+ "elf.R_AARCH64_P32_TLS_TPREL": "debug/elf",
+ "elf.R_AARCH64_P32_TSTBR14": "debug/elf",
+ "elf.R_AARCH64_PREL16": "debug/elf",
+ "elf.R_AARCH64_PREL32": "debug/elf",
+ "elf.R_AARCH64_PREL64": "debug/elf",
+ "elf.R_AARCH64_RELATIVE": "debug/elf",
+ "elf.R_AARCH64_TLSDESC": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_ADD": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_ADD_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_ADR_PAGE21": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_ADR_PREL21": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_CALL": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_LD64_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_LDR": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_LD_PREL19": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_OFF_G0_NC": "debug/elf",
+ "elf.R_AARCH64_TLSDESC_OFF_G1": "debug/elf",
+ "elf.R_AARCH64_TLSGD_ADD_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_TLSGD_ADR_PAGE21": "debug/elf",
+ "elf.R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21": "debug/elf",
+ "elf.R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_TLSIE_LD_GOTTPREL_PREL19": "debug/elf",
+ "elf.R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC": "debug/elf",
+ "elf.R_AARCH64_TLSIE_MOVW_GOTTPREL_G1": "debug/elf",
+ "elf.R_AARCH64_TLSLE_ADD_TPREL_HI12": "debug/elf",
+ "elf.R_AARCH64_TLSLE_ADD_TPREL_LO12": "debug/elf",
+ "elf.R_AARCH64_TLSLE_ADD_TPREL_LO12_NC": "debug/elf",
+ "elf.R_AARCH64_TLSLE_MOVW_TPREL_G0": "debug/elf",
+ "elf.R_AARCH64_TLSLE_MOVW_TPREL_G0_NC": "debug/elf",
+ "elf.R_AARCH64_TLSLE_MOVW_TPREL_G1": "debug/elf",
+ "elf.R_AARCH64_TLSLE_MOVW_TPREL_G1_NC": "debug/elf",
+ "elf.R_AARCH64_TLSLE_MOVW_TPREL_G2": "debug/elf",
+ "elf.R_AARCH64_TLS_DTPMOD64": "debug/elf",
+ "elf.R_AARCH64_TLS_DTPREL64": "debug/elf",
+ "elf.R_AARCH64_TLS_TPREL64": "debug/elf",
+ "elf.R_AARCH64_TSTBR14": "debug/elf",
+ "elf.R_ALPHA": "debug/elf",
+ "elf.R_ALPHA_BRADDR": "debug/elf",
+ "elf.R_ALPHA_COPY": "debug/elf",
+ "elf.R_ALPHA_GLOB_DAT": "debug/elf",
+ "elf.R_ALPHA_GPDISP": "debug/elf",
+ "elf.R_ALPHA_GPREL32": "debug/elf",
+ "elf.R_ALPHA_GPRELHIGH": "debug/elf",
+ "elf.R_ALPHA_GPRELLOW": "debug/elf",
+ "elf.R_ALPHA_GPVALUE": "debug/elf",
+ "elf.R_ALPHA_HINT": "debug/elf",
+ "elf.R_ALPHA_IMMED_BR_HI32": "debug/elf",
+ "elf.R_ALPHA_IMMED_GP_16": "debug/elf",
+ "elf.R_ALPHA_IMMED_GP_HI32": "debug/elf",
+ "elf.R_ALPHA_IMMED_LO32": "debug/elf",
+ "elf.R_ALPHA_IMMED_SCN_HI32": "debug/elf",
+ "elf.R_ALPHA_JMP_SLOT": "debug/elf",
+ "elf.R_ALPHA_LITERAL": "debug/elf",
+ "elf.R_ALPHA_LITUSE": "debug/elf",
+ "elf.R_ALPHA_NONE": "debug/elf",
+ "elf.R_ALPHA_OP_PRSHIFT": "debug/elf",
+ "elf.R_ALPHA_OP_PSUB": "debug/elf",
+ "elf.R_ALPHA_OP_PUSH": "debug/elf",
+ "elf.R_ALPHA_OP_STORE": "debug/elf",
+ "elf.R_ALPHA_REFLONG": "debug/elf",
+ "elf.R_ALPHA_REFQUAD": "debug/elf",
+ "elf.R_ALPHA_RELATIVE": "debug/elf",
+ "elf.R_ALPHA_SREL16": "debug/elf",
+ "elf.R_ALPHA_SREL32": "debug/elf",
+ "elf.R_ALPHA_SREL64": "debug/elf",
+ "elf.R_ARM": "debug/elf",
+ "elf.R_ARM_ABS12": "debug/elf",
+ "elf.R_ARM_ABS16": "debug/elf",
+ "elf.R_ARM_ABS32": "debug/elf",
+ "elf.R_ARM_ABS8": "debug/elf",
+ "elf.R_ARM_AMP_VCALL9": "debug/elf",
+ "elf.R_ARM_COPY": "debug/elf",
+ "elf.R_ARM_GLOB_DAT": "debug/elf",
+ "elf.R_ARM_GNU_VTENTRY": "debug/elf",
+ "elf.R_ARM_GNU_VTINHERIT": "debug/elf",
+ "elf.R_ARM_GOT32": "debug/elf",
+ "elf.R_ARM_GOTOFF": "debug/elf",
+ "elf.R_ARM_GOTPC": "debug/elf",
+ "elf.R_ARM_JUMP_SLOT": "debug/elf",
+ "elf.R_ARM_NONE": "debug/elf",
+ "elf.R_ARM_PC13": "debug/elf",
+ "elf.R_ARM_PC24": "debug/elf",
+ "elf.R_ARM_PLT32": "debug/elf",
+ "elf.R_ARM_RABS32": "debug/elf",
+ "elf.R_ARM_RBASE": "debug/elf",
+ "elf.R_ARM_REL32": "debug/elf",
+ "elf.R_ARM_RELATIVE": "debug/elf",
+ "elf.R_ARM_RPC24": "debug/elf",
+ "elf.R_ARM_RREL32": "debug/elf",
+ "elf.R_ARM_RSBREL32": "debug/elf",
+ "elf.R_ARM_SBREL32": "debug/elf",
+ "elf.R_ARM_SWI24": "debug/elf",
+ "elf.R_ARM_THM_ABS5": "debug/elf",
+ "elf.R_ARM_THM_PC22": "debug/elf",
+ "elf.R_ARM_THM_PC8": "debug/elf",
+ "elf.R_ARM_THM_RPC22": "debug/elf",
+ "elf.R_ARM_THM_SWI8": "debug/elf",
+ "elf.R_ARM_THM_XPC22": "debug/elf",
+ "elf.R_ARM_XPC25": "debug/elf",
+ "elf.R_INFO": "debug/elf",
+ "elf.R_INFO32": "debug/elf",
+ "elf.R_PPC": "debug/elf",
+ "elf.R_PPC64": "debug/elf",
+ "elf.R_PPC64_ADDR14": "debug/elf",
+ "elf.R_PPC64_ADDR14_BRNTAKEN": "debug/elf",
+ "elf.R_PPC64_ADDR14_BRTAKEN": "debug/elf",
+ "elf.R_PPC64_ADDR16": "debug/elf",
+ "elf.R_PPC64_ADDR16_DS": "debug/elf",
+ "elf.R_PPC64_ADDR16_HA": "debug/elf",
+ "elf.R_PPC64_ADDR16_HI": "debug/elf",
+ "elf.R_PPC64_ADDR16_HIGHER": "debug/elf",
+ "elf.R_PPC64_ADDR16_HIGHERA": "debug/elf",
+ "elf.R_PPC64_ADDR16_HIGHEST": "debug/elf",
+ "elf.R_PPC64_ADDR16_HIGHESTA": "debug/elf",
+ "elf.R_PPC64_ADDR16_LO": "debug/elf",
+ "elf.R_PPC64_ADDR16_LO_DS": "debug/elf",
+ "elf.R_PPC64_ADDR24": "debug/elf",
+ "elf.R_PPC64_ADDR32": "debug/elf",
+ "elf.R_PPC64_ADDR64": "debug/elf",
+ "elf.R_PPC64_DTPMOD64": "debug/elf",
+ "elf.R_PPC64_DTPREL16": "debug/elf",
+ "elf.R_PPC64_DTPREL16_DS": "debug/elf",
+ "elf.R_PPC64_DTPREL16_HA": "debug/elf",
+ "elf.R_PPC64_DTPREL16_HI": "debug/elf",
+ "elf.R_PPC64_DTPREL16_HIGHER": "debug/elf",
+ "elf.R_PPC64_DTPREL16_HIGHERA": "debug/elf",
+ "elf.R_PPC64_DTPREL16_HIGHEST": "debug/elf",
+ "elf.R_PPC64_DTPREL16_HIGHESTA": "debug/elf",
+ "elf.R_PPC64_DTPREL16_LO": "debug/elf",
+ "elf.R_PPC64_DTPREL16_LO_DS": "debug/elf",
+ "elf.R_PPC64_DTPREL64": "debug/elf",
+ "elf.R_PPC64_GOT16": "debug/elf",
+ "elf.R_PPC64_GOT16_DS": "debug/elf",
+ "elf.R_PPC64_GOT16_HA": "debug/elf",
+ "elf.R_PPC64_GOT16_HI": "debug/elf",
+ "elf.R_PPC64_GOT16_LO": "debug/elf",
+ "elf.R_PPC64_GOT16_LO_DS": "debug/elf",
+ "elf.R_PPC64_GOT_DTPREL16_DS": "debug/elf",
+ "elf.R_PPC64_GOT_DTPREL16_HA": "debug/elf",
+ "elf.R_PPC64_GOT_DTPREL16_HI": "debug/elf",
+ "elf.R_PPC64_GOT_DTPREL16_LO_DS": "debug/elf",
+ "elf.R_PPC64_GOT_TLSGD16": "debug/elf",
+ "elf.R_PPC64_GOT_TLSGD16_HA": "debug/elf",
+ "elf.R_PPC64_GOT_TLSGD16_HI": "debug/elf",
+ "elf.R_PPC64_GOT_TLSGD16_LO": "debug/elf",
+ "elf.R_PPC64_GOT_TLSLD16": "debug/elf",
+ "elf.R_PPC64_GOT_TLSLD16_HA": "debug/elf",
+ "elf.R_PPC64_GOT_TLSLD16_HI": "debug/elf",
+ "elf.R_PPC64_GOT_TLSLD16_LO": "debug/elf",
+ "elf.R_PPC64_GOT_TPREL16_DS": "debug/elf",
+ "elf.R_PPC64_GOT_TPREL16_HA": "debug/elf",
+ "elf.R_PPC64_GOT_TPREL16_HI": "debug/elf",
+ "elf.R_PPC64_GOT_TPREL16_LO_DS": "debug/elf",
+ "elf.R_PPC64_JMP_SLOT": "debug/elf",
+ "elf.R_PPC64_NONE": "debug/elf",
+ "elf.R_PPC64_REL14": "debug/elf",
+ "elf.R_PPC64_REL14_BRNTAKEN": "debug/elf",
+ "elf.R_PPC64_REL14_BRTAKEN": "debug/elf",
+ "elf.R_PPC64_REL16": "debug/elf",
+ "elf.R_PPC64_REL16_HA": "debug/elf",
+ "elf.R_PPC64_REL16_HI": "debug/elf",
+ "elf.R_PPC64_REL16_LO": "debug/elf",
+ "elf.R_PPC64_REL24": "debug/elf",
+ "elf.R_PPC64_REL32": "debug/elf",
+ "elf.R_PPC64_REL64": "debug/elf",
+ "elf.R_PPC64_TLS": "debug/elf",
+ "elf.R_PPC64_TLSGD": "debug/elf",
+ "elf.R_PPC64_TLSLD": "debug/elf",
+ "elf.R_PPC64_TOC": "debug/elf",
+ "elf.R_PPC64_TOC16": "debug/elf",
+ "elf.R_PPC64_TOC16_DS": "debug/elf",
+ "elf.R_PPC64_TOC16_HA": "debug/elf",
+ "elf.R_PPC64_TOC16_HI": "debug/elf",
+ "elf.R_PPC64_TOC16_LO": "debug/elf",
+ "elf.R_PPC64_TOC16_LO_DS": "debug/elf",
+ "elf.R_PPC64_TPREL16": "debug/elf",
+ "elf.R_PPC64_TPREL16_DS": "debug/elf",
+ "elf.R_PPC64_TPREL16_HA": "debug/elf",
+ "elf.R_PPC64_TPREL16_HI": "debug/elf",
+ "elf.R_PPC64_TPREL16_HIGHER": "debug/elf",
+ "elf.R_PPC64_TPREL16_HIGHERA": "debug/elf",
+ "elf.R_PPC64_TPREL16_HIGHEST": "debug/elf",
+ "elf.R_PPC64_TPREL16_HIGHESTA": "debug/elf",
+ "elf.R_PPC64_TPREL16_LO": "debug/elf",
+ "elf.R_PPC64_TPREL16_LO_DS": "debug/elf",
+ "elf.R_PPC64_TPREL64": "debug/elf",
+ "elf.R_PPC_ADDR14": "debug/elf",
+ "elf.R_PPC_ADDR14_BRNTAKEN": "debug/elf",
+ "elf.R_PPC_ADDR14_BRTAKEN": "debug/elf",
+ "elf.R_PPC_ADDR16": "debug/elf",
+ "elf.R_PPC_ADDR16_HA": "debug/elf",
+ "elf.R_PPC_ADDR16_HI": "debug/elf",
+ "elf.R_PPC_ADDR16_LO": "debug/elf",
+ "elf.R_PPC_ADDR24": "debug/elf",
+ "elf.R_PPC_ADDR32": "debug/elf",
+ "elf.R_PPC_COPY": "debug/elf",
+ "elf.R_PPC_DTPMOD32": "debug/elf",
+ "elf.R_PPC_DTPREL16": "debug/elf",
+ "elf.R_PPC_DTPREL16_HA": "debug/elf",
+ "elf.R_PPC_DTPREL16_HI": "debug/elf",
+ "elf.R_PPC_DTPREL16_LO": "debug/elf",
+ "elf.R_PPC_DTPREL32": "debug/elf",
+ "elf.R_PPC_EMB_BIT_FLD": "debug/elf",
+ "elf.R_PPC_EMB_MRKREF": "debug/elf",
+ "elf.R_PPC_EMB_NADDR16": "debug/elf",
+ "elf.R_PPC_EMB_NADDR16_HA": "debug/elf",
+ "elf.R_PPC_EMB_NADDR16_HI": "debug/elf",
+ "elf.R_PPC_EMB_NADDR16_LO": "debug/elf",
+ "elf.R_PPC_EMB_NADDR32": "debug/elf",
+ "elf.R_PPC_EMB_RELSDA": "debug/elf",
+ "elf.R_PPC_EMB_RELSEC16": "debug/elf",
+ "elf.R_PPC_EMB_RELST_HA": "debug/elf",
+ "elf.R_PPC_EMB_RELST_HI": "debug/elf",
+ "elf.R_PPC_EMB_RELST_LO": "debug/elf",
+ "elf.R_PPC_EMB_SDA21": "debug/elf",
+ "elf.R_PPC_EMB_SDA2I16": "debug/elf",
+ "elf.R_PPC_EMB_SDA2REL": "debug/elf",
+ "elf.R_PPC_EMB_SDAI16": "debug/elf",
+ "elf.R_PPC_GLOB_DAT": "debug/elf",
+ "elf.R_PPC_GOT16": "debug/elf",
+ "elf.R_PPC_GOT16_HA": "debug/elf",
+ "elf.R_PPC_GOT16_HI": "debug/elf",
+ "elf.R_PPC_GOT16_LO": "debug/elf",
+ "elf.R_PPC_GOT_TLSGD16": "debug/elf",
+ "elf.R_PPC_GOT_TLSGD16_HA": "debug/elf",
+ "elf.R_PPC_GOT_TLSGD16_HI": "debug/elf",
+ "elf.R_PPC_GOT_TLSGD16_LO": "debug/elf",
+ "elf.R_PPC_GOT_TLSLD16": "debug/elf",
+ "elf.R_PPC_GOT_TLSLD16_HA": "debug/elf",
+ "elf.R_PPC_GOT_TLSLD16_HI": "debug/elf",
+ "elf.R_PPC_GOT_TLSLD16_LO": "debug/elf",
+ "elf.R_PPC_GOT_TPREL16": "debug/elf",
+ "elf.R_PPC_GOT_TPREL16_HA": "debug/elf",
+ "elf.R_PPC_GOT_TPREL16_HI": "debug/elf",
+ "elf.R_PPC_GOT_TPREL16_LO": "debug/elf",
+ "elf.R_PPC_JMP_SLOT": "debug/elf",
+ "elf.R_PPC_LOCAL24PC": "debug/elf",
+ "elf.R_PPC_NONE": "debug/elf",
+ "elf.R_PPC_PLT16_HA": "debug/elf",
+ "elf.R_PPC_PLT16_HI": "debug/elf",
+ "elf.R_PPC_PLT16_LO": "debug/elf",
+ "elf.R_PPC_PLT32": "debug/elf",
+ "elf.R_PPC_PLTREL24": "debug/elf",
+ "elf.R_PPC_PLTREL32": "debug/elf",
+ "elf.R_PPC_REL14": "debug/elf",
+ "elf.R_PPC_REL14_BRNTAKEN": "debug/elf",
+ "elf.R_PPC_REL14_BRTAKEN": "debug/elf",
+ "elf.R_PPC_REL24": "debug/elf",
+ "elf.R_PPC_REL32": "debug/elf",
+ "elf.R_PPC_RELATIVE": "debug/elf",
+ "elf.R_PPC_SDAREL16": "debug/elf",
+ "elf.R_PPC_SECTOFF": "debug/elf",
+ "elf.R_PPC_SECTOFF_HA": "debug/elf",
+ "elf.R_PPC_SECTOFF_HI": "debug/elf",
+ "elf.R_PPC_SECTOFF_LO": "debug/elf",
+ "elf.R_PPC_TLS": "debug/elf",
+ "elf.R_PPC_TPREL16": "debug/elf",
+ "elf.R_PPC_TPREL16_HA": "debug/elf",
+ "elf.R_PPC_TPREL16_HI": "debug/elf",
+ "elf.R_PPC_TPREL16_LO": "debug/elf",
+ "elf.R_PPC_TPREL32": "debug/elf",
+ "elf.R_PPC_UADDR16": "debug/elf",
+ "elf.R_PPC_UADDR32": "debug/elf",
+ "elf.R_SPARC": "debug/elf",
+ "elf.R_SPARC_10": "debug/elf",
+ "elf.R_SPARC_11": "debug/elf",
+ "elf.R_SPARC_13": "debug/elf",
+ "elf.R_SPARC_16": "debug/elf",
+ "elf.R_SPARC_22": "debug/elf",
+ "elf.R_SPARC_32": "debug/elf",
+ "elf.R_SPARC_5": "debug/elf",
+ "elf.R_SPARC_6": "debug/elf",
+ "elf.R_SPARC_64": "debug/elf",
+ "elf.R_SPARC_7": "debug/elf",
+ "elf.R_SPARC_8": "debug/elf",
+ "elf.R_SPARC_COPY": "debug/elf",
+ "elf.R_SPARC_DISP16": "debug/elf",
+ "elf.R_SPARC_DISP32": "debug/elf",
+ "elf.R_SPARC_DISP64": "debug/elf",
+ "elf.R_SPARC_DISP8": "debug/elf",
+ "elf.R_SPARC_GLOB_DAT": "debug/elf",
+ "elf.R_SPARC_GLOB_JMP": "debug/elf",
+ "elf.R_SPARC_GOT10": "debug/elf",
+ "elf.R_SPARC_GOT13": "debug/elf",
+ "elf.R_SPARC_GOT22": "debug/elf",
+ "elf.R_SPARC_H44": "debug/elf",
+ "elf.R_SPARC_HH22": "debug/elf",
+ "elf.R_SPARC_HI22": "debug/elf",
+ "elf.R_SPARC_HIPLT22": "debug/elf",
+ "elf.R_SPARC_HIX22": "debug/elf",
+ "elf.R_SPARC_HM10": "debug/elf",
+ "elf.R_SPARC_JMP_SLOT": "debug/elf",
+ "elf.R_SPARC_L44": "debug/elf",
+ "elf.R_SPARC_LM22": "debug/elf",
+ "elf.R_SPARC_LO10": "debug/elf",
+ "elf.R_SPARC_LOPLT10": "debug/elf",
+ "elf.R_SPARC_LOX10": "debug/elf",
+ "elf.R_SPARC_M44": "debug/elf",
+ "elf.R_SPARC_NONE": "debug/elf",
+ "elf.R_SPARC_OLO10": "debug/elf",
+ "elf.R_SPARC_PC10": "debug/elf",
+ "elf.R_SPARC_PC22": "debug/elf",
+ "elf.R_SPARC_PCPLT10": "debug/elf",
+ "elf.R_SPARC_PCPLT22": "debug/elf",
+ "elf.R_SPARC_PCPLT32": "debug/elf",
+ "elf.R_SPARC_PC_HH22": "debug/elf",
+ "elf.R_SPARC_PC_HM10": "debug/elf",
+ "elf.R_SPARC_PC_LM22": "debug/elf",
+ "elf.R_SPARC_PLT32": "debug/elf",
+ "elf.R_SPARC_PLT64": "debug/elf",
+ "elf.R_SPARC_REGISTER": "debug/elf",
+ "elf.R_SPARC_RELATIVE": "debug/elf",
+ "elf.R_SPARC_UA16": "debug/elf",
+ "elf.R_SPARC_UA32": "debug/elf",
+ "elf.R_SPARC_UA64": "debug/elf",
+ "elf.R_SPARC_WDISP16": "debug/elf",
+ "elf.R_SPARC_WDISP19": "debug/elf",
+ "elf.R_SPARC_WDISP22": "debug/elf",
+ "elf.R_SPARC_WDISP30": "debug/elf",
+ "elf.R_SPARC_WPLT30": "debug/elf",
+ "elf.R_SYM32": "debug/elf",
+ "elf.R_SYM64": "debug/elf",
+ "elf.R_TYPE32": "debug/elf",
+ "elf.R_TYPE64": "debug/elf",
+ "elf.R_X86_64": "debug/elf",
+ "elf.R_X86_64_16": "debug/elf",
+ "elf.R_X86_64_32": "debug/elf",
+ "elf.R_X86_64_32S": "debug/elf",
+ "elf.R_X86_64_64": "debug/elf",
+ "elf.R_X86_64_8": "debug/elf",
+ "elf.R_X86_64_COPY": "debug/elf",
+ "elf.R_X86_64_DTPMOD64": "debug/elf",
+ "elf.R_X86_64_DTPOFF32": "debug/elf",
+ "elf.R_X86_64_DTPOFF64": "debug/elf",
+ "elf.R_X86_64_GLOB_DAT": "debug/elf",
+ "elf.R_X86_64_GOT32": "debug/elf",
+ "elf.R_X86_64_GOTPCREL": "debug/elf",
+ "elf.R_X86_64_GOTTPOFF": "debug/elf",
+ "elf.R_X86_64_JMP_SLOT": "debug/elf",
+ "elf.R_X86_64_NONE": "debug/elf",
+ "elf.R_X86_64_PC16": "debug/elf",
+ "elf.R_X86_64_PC32": "debug/elf",
+ "elf.R_X86_64_PC8": "debug/elf",
+ "elf.R_X86_64_PLT32": "debug/elf",
+ "elf.R_X86_64_RELATIVE": "debug/elf",
+ "elf.R_X86_64_TLSGD": "debug/elf",
+ "elf.R_X86_64_TLSLD": "debug/elf",
+ "elf.R_X86_64_TPOFF32": "debug/elf",
+ "elf.R_X86_64_TPOFF64": "debug/elf",
+ "elf.Rel32": "debug/elf",
+ "elf.Rel64": "debug/elf",
+ "elf.Rela32": "debug/elf",
+ "elf.Rela64": "debug/elf",
+ "elf.SHF_ALLOC": "debug/elf",
+ "elf.SHF_EXECINSTR": "debug/elf",
+ "elf.SHF_GROUP": "debug/elf",
+ "elf.SHF_INFO_LINK": "debug/elf",
+ "elf.SHF_LINK_ORDER": "debug/elf",
+ "elf.SHF_MASKOS": "debug/elf",
+ "elf.SHF_MASKPROC": "debug/elf",
+ "elf.SHF_MERGE": "debug/elf",
+ "elf.SHF_OS_NONCONFORMING": "debug/elf",
+ "elf.SHF_STRINGS": "debug/elf",
+ "elf.SHF_TLS": "debug/elf",
+ "elf.SHF_WRITE": "debug/elf",
+ "elf.SHN_ABS": "debug/elf",
+ "elf.SHN_COMMON": "debug/elf",
+ "elf.SHN_HIOS": "debug/elf",
+ "elf.SHN_HIPROC": "debug/elf",
+ "elf.SHN_HIRESERVE": "debug/elf",
+ "elf.SHN_LOOS": "debug/elf",
+ "elf.SHN_LOPROC": "debug/elf",
+ "elf.SHN_LORESERVE": "debug/elf",
+ "elf.SHN_UNDEF": "debug/elf",
+ "elf.SHN_XINDEX": "debug/elf",
+ "elf.SHT_DYNAMIC": "debug/elf",
+ "elf.SHT_DYNSYM": "debug/elf",
+ "elf.SHT_FINI_ARRAY": "debug/elf",
+ "elf.SHT_GNU_ATTRIBUTES": "debug/elf",
+ "elf.SHT_GNU_HASH": "debug/elf",
+ "elf.SHT_GNU_LIBLIST": "debug/elf",
+ "elf.SHT_GNU_VERDEF": "debug/elf",
+ "elf.SHT_GNU_VERNEED": "debug/elf",
+ "elf.SHT_GNU_VERSYM": "debug/elf",
+ "elf.SHT_GROUP": "debug/elf",
+ "elf.SHT_HASH": "debug/elf",
+ "elf.SHT_HIOS": "debug/elf",
+ "elf.SHT_HIPROC": "debug/elf",
+ "elf.SHT_HIUSER": "debug/elf",
+ "elf.SHT_INIT_ARRAY": "debug/elf",
+ "elf.SHT_LOOS": "debug/elf",
+ "elf.SHT_LOPROC": "debug/elf",
+ "elf.SHT_LOUSER": "debug/elf",
+ "elf.SHT_NOBITS": "debug/elf",
+ "elf.SHT_NOTE": "debug/elf",
+ "elf.SHT_NULL": "debug/elf",
+ "elf.SHT_PREINIT_ARRAY": "debug/elf",
+ "elf.SHT_PROGBITS": "debug/elf",
+ "elf.SHT_REL": "debug/elf",
+ "elf.SHT_RELA": "debug/elf",
+ "elf.SHT_SHLIB": "debug/elf",
+ "elf.SHT_STRTAB": "debug/elf",
+ "elf.SHT_SYMTAB": "debug/elf",
+ "elf.SHT_SYMTAB_SHNDX": "debug/elf",
+ "elf.STB_GLOBAL": "debug/elf",
+ "elf.STB_HIOS": "debug/elf",
+ "elf.STB_HIPROC": "debug/elf",
+ "elf.STB_LOCAL": "debug/elf",
+ "elf.STB_LOOS": "debug/elf",
+ "elf.STB_LOPROC": "debug/elf",
+ "elf.STB_WEAK": "debug/elf",
+ "elf.STT_COMMON": "debug/elf",
+ "elf.STT_FILE": "debug/elf",
+ "elf.STT_FUNC": "debug/elf",
+ "elf.STT_HIOS": "debug/elf",
+ "elf.STT_HIPROC": "debug/elf",
+ "elf.STT_LOOS": "debug/elf",
+ "elf.STT_LOPROC": "debug/elf",
+ "elf.STT_NOTYPE": "debug/elf",
+ "elf.STT_OBJECT": "debug/elf",
+ "elf.STT_SECTION": "debug/elf",
+ "elf.STT_TLS": "debug/elf",
+ "elf.STV_DEFAULT": "debug/elf",
+ "elf.STV_HIDDEN": "debug/elf",
+ "elf.STV_INTERNAL": "debug/elf",
+ "elf.STV_PROTECTED": "debug/elf",
+ "elf.ST_BIND": "debug/elf",
+ "elf.ST_INFO": "debug/elf",
+ "elf.ST_TYPE": "debug/elf",
+ "elf.ST_VISIBILITY": "debug/elf",
+ "elf.Section": "debug/elf",
+ "elf.Section32": "debug/elf",
+ "elf.Section64": "debug/elf",
+ "elf.SectionFlag": "debug/elf",
+ "elf.SectionHeader": "debug/elf",
+ "elf.SectionIndex": "debug/elf",
+ "elf.SectionType": "debug/elf",
+ "elf.Sym32": "debug/elf",
+ "elf.Sym32Size": "debug/elf",
+ "elf.Sym64": "debug/elf",
+ "elf.Sym64Size": "debug/elf",
+ "elf.SymBind": "debug/elf",
+ "elf.SymType": "debug/elf",
+ "elf.SymVis": "debug/elf",
+ "elf.Symbol": "debug/elf",
+ "elf.Type": "debug/elf",
+ "elf.Version": "debug/elf",
+ "elliptic.Curve": "crypto/elliptic",
+ "elliptic.CurveParams": "crypto/elliptic",
+ "elliptic.GenerateKey": "crypto/elliptic",
+ "elliptic.Marshal": "crypto/elliptic",
+ "elliptic.P224": "crypto/elliptic",
+ "elliptic.P256": "crypto/elliptic",
+ "elliptic.P384": "crypto/elliptic",
+ "elliptic.P521": "crypto/elliptic",
+ "elliptic.Unmarshal": "crypto/elliptic",
+ "encoding.BinaryMarshaler": "encoding",
+ "encoding.BinaryUnmarshaler": "encoding",
+ "encoding.TextMarshaler": "encoding",
+ "encoding.TextUnmarshaler": "encoding",
+ "errors.New": "errors",
+ "exec.Cmd": "os/exec",
+ "exec.Command": "os/exec",
+ "exec.ErrNotFound": "os/exec",
+ "exec.Error": "os/exec",
+ "exec.ExitError": "os/exec",
+ "exec.LookPath": "os/exec",
+ "expvar.Do": "expvar",
+ "expvar.Float": "expvar",
+ "expvar.Func": "expvar",
+ "expvar.Get": "expvar",
+ "expvar.Int": "expvar",
+ "expvar.KeyValue": "expvar",
+ "expvar.Map": "expvar",
+ "expvar.NewFloat": "expvar",
+ "expvar.NewInt": "expvar",
+ "expvar.NewMap": "expvar",
+ "expvar.NewString": "expvar",
+ "expvar.Publish": "expvar",
+ "expvar.String": "expvar",
+ "expvar.Var": "expvar",
+ "fcgi.ErrConnClosed": "net/http/fcgi",
+ "fcgi.ErrRequestAborted": "net/http/fcgi",
+ "fcgi.Serve": "net/http/fcgi",
+ "filepath.Abs": "path/filepath",
+ "filepath.Base": "path/filepath",
+ "filepath.Clean": "path/filepath",
+ "filepath.Dir": "path/filepath",
+ "filepath.ErrBadPattern": "path/filepath",
+ "filepath.EvalSymlinks": "path/filepath",
+ "filepath.Ext": "path/filepath",
+ "filepath.FromSlash": "path/filepath",
+ "filepath.Glob": "path/filepath",
+ "filepath.HasPrefix": "path/filepath",
+ "filepath.IsAbs": "path/filepath",
+ "filepath.Join": "path/filepath",
+ "filepath.ListSeparator": "path/filepath",
+ "filepath.Match": "path/filepath",
+ "filepath.Rel": "path/filepath",
+ "filepath.Separator": "path/filepath",
+ "filepath.SkipDir": "path/filepath",
+ "filepath.Split": "path/filepath",
+ "filepath.SplitList": "path/filepath",
+ "filepath.ToSlash": "path/filepath",
+ "filepath.VolumeName": "path/filepath",
+ "filepath.Walk": "path/filepath",
+ "filepath.WalkFunc": "path/filepath",
+ "flag.Arg": "flag",
+ "flag.Args": "flag",
+ "flag.Bool": "flag",
+ "flag.BoolVar": "flag",
+ "flag.CommandLine": "flag",
+ "flag.ContinueOnError": "flag",
+ "flag.Duration": "flag",
+ "flag.DurationVar": "flag",
+ "flag.ErrHelp": "flag",
+ "flag.ErrorHandling": "flag",
+ "flag.ExitOnError": "flag",
+ "flag.Flag": "flag",
+ "flag.FlagSet": "flag",
+ "flag.Float64": "flag",
+ "flag.Float64Var": "flag",
+ "flag.Getter": "flag",
+ "flag.Int": "flag",
+ "flag.Int64": "flag",
+ "flag.Int64Var": "flag",
+ "flag.IntVar": "flag",
+ "flag.Lookup": "flag",
+ "flag.NArg": "flag",
+ "flag.NFlag": "flag",
+ "flag.NewFlagSet": "flag",
+ "flag.PanicOnError": "flag",
+ "flag.Parse": "flag",
+ "flag.Parsed": "flag",
+ "flag.PrintDefaults": "flag",
+ "flag.Set": "flag",
+ "flag.String": "flag",
+ "flag.StringVar": "flag",
+ "flag.Uint": "flag",
+ "flag.Uint64": "flag",
+ "flag.Uint64Var": "flag",
+ "flag.UintVar": "flag",
+ "flag.UnquoteUsage": "flag",
+ "flag.Usage": "flag",
+ "flag.Value": "flag",
+ "flag.Var": "flag",
+ "flag.Visit": "flag",
+ "flag.VisitAll": "flag",
+ "flate.BestCompression": "compress/flate",
+ "flate.BestSpeed": "compress/flate",
+ "flate.CorruptInputError": "compress/flate",
+ "flate.DefaultCompression": "compress/flate",
+ "flate.InternalError": "compress/flate",
+ "flate.NewReader": "compress/flate",
+ "flate.NewReaderDict": "compress/flate",
+ "flate.NewWriter": "compress/flate",
+ "flate.NewWriterDict": "compress/flate",
+ "flate.NoCompression": "compress/flate",
+ "flate.ReadError": "compress/flate",
+ "flate.Reader": "compress/flate",
+ "flate.Resetter": "compress/flate",
+ "flate.WriteError": "compress/flate",
+ "flate.Writer": "compress/flate",
+ "fmt.Errorf": "fmt",
+ "fmt.Formatter": "fmt",
+ "fmt.Fprint": "fmt",
+ "fmt.Fprintf": "fmt",
+ "fmt.Fprintln": "fmt",
+ "fmt.Fscan": "fmt",
+ "fmt.Fscanf": "fmt",
+ "fmt.Fscanln": "fmt",
+ "fmt.GoStringer": "fmt",
+ "fmt.Print": "fmt",
+ "fmt.Printf": "fmt",
+ "fmt.Println": "fmt",
+ "fmt.Scan": "fmt",
+ "fmt.ScanState": "fmt",
+ "fmt.Scanf": "fmt",
+ "fmt.Scanln": "fmt",
+ "fmt.Scanner": "fmt",
+ "fmt.Sprint": "fmt",
+ "fmt.Sprintf": "fmt",
+ "fmt.Sprintln": "fmt",
+ "fmt.Sscan": "fmt",
+ "fmt.Sscanf": "fmt",
+ "fmt.Sscanln": "fmt",
+ "fmt.State": "fmt",
+ "fmt.Stringer": "fmt",
+ "fnv.New32": "hash/fnv",
+ "fnv.New32a": "hash/fnv",
+ "fnv.New64": "hash/fnv",
+ "fnv.New64a": "hash/fnv",
+ "format.Node": "go/format",
+ "format.Source": "go/format",
+ "gif.Decode": "image/gif",
+ "gif.DecodeAll": "image/gif",
+ "gif.DecodeConfig": "image/gif",
+ "gif.DisposalBackground": "image/gif",
+ "gif.DisposalNone": "image/gif",
+ "gif.DisposalPrevious": "image/gif",
+ "gif.Encode": "image/gif",
+ "gif.EncodeAll": "image/gif",
+ "gif.GIF": "image/gif",
+ "gif.Options": "image/gif",
+ "gob.CommonType": "encoding/gob",
+ "gob.Decoder": "encoding/gob",
+ "gob.Encoder": "encoding/gob",
+ "gob.GobDecoder": "encoding/gob",
+ "gob.GobEncoder": "encoding/gob",
+ "gob.NewDecoder": "encoding/gob",
+ "gob.NewEncoder": "encoding/gob",
+ "gob.Register": "encoding/gob",
+ "gob.RegisterName": "encoding/gob",
+ "gosym.DecodingError": "debug/gosym",
+ "gosym.Func": "debug/gosym",
+ "gosym.LineTable": "debug/gosym",
+ "gosym.NewLineTable": "debug/gosym",
+ "gosym.NewTable": "debug/gosym",
+ "gosym.Obj": "debug/gosym",
+ "gosym.Sym": "debug/gosym",
+ "gosym.Table": "debug/gosym",
+ "gosym.UnknownFileError": "debug/gosym",
+ "gosym.UnknownLineError": "debug/gosym",
+ "gzip.BestCompression": "compress/gzip",
+ "gzip.BestSpeed": "compress/gzip",
+ "gzip.DefaultCompression": "compress/gzip",
+ "gzip.ErrChecksum": "compress/gzip",
+ "gzip.ErrHeader": "compress/gzip",
+ "gzip.Header": "compress/gzip",
+ "gzip.NewReader": "compress/gzip",
+ "gzip.NewWriter": "compress/gzip",
+ "gzip.NewWriterLevel": "compress/gzip",
+ "gzip.NoCompression": "compress/gzip",
+ "gzip.Reader": "compress/gzip",
+ "gzip.Writer": "compress/gzip",
+ "hash.Hash": "hash",
+ "hash.Hash32": "hash",
+ "hash.Hash64": "hash",
+ "heap.Fix": "container/heap",
+ "heap.Init": "container/heap",
+ "heap.Interface": "container/heap",
+ "heap.Pop": "container/heap",
+ "heap.Push": "container/heap",
+ "heap.Remove": "container/heap",
+ "hex.Decode": "encoding/hex",
+ "hex.DecodeString": "encoding/hex",
+ "hex.DecodedLen": "encoding/hex",
+ "hex.Dump": "encoding/hex",
+ "hex.Dumper": "encoding/hex",
+ "hex.Encode": "encoding/hex",
+ "hex.EncodeToString": "encoding/hex",
+ "hex.EncodedLen": "encoding/hex",
+ "hex.ErrLength": "encoding/hex",
+ "hex.InvalidByteError": "encoding/hex",
+ "hmac.Equal": "crypto/hmac",
+ "hmac.New": "crypto/hmac",
+ "html.EscapeString": "html",
+ "html.UnescapeString": "html",
+ "http.CanonicalHeaderKey": "net/http",
+ "http.Client": "net/http",
+ "http.CloseNotifier": "net/http",
+ "http.ConnState": "net/http",
+ "http.Cookie": "net/http",
+ "http.CookieJar": "net/http",
+ "http.DefaultClient": "net/http",
+ "http.DefaultMaxHeaderBytes": "net/http",
+ "http.DefaultMaxIdleConnsPerHost": "net/http",
+ "http.DefaultServeMux": "net/http",
+ "http.DefaultTransport": "net/http",
+ "http.DetectContentType": "net/http",
+ "http.Dir": "net/http",
+ "http.ErrBodyNotAllowed": "net/http",
+ "http.ErrBodyReadAfterClose": "net/http",
+ "http.ErrContentLength": "net/http",
+ "http.ErrHandlerTimeout": "net/http",
+ "http.ErrHeaderTooLong": "net/http",
+ "http.ErrHijacked": "net/http",
+ "http.ErrLineTooLong": "net/http",
+ "http.ErrMissingBoundary": "net/http",
+ "http.ErrMissingContentLength": "net/http",
+ "http.ErrMissingFile": "net/http",
+ "http.ErrNoCookie": "net/http",
+ "http.ErrNoLocation": "net/http",
+ "http.ErrNotMultipart": "net/http",
+ "http.ErrNotSupported": "net/http",
+ "http.ErrShortBody": "net/http",
+ "http.ErrUnexpectedTrailer": "net/http",
+ "http.ErrWriteAfterFlush": "net/http",
+ "http.Error": "net/http",
+ "http.File": "net/http",
+ "http.FileServer": "net/http",
+ "http.FileSystem": "net/http",
+ "http.Flusher": "net/http",
+ "http.Get": "net/http",
+ "http.Handle": "net/http",
+ "http.HandleFunc": "net/http",
+ "http.Handler": "net/http",
+ "http.HandlerFunc": "net/http",
+ "http.Head": "net/http",
+ "http.Header": "net/http",
+ "http.Hijacker": "net/http",
+ "http.ListenAndServe": "net/http",
+ "http.ListenAndServeTLS": "net/http",
+ "http.MaxBytesReader": "net/http",
+ "http.NewFileTransport": "net/http",
+ "http.NewRequest": "net/http",
+ "http.NewServeMux": "net/http",
+ "http.NotFound": "net/http",
+ "http.NotFoundHandler": "net/http",
+ "http.ParseHTTPVersion": "net/http",
+ "http.ParseTime": "net/http",
+ "http.Post": "net/http",
+ "http.PostForm": "net/http",
+ "http.ProtocolError": "net/http",
+ "http.ProxyFromEnvironment": "net/http",
+ "http.ProxyURL": "net/http",
+ "http.ReadRequest": "net/http",
+ "http.ReadResponse": "net/http",
+ "http.Redirect": "net/http",
+ "http.RedirectHandler": "net/http",
+ "http.Request": "net/http",
+ "http.Response": "net/http",
+ "http.ResponseWriter": "net/http",
+ "http.RoundTripper": "net/http",
+ "http.Serve": "net/http",
+ "http.ServeContent": "net/http",
+ "http.ServeFile": "net/http",
+ "http.ServeMux": "net/http",
+ "http.Server": "net/http",
+ "http.SetCookie": "net/http",
+ "http.StateActive": "net/http",
+ "http.StateClosed": "net/http",
+ "http.StateHijacked": "net/http",
+ "http.StateIdle": "net/http",
+ "http.StateNew": "net/http",
+ "http.StatusAccepted": "net/http",
+ "http.StatusBadGateway": "net/http",
+ "http.StatusBadRequest": "net/http",
+ "http.StatusConflict": "net/http",
+ "http.StatusContinue": "net/http",
+ "http.StatusCreated": "net/http",
+ "http.StatusExpectationFailed": "net/http",
+ "http.StatusForbidden": "net/http",
+ "http.StatusFound": "net/http",
+ "http.StatusGatewayTimeout": "net/http",
+ "http.StatusGone": "net/http",
+ "http.StatusHTTPVersionNotSupported": "net/http",
+ "http.StatusInternalServerError": "net/http",
+ "http.StatusLengthRequired": "net/http",
+ "http.StatusMethodNotAllowed": "net/http",
+ "http.StatusMovedPermanently": "net/http",
+ "http.StatusMultipleChoices": "net/http",
+ "http.StatusNoContent": "net/http",
+ "http.StatusNonAuthoritativeInfo": "net/http",
+ "http.StatusNotAcceptable": "net/http",
+ "http.StatusNotFound": "net/http",
+ "http.StatusNotImplemented": "net/http",
+ "http.StatusNotModified": "net/http",
+ "http.StatusOK": "net/http",
+ "http.StatusPartialContent": "net/http",
+ "http.StatusPaymentRequired": "net/http",
+ "http.StatusPreconditionFailed": "net/http",
+ "http.StatusProxyAuthRequired": "net/http",
+ "http.StatusRequestEntityTooLarge": "net/http",
+ "http.StatusRequestTimeout": "net/http",
+ "http.StatusRequestURITooLong": "net/http",
+ "http.StatusRequestedRangeNotSatisfiable": "net/http",
+ "http.StatusResetContent": "net/http",
+ "http.StatusSeeOther": "net/http",
+ "http.StatusServiceUnavailable": "net/http",
+ "http.StatusSwitchingProtocols": "net/http",
+ "http.StatusTeapot": "net/http",
+ "http.StatusTemporaryRedirect": "net/http",
+ "http.StatusText": "net/http",
+ "http.StatusUnauthorized": "net/http",
+ "http.StatusUnsupportedMediaType": "net/http",
+ "http.StatusUseProxy": "net/http",
+ "http.StripPrefix": "net/http",
+ "http.TimeFormat": "net/http",
+ "http.TimeoutHandler": "net/http",
+ "http.Transport": "net/http",
+ "httptest.DefaultRemoteAddr": "net/http/httptest",
+ "httptest.NewRecorder": "net/http/httptest",
+ "httptest.NewServer": "net/http/httptest",
+ "httptest.NewTLSServer": "net/http/httptest",
+ "httptest.NewUnstartedServer": "net/http/httptest",
+ "httptest.ResponseRecorder": "net/http/httptest",
+ "httptest.Server": "net/http/httptest",
+ "httputil.ClientConn": "net/http/httputil",
+ "httputil.DumpRequest": "net/http/httputil",
+ "httputil.DumpRequestOut": "net/http/httputil",
+ "httputil.DumpResponse": "net/http/httputil",
+ "httputil.ErrClosed": "net/http/httputil",
+ "httputil.ErrLineTooLong": "net/http/httputil",
+ "httputil.ErrPersistEOF": "net/http/httputil",
+ "httputil.ErrPipeline": "net/http/httputil",
+ "httputil.NewChunkedReader": "net/http/httputil",
+ "httputil.NewChunkedWriter": "net/http/httputil",
+ "httputil.NewClientConn": "net/http/httputil",
+ "httputil.NewProxyClientConn": "net/http/httputil",
+ "httputil.NewServerConn": "net/http/httputil",
+ "httputil.NewSingleHostReverseProxy": "net/http/httputil",
+ "httputil.ReverseProxy": "net/http/httputil",
+ "httputil.ServerConn": "net/http/httputil",
+ "image.Alpha": "image",
+ "image.Alpha16": "image",
+ "image.Black": "image",
+ "image.CMYK": "image",
+ "image.Config": "image",
+ "image.Decode": "image",
+ "image.DecodeConfig": "image",
+ "image.ErrFormat": "image",
+ "image.Gray": "image",
+ "image.Gray16": "image",
+ "image.Image": "image",
+ "image.NRGBA": "image",
+ "image.NRGBA64": "image",
+ "image.NewAlpha": "image",
+ "image.NewAlpha16": "image",
+ "image.NewCMYK": "image",
+ "image.NewGray": "image",
+ "image.NewGray16": "image",
+ "image.NewNRGBA": "image",
+ "image.NewNRGBA64": "image",
+ "image.NewPaletted": "image",
+ "image.NewRGBA": "image",
+ "image.NewRGBA64": "image",
+ "image.NewUniform": "image",
+ "image.NewYCbCr": "image",
+ "image.Opaque": "image",
+ "image.Paletted": "image",
+ "image.PalettedImage": "image",
+ "image.Point": "image",
+ "image.Pt": "image",
+ "image.RGBA": "image",
+ "image.RGBA64": "image",
+ "image.Rect": "image",
+ "image.Rectangle": "image",
+ "image.RegisterFormat": "image",
+ "image.Transparent": "image",
+ "image.Uniform": "image",
+ "image.White": "image",
+ "image.YCbCr": "image",
+ "image.YCbCrSubsampleRatio": "image",
+ "image.YCbCrSubsampleRatio410": "image",
+ "image.YCbCrSubsampleRatio411": "image",
+ "image.YCbCrSubsampleRatio420": "image",
+ "image.YCbCrSubsampleRatio422": "image",
+ "image.YCbCrSubsampleRatio440": "image",
+ "image.YCbCrSubsampleRatio444": "image",
+ "image.ZP": "image",
+ "image.ZR": "image",
+ "importer.Default": "go/importer",
+ "importer.For": "go/importer",
+ "importer.Lookup": "go/importer",
+ "io.ByteReader": "io",
+ "io.ByteScanner": "io",
+ "io.ByteWriter": "io",
+ "io.Closer": "io",
+ "io.Copy": "io",
+ "io.CopyBuffer": "io",
+ "io.CopyN": "io",
+ "io.EOF": "io",
+ "io.ErrClosedPipe": "io",
+ "io.ErrNoProgress": "io",
+ "io.ErrShortBuffer": "io",
+ "io.ErrShortWrite": "io",
+ "io.ErrUnexpectedEOF": "io",
+ "io.LimitReader": "io",
+ "io.LimitedReader": "io",
+ "io.MultiReader": "io",
+ "io.MultiWriter": "io",
+ "io.NewSectionReader": "io",
+ "io.Pipe": "io",
+ "io.PipeReader": "io",
+ "io.PipeWriter": "io",
+ "io.ReadAtLeast": "io",
+ "io.ReadCloser": "io",
+ "io.ReadFull": "io",
+ "io.ReadSeeker": "io",
+ "io.ReadWriteCloser": "io",
+ "io.ReadWriteSeeker": "io",
+ "io.ReadWriter": "io",
+ "io.Reader": "io",
+ "io.ReaderAt": "io",
+ "io.ReaderFrom": "io",
+ "io.RuneReader": "io",
+ "io.RuneScanner": "io",
+ "io.SectionReader": "io",
+ "io.Seeker": "io",
+ "io.TeeReader": "io",
+ "io.WriteCloser": "io",
+ "io.WriteSeeker": "io",
+ "io.WriteString": "io",
+ "io.Writer": "io",
+ "io.WriterAt": "io",
+ "io.WriterTo": "io",
+ "iotest.DataErrReader": "testing/iotest",
+ "iotest.ErrTimeout": "testing/iotest",
+ "iotest.HalfReader": "testing/iotest",
+ "iotest.NewReadLogger": "testing/iotest",
+ "iotest.NewWriteLogger": "testing/iotest",
+ "iotest.OneByteReader": "testing/iotest",
+ "iotest.TimeoutReader": "testing/iotest",
+ "iotest.TruncateWriter": "testing/iotest",
+ "ioutil.Discard": "io/ioutil",
+ "ioutil.NopCloser": "io/ioutil",
+ "ioutil.ReadAll": "io/ioutil",
+ "ioutil.ReadDir": "io/ioutil",
+ "ioutil.ReadFile": "io/ioutil",
+ "ioutil.TempDir": "io/ioutil",
+ "ioutil.TempFile": "io/ioutil",
+ "ioutil.WriteFile": "io/ioutil",
+ "jpeg.Decode": "image/jpeg",
+ "jpeg.DecodeConfig": "image/jpeg",
+ "jpeg.DefaultQuality": "image/jpeg",
+ "jpeg.Encode": "image/jpeg",
+ "jpeg.FormatError": "image/jpeg",
+ "jpeg.Options": "image/jpeg",
+ "jpeg.Reader": "image/jpeg",
+ "jpeg.UnsupportedError": "image/jpeg",
+ "json.Compact": "encoding/json",
+ "json.Decoder": "encoding/json",
+ "json.Delim": "encoding/json",
+ "json.Encoder": "encoding/json",
+ "json.HTMLEscape": "encoding/json",
+ "json.Indent": "encoding/json",
+ "json.InvalidUTF8Error": "encoding/json",
+ "json.InvalidUnmarshalError": "encoding/json",
+ "json.Marshal": "encoding/json",
+ "json.MarshalIndent": "encoding/json",
+ "json.Marshaler": "encoding/json",
+ "json.MarshalerError": "encoding/json",
+ "json.NewDecoder": "encoding/json",
+ "json.NewEncoder": "encoding/json",
+ "json.Number": "encoding/json",
+ "json.RawMessage": "encoding/json",
+ "json.SyntaxError": "encoding/json",
+ "json.Token": "encoding/json",
+ "json.Unmarshal": "encoding/json",
+ "json.UnmarshalFieldError": "encoding/json",
+ "json.UnmarshalTypeError": "encoding/json",
+ "json.Unmarshaler": "encoding/json",
+ "json.UnsupportedTypeError": "encoding/json",
+ "json.UnsupportedValueError": "encoding/json",
+ "jsonrpc.Dial": "net/rpc/jsonrpc",
+ "jsonrpc.NewClient": "net/rpc/jsonrpc",
+ "jsonrpc.NewClientCodec": "net/rpc/jsonrpc",
+ "jsonrpc.NewServerCodec": "net/rpc/jsonrpc",
+ "jsonrpc.ServeConn": "net/rpc/jsonrpc",
+ "list.Element": "container/list",
+ "list.List": "container/list",
+ "list.New": "container/list",
+ "log.Fatal": "log",
+ "log.Fatalf": "log",
+ "log.Fatalln": "log",
+ "log.Flags": "log",
+ "log.LUTC": "log",
+ "log.Ldate": "log",
+ "log.Llongfile": "log",
+ "log.Lmicroseconds": "log",
+ "log.Logger": "log",
+ "log.Lshortfile": "log",
+ "log.LstdFlags": "log",
+ "log.Ltime": "log",
+ "log.New": "log",
+ "log.Output": "log",
+ "log.Panic": "log",
+ "log.Panicf": "log",
+ "log.Panicln": "log",
+ "log.Prefix": "log",
+ "log.Print": "log",
+ "log.Printf": "log",
+ "log.Println": "log",
+ "log.SetFlags": "log",
+ "log.SetOutput": "log",
+ "log.SetPrefix": "log",
+ "lzw.LSB": "compress/lzw",
+ "lzw.MSB": "compress/lzw",
+ "lzw.NewReader": "compress/lzw",
+ "lzw.NewWriter": "compress/lzw",
+ "lzw.Order": "compress/lzw",
+ "macho.Cpu": "debug/macho",
+ "macho.Cpu386": "debug/macho",
+ "macho.CpuAmd64": "debug/macho",
+ "macho.CpuArm": "debug/macho",
+ "macho.CpuPpc": "debug/macho",
+ "macho.CpuPpc64": "debug/macho",
+ "macho.Dylib": "debug/macho",
+ "macho.DylibCmd": "debug/macho",
+ "macho.Dysymtab": "debug/macho",
+ "macho.DysymtabCmd": "debug/macho",
+ "macho.ErrNotFat": "debug/macho",
+ "macho.FatArch": "debug/macho",
+ "macho.FatArchHeader": "debug/macho",
+ "macho.FatFile": "debug/macho",
+ "macho.File": "debug/macho",
+ "macho.FileHeader": "debug/macho",
+ "macho.FormatError": "debug/macho",
+ "macho.Load": "debug/macho",
+ "macho.LoadBytes": "debug/macho",
+ "macho.LoadCmd": "debug/macho",
+ "macho.LoadCmdDylib": "debug/macho",
+ "macho.LoadCmdDylinker": "debug/macho",
+ "macho.LoadCmdDysymtab": "debug/macho",
+ "macho.LoadCmdSegment": "debug/macho",
+ "macho.LoadCmdSegment64": "debug/macho",
+ "macho.LoadCmdSymtab": "debug/macho",
+ "macho.LoadCmdThread": "debug/macho",
+ "macho.LoadCmdUnixThread": "debug/macho",
+ "macho.Magic32": "debug/macho",
+ "macho.Magic64": "debug/macho",
+ "macho.MagicFat": "debug/macho",
+ "macho.NewFatFile": "debug/macho",
+ "macho.NewFile": "debug/macho",
+ "macho.Nlist32": "debug/macho",
+ "macho.Nlist64": "debug/macho",
+ "macho.Open": "debug/macho",
+ "macho.OpenFat": "debug/macho",
+ "macho.Regs386": "debug/macho",
+ "macho.RegsAMD64": "debug/macho",
+ "macho.Section": "debug/macho",
+ "macho.Section32": "debug/macho",
+ "macho.Section64": "debug/macho",
+ "macho.SectionHeader": "debug/macho",
+ "macho.Segment": "debug/macho",
+ "macho.Segment32": "debug/macho",
+ "macho.Segment64": "debug/macho",
+ "macho.SegmentHeader": "debug/macho",
+ "macho.Symbol": "debug/macho",
+ "macho.Symtab": "debug/macho",
+ "macho.SymtabCmd": "debug/macho",
+ "macho.Thread": "debug/macho",
+ "macho.Type": "debug/macho",
+ "macho.TypeBundle": "debug/macho",
+ "macho.TypeDylib": "debug/macho",
+ "macho.TypeExec": "debug/macho",
+ "macho.TypeObj": "debug/macho",
+ "mail.Address": "net/mail",
+ "mail.AddressParser": "net/mail",
+ "mail.ErrHeaderNotPresent": "net/mail",
+ "mail.Header": "net/mail",
+ "mail.Message": "net/mail",
+ "mail.ParseAddress": "net/mail",
+ "mail.ParseAddressList": "net/mail",
+ "mail.ReadMessage": "net/mail",
+ "math.Abs": "math",
+ "math.Acos": "math",
+ "math.Acosh": "math",
+ "math.Asin": "math",
+ "math.Asinh": "math",
+ "math.Atan": "math",
+ "math.Atan2": "math",
+ "math.Atanh": "math",
+ "math.Cbrt": "math",
+ "math.Ceil": "math",
+ "math.Copysign": "math",
+ "math.Cos": "math",
+ "math.Cosh": "math",
+ "math.Dim": "math",
+ "math.E": "math",
+ "math.Erf": "math",
+ "math.Erfc": "math",
+ "math.Exp": "math",
+ "math.Exp2": "math",
+ "math.Expm1": "math",
+ "math.Float32bits": "math",
+ "math.Float32frombits": "math",
+ "math.Float64bits": "math",
+ "math.Float64frombits": "math",
+ "math.Floor": "math",
+ "math.Frexp": "math",
+ "math.Gamma": "math",
+ "math.Hypot": "math",
+ "math.Ilogb": "math",
+ "math.Inf": "math",
+ "math.IsInf": "math",
+ "math.IsNaN": "math",
+ "math.J0": "math",
+ "math.J1": "math",
+ "math.Jn": "math",
+ "math.Ldexp": "math",
+ "math.Lgamma": "math",
+ "math.Ln10": "math",
+ "math.Ln2": "math",
+ "math.Log": "math",
+ "math.Log10": "math",
+ "math.Log10E": "math",
+ "math.Log1p": "math",
+ "math.Log2": "math",
+ "math.Log2E": "math",
+ "math.Logb": "math",
+ "math.Max": "math",
+ "math.MaxFloat32": "math",
+ "math.MaxFloat64": "math",
+ "math.MaxInt16": "math",
+ "math.MaxInt32": "math",
+ "math.MaxInt64": "math",
+ "math.MaxInt8": "math",
+ "math.MaxUint16": "math",
+ "math.MaxUint32": "math",
+ "math.MaxUint64": "math",
+ "math.MaxUint8": "math",
+ "math.Min": "math",
+ "math.MinInt16": "math",
+ "math.MinInt32": "math",
+ "math.MinInt64": "math",
+ "math.MinInt8": "math",
+ "math.Mod": "math",
+ "math.Modf": "math",
+ "math.NaN": "math",
+ "math.Nextafter": "math",
+ "math.Nextafter32": "math",
+ "math.Phi": "math",
+ "math.Pi": "math",
+ "math.Pow": "math",
+ "math.Pow10": "math",
+ "math.Remainder": "math",
+ "math.Signbit": "math",
+ "math.Sin": "math",
+ "math.Sincos": "math",
+ "math.Sinh": "math",
+ "math.SmallestNonzeroFloat32": "math",
+ "math.SmallestNonzeroFloat64": "math",
+ "math.Sqrt": "math",
+ "math.Sqrt2": "math",
+ "math.SqrtE": "math",
+ "math.SqrtPhi": "math",
+ "math.SqrtPi": "math",
+ "math.Tan": "math",
+ "math.Tanh": "math",
+ "math.Trunc": "math",
+ "math.Y0": "math",
+ "math.Y1": "math",
+ "math.Yn": "math",
+ "md5.BlockSize": "crypto/md5",
+ "md5.New": "crypto/md5",
+ "md5.Size": "crypto/md5",
+ "md5.Sum": "crypto/md5",
+ "mime.AddExtensionType": "mime",
+ "mime.BEncoding": "mime",
+ "mime.ExtensionsByType": "mime",
+ "mime.FormatMediaType": "mime",
+ "mime.ParseMediaType": "mime",
+ "mime.QEncoding": "mime",
+ "mime.TypeByExtension": "mime",
+ "mime.WordDecoder": "mime",
+ "mime.WordEncoder": "mime",
+ "multipart.File": "mime/multipart",
+ "multipart.FileHeader": "mime/multipart",
+ "multipart.Form": "mime/multipart",
+ "multipart.NewReader": "mime/multipart",
+ "multipart.NewWriter": "mime/multipart",
+ "multipart.Part": "mime/multipart",
+ "multipart.Reader": "mime/multipart",
+ "multipart.Writer": "mime/multipart",
+ "net.Addr": "net",
+ "net.AddrError": "net",
+ "net.CIDRMask": "net",
+ "net.Conn": "net",
+ "net.DNSConfigError": "net",
+ "net.DNSError": "net",
+ "net.Dial": "net",
+ "net.DialIP": "net",
+ "net.DialTCP": "net",
+ "net.DialTimeout": "net",
+ "net.DialUDP": "net",
+ "net.DialUnix": "net",
+ "net.Dialer": "net",
+ "net.ErrWriteToConnected": "net",
+ "net.Error": "net",
+ "net.FileConn": "net",
+ "net.FileListener": "net",
+ "net.FilePacketConn": "net",
+ "net.FlagBroadcast": "net",
+ "net.FlagLoopback": "net",
+ "net.FlagMulticast": "net",
+ "net.FlagPointToPoint": "net",
+ "net.FlagUp": "net",
+ "net.Flags": "net",
+ "net.HardwareAddr": "net",
+ "net.IP": "net",
+ "net.IPAddr": "net",
+ "net.IPConn": "net",
+ "net.IPMask": "net",
+ "net.IPNet": "net",
+ "net.IPv4": "net",
+ "net.IPv4Mask": "net",
+ "net.IPv4allrouter": "net",
+ "net.IPv4allsys": "net",
+ "net.IPv4bcast": "net",
+ "net.IPv4len": "net",
+ "net.IPv4zero": "net",
+ "net.IPv6interfacelocalallnodes": "net",
+ "net.IPv6len": "net",
+ "net.IPv6linklocalallnodes": "net",
+ "net.IPv6linklocalallrouters": "net",
+ "net.IPv6loopback": "net",
+ "net.IPv6unspecified": "net",
+ "net.IPv6zero": "net",
+ "net.Interface": "net",
+ "net.InterfaceAddrs": "net",
+ "net.InterfaceByIndex": "net",
+ "net.InterfaceByName": "net",
+ "net.Interfaces": "net",
+ "net.InvalidAddrError": "net",
+ "net.JoinHostPort": "net",
+ "net.Listen": "net",
+ "net.ListenIP": "net",
+ "net.ListenMulticastUDP": "net",
+ "net.ListenPacket": "net",
+ "net.ListenTCP": "net",
+ "net.ListenUDP": "net",
+ "net.ListenUnix": "net",
+ "net.ListenUnixgram": "net",
+ "net.Listener": "net",
+ "net.LookupAddr": "net",
+ "net.LookupCNAME": "net",
+ "net.LookupHost": "net",
+ "net.LookupIP": "net",
+ "net.LookupMX": "net",
+ "net.LookupNS": "net",
+ "net.LookupPort": "net",
+ "net.LookupSRV": "net",
+ "net.LookupTXT": "net",
+ "net.MX": "net",
+ "net.NS": "net",
+ "net.OpError": "net",
+ "net.PacketConn": "net",
+ "net.ParseCIDR": "net",
+ "net.ParseError": "net",
+ "net.ParseIP": "net",
+ "net.ParseMAC": "net",
+ "net.Pipe": "net",
+ "net.ResolveIPAddr": "net",
+ "net.ResolveTCPAddr": "net",
+ "net.ResolveUDPAddr": "net",
+ "net.ResolveUnixAddr": "net",
+ "net.SRV": "net",
+ "net.SplitHostPort": "net",
+ "net.TCPAddr": "net",
+ "net.TCPConn": "net",
+ "net.TCPListener": "net",
+ "net.UDPAddr": "net",
+ "net.UDPConn": "net",
+ "net.UnixAddr": "net",
+ "net.UnixConn": "net",
+ "net.UnixListener": "net",
+ "net.UnknownNetworkError": "net",
+ "os.Args": "os",
+ "os.Chdir": "os",
+ "os.Chmod": "os",
+ "os.Chown": "os",
+ "os.Chtimes": "os",
+ "os.Clearenv": "os",
+ "os.Create": "os",
+ "os.DevNull": "os",
+ "os.Environ": "os",
+ "os.ErrExist": "os",
+ "os.ErrInvalid": "os",
+ "os.ErrNotExist": "os",
+ "os.ErrPermission": "os",
+ "os.Exit": "os",
+ "os.Expand": "os",
+ "os.ExpandEnv": "os",
+ "os.File": "os",
+ "os.FileInfo": "os",
+ "os.FileMode": "os",
+ "os.FindProcess": "os",
+ "os.Getegid": "os",
+ "os.Getenv": "os",
+ "os.Geteuid": "os",
+ "os.Getgid": "os",
+ "os.Getgroups": "os",
+ "os.Getpagesize": "os",
+ "os.Getpid": "os",
+ "os.Getppid": "os",
+ "os.Getuid": "os",
+ "os.Getwd": "os",
+ "os.Hostname": "os",
+ "os.Interrupt": "os",
+ "os.IsExist": "os",
+ "os.IsNotExist": "os",
+ "os.IsPathSeparator": "os",
+ "os.IsPermission": "os",
+ "os.Kill": "os",
+ "os.Lchown": "os",
+ "os.Link": "os",
+ "os.LinkError": "os",
+ "os.LookupEnv": "os",
+ "os.Lstat": "os",
+ "os.Mkdir": "os",
+ "os.MkdirAll": "os",
+ "os.ModeAppend": "os",
+ "os.ModeCharDevice": "os",
+ "os.ModeDevice": "os",
+ "os.ModeDir": "os",
+ "os.ModeExclusive": "os",
+ "os.ModeNamedPipe": "os",
+ "os.ModePerm": "os",
+ "os.ModeSetgid": "os",
+ "os.ModeSetuid": "os",
+ "os.ModeSocket": "os",
+ "os.ModeSticky": "os",
+ "os.ModeSymlink": "os",
+ "os.ModeTemporary": "os",
+ "os.ModeType": "os",
+ "os.NewFile": "os",
+ "os.NewSyscallError": "os",
+ "os.O_APPEND": "os",
+ "os.O_CREATE": "os",
+ "os.O_EXCL": "os",
+ "os.O_RDONLY": "os",
+ "os.O_RDWR": "os",
+ "os.O_SYNC": "os",
+ "os.O_TRUNC": "os",
+ "os.O_WRONLY": "os",
+ "os.Open": "os",
+ "os.OpenFile": "os",
+ "os.PathError": "os",
+ "os.PathListSeparator": "os",
+ "os.PathSeparator": "os",
+ "os.Pipe": "os",
+ "os.ProcAttr": "os",
+ "os.Process": "os",
+ "os.ProcessState": "os",
+ "os.Readlink": "os",
+ "os.Remove": "os",
+ "os.RemoveAll": "os",
+ "os.Rename": "os",
+ "os.SEEK_CUR": "os",
+ "os.SEEK_END": "os",
+ "os.SEEK_SET": "os",
+ "os.SameFile": "os",
+ "os.Setenv": "os",
+ "os.Signal": "os",
+ "os.StartProcess": "os",
+ "os.Stat": "os",
+ "os.Stderr": "os",
+ "os.Stdin": "os",
+ "os.Stdout": "os",
+ "os.Symlink": "os",
+ "os.SyscallError": "os",
+ "os.TempDir": "os",
+ "os.Truncate": "os",
+ "os.Unsetenv": "os",
+ "palette.Plan9": "image/color/palette",
+ "palette.WebSafe": "image/color/palette",
+ "parse.ActionNode": "text/template/parse",
+ "parse.BoolNode": "text/template/parse",
+ "parse.BranchNode": "text/template/parse",
+ "parse.ChainNode": "text/template/parse",
+ "parse.CommandNode": "text/template/parse",
+ "parse.DotNode": "text/template/parse",
+ "parse.FieldNode": "text/template/parse",
+ "parse.IdentifierNode": "text/template/parse",
+ "parse.IfNode": "text/template/parse",
+ "parse.IsEmptyTree": "text/template/parse",
+ "parse.ListNode": "text/template/parse",
+ "parse.New": "text/template/parse",
+ "parse.NewIdentifier": "text/template/parse",
+ "parse.NilNode": "text/template/parse",
+ "parse.Node": "text/template/parse",
+ "parse.NodeAction": "text/template/parse",
+ "parse.NodeBool": "text/template/parse",
+ "parse.NodeChain": "text/template/parse",
+ "parse.NodeCommand": "text/template/parse",
+ "parse.NodeDot": "text/template/parse",
+ "parse.NodeField": "text/template/parse",
+ "parse.NodeIdentifier": "text/template/parse",
+ "parse.NodeIf": "text/template/parse",
+ "parse.NodeList": "text/template/parse",
+ "parse.NodeNil": "text/template/parse",
+ "parse.NodeNumber": "text/template/parse",
+ "parse.NodePipe": "text/template/parse",
+ "parse.NodeRange": "text/template/parse",
+ "parse.NodeString": "text/template/parse",
+ "parse.NodeTemplate": "text/template/parse",
+ "parse.NodeText": "text/template/parse",
+ "parse.NodeType": "text/template/parse",
+ "parse.NodeVariable": "text/template/parse",
+ "parse.NodeWith": "text/template/parse",
+ "parse.NumberNode": "text/template/parse",
+ "parse.Parse": "text/template/parse",
+ "parse.PipeNode": "text/template/parse",
+ "parse.Pos": "text/template/parse",
+ "parse.RangeNode": "text/template/parse",
+ "parse.StringNode": "text/template/parse",
+ "parse.TemplateNode": "text/template/parse",
+ "parse.TextNode": "text/template/parse",
+ "parse.Tree": "text/template/parse",
+ "parse.VariableNode": "text/template/parse",
+ "parse.WithNode": "text/template/parse",
+ "parser.AllErrors": "go/parser",
+ "parser.DeclarationErrors": "go/parser",
+ "parser.ImportsOnly": "go/parser",
+ "parser.Mode": "go/parser",
+ "parser.PackageClauseOnly": "go/parser",
+ "parser.ParseComments": "go/parser",
+ "parser.ParseDir": "go/parser",
+ "parser.ParseExpr": "go/parser",
+ "parser.ParseExprFrom": "go/parser",
+ "parser.ParseFile": "go/parser",
+ "parser.SpuriousErrors": "go/parser",
+ "parser.Trace": "go/parser",
+ "path.Base": "path",
+ "path.Clean": "path",
+ "path.Dir": "path",
+ "path.ErrBadPattern": "path",
+ "path.Ext": "path",
+ "path.IsAbs": "path",
+ "path.Join": "path",
+ "path.Match": "path",
+ "path.Split": "path",
+ "pe.COFFSymbol": "debug/pe",
+ "pe.COFFSymbolSize": "debug/pe",
+ "pe.DataDirectory": "debug/pe",
+ "pe.File": "debug/pe",
+ "pe.FileHeader": "debug/pe",
+ "pe.FormatError": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_AM33": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_AMD64": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_ARM": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_EBC": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_I386": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_IA64": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_M32R": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_MIPS16": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_MIPSFPU": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_MIPSFPU16": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_POWERPC": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_POWERPCFP": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_R4000": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_SH3": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_SH3DSP": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_SH4": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_SH5": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_THUMB": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_UNKNOWN": "debug/pe",
+ "pe.IMAGE_FILE_MACHINE_WCEMIPSV2": "debug/pe",
+ "pe.ImportDirectory": "debug/pe",
+ "pe.NewFile": "debug/pe",
+ "pe.Open": "debug/pe",
+ "pe.OptionalHeader32": "debug/pe",
+ "pe.OptionalHeader64": "debug/pe",
+ "pe.Section": "debug/pe",
+ "pe.SectionHeader": "debug/pe",
+ "pe.SectionHeader32": "debug/pe",
+ "pe.Symbol": "debug/pe",
+ "pem.Block": "encoding/pem",
+ "pem.Decode": "encoding/pem",
+ "pem.Encode": "encoding/pem",
+ "pem.EncodeToMemory": "encoding/pem",
+ "pkix.AlgorithmIdentifier": "crypto/x509/pkix",
+ "pkix.AttributeTypeAndValue": "crypto/x509/pkix",
+ "pkix.AttributeTypeAndValueSET": "crypto/x509/pkix",
+ "pkix.CertificateList": "crypto/x509/pkix",
+ "pkix.Extension": "crypto/x509/pkix",
+ "pkix.Name": "crypto/x509/pkix",
+ "pkix.RDNSequence": "crypto/x509/pkix",
+ "pkix.RelativeDistinguishedNameSET": "crypto/x509/pkix",
+ "pkix.RevokedCertificate": "crypto/x509/pkix",
+ "pkix.TBSCertificateList": "crypto/x509/pkix",
+ "plan9obj.File": "debug/plan9obj",
+ "plan9obj.FileHeader": "debug/plan9obj",
+ "plan9obj.Magic386": "debug/plan9obj",
+ "plan9obj.Magic64": "debug/plan9obj",
+ "plan9obj.MagicAMD64": "debug/plan9obj",
+ "plan9obj.MagicARM": "debug/plan9obj",
+ "plan9obj.NewFile": "debug/plan9obj",
+ "plan9obj.Open": "debug/plan9obj",
+ "plan9obj.Section": "debug/plan9obj",
+ "plan9obj.SectionHeader": "debug/plan9obj",
+ "plan9obj.Sym": "debug/plan9obj",
+ "png.BestCompression": "image/png",
+ "png.BestSpeed": "image/png",
+ "png.CompressionLevel": "image/png",
+ "png.Decode": "image/png",
+ "png.DecodeConfig": "image/png",
+ "png.DefaultCompression": "image/png",
+ "png.Encode": "image/png",
+ "png.Encoder": "image/png",
+ "png.FormatError": "image/png",
+ "png.NoCompression": "image/png",
+ "png.UnsupportedError": "image/png",
+ "pprof.Cmdline": "net/http/pprof",
+ "pprof.Handler": "net/http/pprof",
+ "pprof.Index": "net/http/pprof",
+ "pprof.Lookup": "runtime/pprof",
+ "pprof.NewProfile": "runtime/pprof",
// "pprof.Profile" is ambiguous
- "pprof.Profiles": "runtime/pprof",
- "pprof.StartCPUProfile": "runtime/pprof",
- "pprof.StopCPUProfile": "runtime/pprof",
- "pprof.Symbol": "net/http/pprof",
- "pprof.WriteHeapProfile": "runtime/pprof",
- "printer.CommentedNode": "go/printer",
- "printer.Config": "go/printer",
- "printer.Fprint": "go/printer",
- "printer.Mode": "go/printer",
- "printer.RawFormat": "go/printer",
- "printer.SourcePos": "go/printer",
- "printer.TabIndent": "go/printer",
- "printer.UseSpaces": "go/printer",
- "quick.Check": "testing/quick",
- "quick.CheckEqual": "testing/quick",
- "quick.CheckEqualError": "testing/quick",
- "quick.CheckError": "testing/quick",
- "quick.Config": "testing/quick",
- "quick.Generator": "testing/quick",
- "quick.SetupError": "testing/quick",
- "quick.Value": "testing/quick",
- "rand.ExpFloat64": "math/rand",
- "rand.Float32": "math/rand",
- "rand.Float64": "math/rand",
+ "pprof.Profiles": "runtime/pprof",
+ "pprof.StartCPUProfile": "runtime/pprof",
+ "pprof.StopCPUProfile": "runtime/pprof",
+ "pprof.Symbol": "net/http/pprof",
+ "pprof.Trace": "net/http/pprof",
+ "pprof.WriteHeapProfile": "runtime/pprof",
+ "printer.CommentedNode": "go/printer",
+ "printer.Config": "go/printer",
+ "printer.Fprint": "go/printer",
+ "printer.Mode": "go/printer",
+ "printer.RawFormat": "go/printer",
+ "printer.SourcePos": "go/printer",
+ "printer.TabIndent": "go/printer",
+ "printer.UseSpaces": "go/printer",
+ "quick.Check": "testing/quick",
+ "quick.CheckEqual": "testing/quick",
+ "quick.CheckEqualError": "testing/quick",
+ "quick.CheckError": "testing/quick",
+ "quick.Config": "testing/quick",
+ "quick.Generator": "testing/quick",
+ "quick.SetupError": "testing/quick",
+ "quick.Value": "testing/quick",
+ "quotedprintable.NewReader": "mime/quotedprintable",
+ "quotedprintable.NewWriter": "mime/quotedprintable",
+ "quotedprintable.Reader": "mime/quotedprintable",
+ "quotedprintable.Writer": "mime/quotedprintable",
+ "rand.ExpFloat64": "math/rand",
+ "rand.Float32": "math/rand",
+ "rand.Float64": "math/rand",
// "rand.Int" is ambiguous
"rand.Int31": "math/rand",
"rand.Int31n": "math/rand",
@@ -2191,6 +2573,7 @@
"reflect.Append": "reflect",
"reflect.AppendSlice": "reflect",
"reflect.Array": "reflect",
+ "reflect.ArrayOf": "reflect",
"reflect.Bool": "reflect",
"reflect.BothDir": "reflect",
"reflect.Chan": "reflect",
@@ -2203,6 +2586,7 @@
"reflect.Float32": "reflect",
"reflect.Float64": "reflect",
"reflect.Func": "reflect",
+ "reflect.FuncOf": "reflect",
"reflect.Indirect": "reflect",
"reflect.Int": "reflect",
"reflect.Int16": "reflect",
@@ -2298,6 +2682,8 @@
"rsa.ErrVerification": "crypto/rsa",
"rsa.GenerateKey": "crypto/rsa",
"rsa.GenerateMultiPrimeKey": "crypto/rsa",
+ "rsa.OAEPOptions": "crypto/rsa",
+ "rsa.PKCS1v15DecryptOptions": "crypto/rsa",
"rsa.PSSOptions": "crypto/rsa",
"rsa.PSSSaltLengthAuto": "crypto/rsa",
"rsa.PSSSaltLengthEqualsHash": "crypto/rsa",
@@ -2335,11 +2721,14 @@
"runtime.NumCgoCall": "runtime",
"runtime.NumGoroutine": "runtime",
"runtime.ReadMemStats": "runtime",
+ "runtime.ReadTrace": "runtime",
"runtime.SetBlockProfileRate": "runtime",
"runtime.SetCPUProfileRate": "runtime",
"runtime.SetFinalizer": "runtime",
"runtime.Stack": "runtime",
"runtime.StackRecord": "runtime",
+ "runtime.StartTrace": "runtime",
+ "runtime.StopTrace": "runtime",
"runtime.ThreadCreateProfile": "runtime",
"runtime.TypeAssertionError": "runtime",
"runtime.UnlockOSThread": "runtime",
@@ -2384,11 +2773,19 @@
"sha512.BlockSize": "crypto/sha512",
"sha512.New": "crypto/sha512",
"sha512.New384": "crypto/sha512",
+ "sha512.New512_224": "crypto/sha512",
+ "sha512.New512_256": "crypto/sha512",
"sha512.Size": "crypto/sha512",
+ "sha512.Size224": "crypto/sha512",
+ "sha512.Size256": "crypto/sha512",
"sha512.Size384": "crypto/sha512",
"sha512.Sum384": "crypto/sha512",
"sha512.Sum512": "crypto/sha512",
+ "sha512.Sum512_224": "crypto/sha512",
+ "sha512.Sum512_256": "crypto/sha512",
+ "signal.Ignore": "os/signal",
"signal.Notify": "os/signal",
+ "signal.Reset": "os/signal",
"signal.Stop": "os/signal",
"smtp.Auth": "net/smtp",
"smtp.CRAMMD5Auth": "net/smtp",
@@ -2417,6 +2814,8 @@
"sort.Strings": "sort",
"sort.StringsAreSorted": "sort",
"sql.DB": "database/sql",
+ "sql.DBStats": "database/sql",
+ "sql.Drivers": "database/sql",
"sql.ErrNoRows": "database/sql",
"sql.ErrTxDone": "database/sql",
"sql.NullBool": "database/sql",
@@ -2462,6 +2861,7 @@
"strconv.QuoteToASCII": "strconv",
"strconv.Unquote": "strconv",
"strconv.UnquoteChar": "strconv",
+ "strings.Compare": "strings",
"strings.Contains": "strings",
"strings.ContainsAny": "strings",
"strings.ContainsRune": "strings",
@@ -2479,6 +2879,7 @@
"strings.Join": "strings",
"strings.LastIndex": "strings",
"strings.LastIndexAny": "strings",
+ "strings.LastIndexByte": "strings",
"strings.LastIndexFunc": "strings",
"strings.Map": "strings",
"strings.NewReader": "strings",
@@ -2520,6 +2921,7 @@
"sync.Mutex": "sync",
"sync.NewCond": "sync",
"sync.Once": "sync",
+ "sync.Pool": "sync",
"sync.RWMutex": "sync",
"sync.WaitGroup": "sync",
"syntax.ClassNL": "regexp/syntax",
@@ -2628,6 +3030,8 @@
"syscall.AF_IMPLINK": "syscall",
"syscall.AF_INET": "syscall",
"syscall.AF_INET6": "syscall",
+ "syscall.AF_INET6_SDP": "syscall",
+ "syscall.AF_INET_SDP": "syscall",
"syscall.AF_IPX": "syscall",
"syscall.AF_IRDA": "syscall",
"syscall.AF_ISDN": "syscall",
@@ -3005,6 +3409,7 @@
"syscall.CLOCAL": "syscall",
"syscall.CLONE_CHILD_CLEARTID": "syscall",
"syscall.CLONE_CHILD_SETTID": "syscall",
+ "syscall.CLONE_CSIGNAL": "syscall",
"syscall.CLONE_DETACHED": "syscall",
"syscall.CLONE_FILES": "syscall",
"syscall.CLONE_FS": "syscall",
@@ -3017,6 +3422,7 @@
"syscall.CLONE_NEWUTS": "syscall",
"syscall.CLONE_PARENT": "syscall",
"syscall.CLONE_PARENT_SETTID": "syscall",
+ "syscall.CLONE_PID": "syscall",
"syscall.CLONE_PTRACE": "syscall",
"syscall.CLONE_SETTLS": "syscall",
"syscall.CLONE_SIGHAND": "syscall",
@@ -3100,9 +3506,12 @@
"syscall.CreateDirectory": "syscall",
"syscall.CreateFile": "syscall",
"syscall.CreateFileMapping": "syscall",
+ "syscall.CreateHardLink": "syscall",
"syscall.CreateIoCompletionPort": "syscall",
"syscall.CreatePipe": "syscall",
"syscall.CreateProcess": "syscall",
+ "syscall.CreateSymbolicLink": "syscall",
+ "syscall.CreateToolhelp32Snapshot": "syscall",
"syscall.Credential": "syscall",
"syscall.CryptAcquireContext": "syscall",
"syscall.CryptGenRandom": "syscall",
@@ -3281,6 +3690,7 @@
"syscall.DNSRecord": "syscall",
"syscall.DNSSRVData": "syscall",
"syscall.DNSTXTData": "syscall",
+ "syscall.DNS_INFO_NO_RECORDS": "syscall",
"syscall.DNS_TYPE_A": "syscall",
"syscall.DNS_TYPE_A6": "syscall",
"syscall.DNS_TYPE_AAAA": "syscall",
@@ -3356,9 +3766,15 @@
"syscall.DUPLICATE_SAME_ACCESS": "syscall",
"syscall.DeleteFile": "syscall",
"syscall.DetachLsf": "syscall",
+ "syscall.DeviceIoControl": "syscall",
"syscall.Dirent": "syscall",
+ "syscall.DnsNameCompare": "syscall",
"syscall.DnsQuery": "syscall",
"syscall.DnsRecordListFree": "syscall",
+ "syscall.DnsSectionAdditional": "syscall",
+ "syscall.DnsSectionAnswer": "syscall",
+ "syscall.DnsSectionAuthority": "syscall",
+ "syscall.DnsSectionQuestion": "syscall",
"syscall.Dup": "syscall",
"syscall.Dup2": "syscall",
"syscall.Dup3": "syscall",
@@ -3560,10 +3976,13 @@
"syscall.ERROR_INSUFFICIENT_BUFFER": "syscall",
"syscall.ERROR_IO_PENDING": "syscall",
"syscall.ERROR_MOD_NOT_FOUND": "syscall",
+ "syscall.ERROR_MORE_DATA": "syscall",
+ "syscall.ERROR_NETNAME_DELETED": "syscall",
"syscall.ERROR_NOT_FOUND": "syscall",
"syscall.ERROR_NO_MORE_FILES": "syscall",
"syscall.ERROR_OPERATION_ABORTED": "syscall",
"syscall.ERROR_PATH_NOT_FOUND": "syscall",
+ "syscall.ERROR_PRIVILEGE_NOT_HELD": "syscall",
"syscall.ERROR_PROC_NOT_FOUND": "syscall",
"syscall.ESHLIBVERS": "syscall",
"syscall.ESHUTDOWN": "syscall",
@@ -3868,6 +4287,7 @@
"syscall.EV_DELETE": "syscall",
"syscall.EV_DISABLE": "syscall",
"syscall.EV_DISPATCH": "syscall",
+ "syscall.EV_DROP": "syscall",
"syscall.EV_ENABLE": "syscall",
"syscall.EV_EOF": "syscall",
"syscall.EV_ERROR": "syscall",
@@ -3910,11 +4330,13 @@
"syscall.FILE_ATTRIBUTE_HIDDEN": "syscall",
"syscall.FILE_ATTRIBUTE_NORMAL": "syscall",
"syscall.FILE_ATTRIBUTE_READONLY": "syscall",
+ "syscall.FILE_ATTRIBUTE_REPARSE_POINT": "syscall",
"syscall.FILE_ATTRIBUTE_SYSTEM": "syscall",
"syscall.FILE_BEGIN": "syscall",
"syscall.FILE_CURRENT": "syscall",
"syscall.FILE_END": "syscall",
"syscall.FILE_FLAG_BACKUP_SEMANTICS": "syscall",
+ "syscall.FILE_FLAG_OPEN_REPARSE_POINT": "syscall",
"syscall.FILE_FLAG_OVERLAPPED": "syscall",
"syscall.FILE_LIST_DIRECTORY": "syscall",
"syscall.FILE_MAP_COPY": "syscall",
@@ -3947,6 +4369,7 @@
"syscall.FORMAT_MESSAGE_FROM_SYSTEM": "syscall",
"syscall.FORMAT_MESSAGE_IGNORE_INSERTS": "syscall",
"syscall.FORMAT_MESSAGE_MAX_WIDTH_MASK": "syscall",
+ "syscall.FSCTL_GET_REPARSE_POINT": "syscall",
"syscall.F_ADDFILESIGS": "syscall",
"syscall.F_ADDSIGS": "syscall",
"syscall.F_ALLOCATEALL": "syscall",
@@ -4041,6 +4464,7 @@
"syscall.Fchmodat": "syscall",
"syscall.Fchown": "syscall",
"syscall.Fchownat": "syscall",
+ "syscall.FcntlFlock": "syscall",
"syscall.FdSet": "syscall",
"syscall.Fdatasync": "syscall",
"syscall.FileNotifyInformation": "syscall",
@@ -4066,6 +4490,7 @@
"syscall.Fstore_t": "syscall",
"syscall.Fsync": "syscall",
"syscall.Ftruncate": "syscall",
+ "syscall.FullPath": "syscall",
"syscall.Futimes": "syscall",
"syscall.Futimesat": "syscall",
"syscall.GENERIC_ALL": "syscall",
@@ -4532,7 +4957,9 @@
"syscall.IOC_IN": "syscall",
"syscall.IOC_INOUT": "syscall",
"syscall.IOC_OUT": "syscall",
+ "syscall.IOC_VENDOR": "syscall",
"syscall.IOC_WS2": "syscall",
+ "syscall.IO_REPARSE_TAG_SYMLINK": "syscall",
"syscall.IPMreq": "syscall",
"syscall.IPMreqn": "syscall",
"syscall.IPPROTO_3PC": "syscall",
@@ -4556,6 +4983,8 @@
"syscall.IPPROTO_DDP": "syscall",
"syscall.IPPROTO_DGP": "syscall",
"syscall.IPPROTO_DIVERT": "syscall",
+ "syscall.IPPROTO_DIVERT_INIT": "syscall",
+ "syscall.IPPROTO_DIVERT_RESP": "syscall",
"syscall.IPPROTO_DONE": "syscall",
"syscall.IPPROTO_DSTOPTS": "syscall",
"syscall.IPPROTO_EGP": "syscall",
@@ -4729,6 +5158,7 @@
"syscall.IPV6_PORTRANGE_LOW": "syscall",
"syscall.IPV6_PREFER_TEMPADDR": "syscall",
"syscall.IPV6_RECVDSTOPTS": "syscall",
+ "syscall.IPV6_RECVDSTPORT": "syscall",
"syscall.IPV6_RECVERR": "syscall",
"syscall.IPV6_RECVHOPLIMIT": "syscall",
"syscall.IPV6_RECVHOPOPTS": "syscall",
@@ -4762,6 +5192,7 @@
"syscall.IP_DEFAULT_MULTICAST_LOOP": "syscall",
"syscall.IP_DEFAULT_MULTICAST_TTL": "syscall",
"syscall.IP_DF": "syscall",
+ "syscall.IP_DIVERTFL": "syscall",
"syscall.IP_DONTFRAG": "syscall",
"syscall.IP_DROP_MEMBERSHIP": "syscall",
"syscall.IP_DROP_SOURCE_MEMBERSHIP": "syscall",
@@ -4954,6 +5385,7 @@
"syscall.Listxattr": "syscall",
"syscall.LoadCancelIoEx": "syscall",
"syscall.LoadConnectEx": "syscall",
+ "syscall.LoadCreateSymbolicLink": "syscall",
"syscall.LoadDLL": "syscall",
"syscall.LoadGetAddrInfo": "syscall",
"syscall.LoadLibrary": "syscall",
@@ -4987,10 +5419,20 @@
"syscall.MADV_RANDOM": "syscall",
"syscall.MADV_REMOVE": "syscall",
"syscall.MADV_SEQUENTIAL": "syscall",
+ "syscall.MADV_SPACEAVAIL": "syscall",
"syscall.MADV_UNMERGEABLE": "syscall",
"syscall.MADV_WILLNEED": "syscall",
"syscall.MADV_ZERO_WIRED_PAGES": "syscall",
"syscall.MAP_32BIT": "syscall",
+ "syscall.MAP_ALIGNED_SUPER": "syscall",
+ "syscall.MAP_ALIGNMENT_16MB": "syscall",
+ "syscall.MAP_ALIGNMENT_1TB": "syscall",
+ "syscall.MAP_ALIGNMENT_256TB": "syscall",
+ "syscall.MAP_ALIGNMENT_4GB": "syscall",
+ "syscall.MAP_ALIGNMENT_64KB": "syscall",
+ "syscall.MAP_ALIGNMENT_64PB": "syscall",
+ "syscall.MAP_ALIGNMENT_MASK": "syscall",
+ "syscall.MAP_ALIGNMENT_SHIFT": "syscall",
"syscall.MAP_ANON": "syscall",
"syscall.MAP_ANONYMOUS": "syscall",
"syscall.MAP_COPY": "syscall",
@@ -4998,9 +5440,16 @@
"syscall.MAP_EXECUTABLE": "syscall",
"syscall.MAP_FILE": "syscall",
"syscall.MAP_FIXED": "syscall",
+ "syscall.MAP_FLAGMASK": "syscall",
"syscall.MAP_GROWSDOWN": "syscall",
"syscall.MAP_HASSEMAPHORE": "syscall",
"syscall.MAP_HUGETLB": "syscall",
+ "syscall.MAP_INHERIT": "syscall",
+ "syscall.MAP_INHERIT_COPY": "syscall",
+ "syscall.MAP_INHERIT_DEFAULT": "syscall",
+ "syscall.MAP_INHERIT_DONATE_COPY": "syscall",
+ "syscall.MAP_INHERIT_NONE": "syscall",
+ "syscall.MAP_INHERIT_SHARE": "syscall",
"syscall.MAP_JIT": "syscall",
"syscall.MAP_LOCKED": "syscall",
"syscall.MAP_NOCACHE": "syscall",
@@ -5017,7 +5466,10 @@
"syscall.MAP_RESERVED0100": "syscall",
"syscall.MAP_SHARED": "syscall",
"syscall.MAP_STACK": "syscall",
+ "syscall.MAP_TRYFIXED": "syscall",
"syscall.MAP_TYPE": "syscall",
+ "syscall.MAP_WIRED": "syscall",
+ "syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE": "syscall",
"syscall.MAXLEN_IFDESCR": "syscall",
"syscall.MAXLEN_PHYSADDR": "syscall",
"syscall.MAX_ADAPTER_ADDRESS_LENGTH": "syscall",
@@ -5250,6 +5702,7 @@
"syscall.NetlinkRouteAttr": "syscall",
"syscall.NetlinkRouteRequest": "syscall",
"syscall.NewCallback": "syscall",
+ "syscall.NewCallbackCDecl": "syscall",
"syscall.NewLazyDLL": "syscall",
"syscall.NlAttr": "syscall",
"syscall.NlMsgerr": "syscall",
@@ -5533,6 +5986,9 @@
"syscall.Pread": "syscall",
"syscall.Proc": "syscall",
"syscall.ProcAttr": "syscall",
+ "syscall.Process32First": "syscall",
+ "syscall.Process32Next": "syscall",
+ "syscall.ProcessEntry32": "syscall",
"syscall.ProcessInformation": "syscall",
"syscall.Protoent": "syscall",
"syscall.PtraceAttach": "syscall",
@@ -5651,6 +6107,7 @@
"syscall.RTF_FLOW": "syscall",
"syscall.RTF_FMASK": "syscall",
"syscall.RTF_GATEWAY": "syscall",
+ "syscall.RTF_GWFLAG_COMPAT": "syscall",
"syscall.RTF_HOST": "syscall",
"syscall.RTF_IFREF": "syscall",
"syscall.RTF_IFSCOPE": "syscall",
@@ -6026,6 +6483,7 @@
"syscall.SIOCGIFGENERIC": "syscall",
"syscall.SIOCGIFGMEMB": "syscall",
"syscall.SIOCGIFGROUP": "syscall",
+ "syscall.SIOCGIFHARDMTU": "syscall",
"syscall.SIOCGIFHWADDR": "syscall",
"syscall.SIOCGIFINDEX": "syscall",
"syscall.SIOCGIFKPI": "syscall",
@@ -6054,15 +6512,18 @@
"syscall.SIOCGLIFADDR": "syscall",
"syscall.SIOCGLIFPHYADDR": "syscall",
"syscall.SIOCGLIFPHYRTABLE": "syscall",
+ "syscall.SIOCGLIFPHYTTL": "syscall",
"syscall.SIOCGLINKSTR": "syscall",
"syscall.SIOCGLOWAT": "syscall",
"syscall.SIOCGPGRP": "syscall",
"syscall.SIOCGPRIVATE_0": "syscall",
"syscall.SIOCGPRIVATE_1": "syscall",
"syscall.SIOCGRARP": "syscall",
+ "syscall.SIOCGSPPPPARAMS": "syscall",
"syscall.SIOCGSTAMP": "syscall",
"syscall.SIOCGSTAMPNS": "syscall",
"syscall.SIOCGVH": "syscall",
+ "syscall.SIOCGVNETID": "syscall",
"syscall.SIOCIFCREATE": "syscall",
"syscall.SIOCIFCREATE2": "syscall",
"syscall.SIOCIFDESTROY": "syscall",
@@ -6122,14 +6583,19 @@
"syscall.SIOCSIFXFLAGS": "syscall",
"syscall.SIOCSLIFPHYADDR": "syscall",
"syscall.SIOCSLIFPHYRTABLE": "syscall",
+ "syscall.SIOCSLIFPHYTTL": "syscall",
"syscall.SIOCSLINKSTR": "syscall",
"syscall.SIOCSLOWAT": "syscall",
"syscall.SIOCSPGRP": "syscall",
"syscall.SIOCSRARP": "syscall",
+ "syscall.SIOCSSPPPPARAMS": "syscall",
"syscall.SIOCSVH": "syscall",
+ "syscall.SIOCSVNETID": "syscall",
"syscall.SIOCZIFDATA": "syscall",
"syscall.SIO_GET_EXTENSION_FUNCTION_POINTER": "syscall",
"syscall.SIO_GET_INTERFACE_LIST": "syscall",
+ "syscall.SIO_KEEPALIVE_VALS": "syscall",
+ "syscall.SIO_UDP_CONNRESET": "syscall",
"syscall.SOCK_CLOEXEC": "syscall",
"syscall.SOCK_DCCP": "syscall",
"syscall.SOCK_DGRAM": "syscall",
@@ -6233,6 +6699,7 @@
"syscall.SO_UPDATE_CONNECT_CONTEXT": "syscall",
"syscall.SO_USELOOPBACK": "syscall",
"syscall.SO_USER_COOKIE": "syscall",
+ "syscall.SO_VENDOR": "syscall",
"syscall.SO_WANTMORE": "syscall",
"syscall.SO_WANTOOBFLAG": "syscall",
"syscall.SSLExtraCertChainPolicyPara": "syscall",
@@ -6261,6 +6728,7 @@
"syscall.SW_SHOWNA": "syscall",
"syscall.SW_SHOWNOACTIVATE": "syscall",
"syscall.SW_SHOWNORMAL": "syscall",
+ "syscall.SYMBOLIC_LINK_FLAG_DIRECTORY": "syscall",
"syscall.SYNCHRONIZE": "syscall",
"syscall.SYSCTL_VERSION": "syscall",
"syscall.SYSCTL_VERS_0": "syscall",
@@ -6306,6 +6774,7 @@
"syscall.SYS_AUDIT_SESSION_SELF": "syscall",
"syscall.SYS_BDFLUSH": "syscall",
"syscall.SYS_BIND": "syscall",
+ "syscall.SYS_BINDAT": "syscall",
"syscall.SYS_BREAK": "syscall",
"syscall.SYS_BRK": "syscall",
"syscall.SYS_BSDTHREAD_CREATE": "syscall",
@@ -6325,6 +6794,7 @@
"syscall.SYS_CAP_RIGHTS_LIMIT": "syscall",
"syscall.SYS_CHDIR": "syscall",
"syscall.SYS_CHFLAGS": "syscall",
+ "syscall.SYS_CHFLAGSAT": "syscall",
"syscall.SYS_CHMOD": "syscall",
"syscall.SYS_CHMOD_EXTENDED": "syscall",
"syscall.SYS_CHOWN": "syscall",
@@ -6342,6 +6812,7 @@
"syscall.SYS_CLOSEFROM": "syscall",
"syscall.SYS_CLOSE_NOCANCEL": "syscall",
"syscall.SYS_CONNECT": "syscall",
+ "syscall.SYS_CONNECTAT": "syscall",
"syscall.SYS_CONNECT_NOCANCEL": "syscall",
"syscall.SYS_COPYFILE": "syscall",
"syscall.SYS_CPUSET": "syscall",
@@ -6698,6 +7169,7 @@
"syscall.SYS_PREADV": "syscall",
"syscall.SYS_PREAD_NOCANCEL": "syscall",
"syscall.SYS_PRLIMIT64": "syscall",
+ "syscall.SYS_PROCCTL": "syscall",
"syscall.SYS_PROCESS_POLICY": "syscall",
"syscall.SYS_PROCESS_VM_READV": "syscall",
"syscall.SYS_PROCESS_VM_WRITEV": "syscall",
@@ -7118,6 +7590,7 @@
"syscall.Select": "syscall",
"syscall.Sendfile": "syscall",
"syscall.Sendmsg": "syscall",
+ "syscall.SendmsgN": "syscall",
"syscall.Sendto": "syscall",
"syscall.Servent": "syscall",
"syscall.SetBpf": "syscall",
@@ -7264,6 +7737,7 @@
"syscall.Sync": "syscall",
"syscall.SyncFileRange": "syscall",
"syscall.SysProcAttr": "syscall",
+ "syscall.SysProcIDMap": "syscall",
"syscall.Syscall": "syscall",
"syscall.Syscall12": "syscall",
"syscall.Syscall15": "syscall",
@@ -7280,6 +7754,7 @@
"syscall.TCIOFLUSH": "syscall",
"syscall.TCOFLUSH": "syscall",
"syscall.TCPInfo": "syscall",
+ "syscall.TCPKeepalive": "syscall",
"syscall.TCP_CA_NAME_MAX": "syscall",
"syscall.TCP_CONGCTL": "syscall",
"syscall.TCP_CONGESTION": "syscall",
@@ -7314,6 +7789,7 @@
"syscall.TCP_RXT_FINDROP": "syscall",
"syscall.TCP_SACK_ENABLE": "syscall",
"syscall.TCP_SYNCNT": "syscall",
+ "syscall.TCP_VENDOR": "syscall",
"syscall.TCP_WINDOW_CLAMP": "syscall",
"syscall.TCSAFLUSH": "syscall",
"syscall.TCSETS": "syscall",
@@ -7323,6 +7799,13 @@
"syscall.TF_USE_KERNEL_APC": "syscall",
"syscall.TF_USE_SYSTEM_THREAD": "syscall",
"syscall.TF_WRITE_BEHIND": "syscall",
+ "syscall.TH32CS_INHERIT": "syscall",
+ "syscall.TH32CS_SNAPALL": "syscall",
+ "syscall.TH32CS_SNAPHEAPLIST": "syscall",
+ "syscall.TH32CS_SNAPMODULE": "syscall",
+ "syscall.TH32CS_SNAPMODULE32": "syscall",
+ "syscall.TH32CS_SNAPPROCESS": "syscall",
+ "syscall.TH32CS_SNAPTHREAD": "syscall",
"syscall.TIME_ZONE_ID_DAYLIGHT": "syscall",
"syscall.TIME_ZONE_ID_STANDARD": "syscall",
"syscall.TIME_ZONE_ID_UNKNOWN": "syscall",
@@ -7540,6 +8023,7 @@
"syscall.Unlinkat": "syscall",
"syscall.UnmapViewOfFile": "syscall",
"syscall.Unmount": "syscall",
+ "syscall.Unsetenv": "syscall",
"syscall.Unshare": "syscall",
"syscall.UserInfo10": "syscall",
"syscall.Ustat": "syscall",
@@ -7597,6 +8081,7 @@
"syscall.WSADESCRIPTION_LEN": "syscall",
"syscall.WSAData": "syscall",
"syscall.WSAEACCES": "syscall",
+ "syscall.WSAECONNRESET": "syscall",
"syscall.WSAEnumProtocols": "syscall",
"syscall.WSAID_CONNECTEX": "syscall",
"syscall.WSAIoctl": "syscall",
@@ -7700,6 +8185,7 @@
"tar.TypeFifo": "archive/tar",
"tar.TypeGNULongLink": "archive/tar",
"tar.TypeGNULongName": "archive/tar",
+ "tar.TypeGNUSparse": "archive/tar",
"tar.TypeLink": "archive/tar",
"tar.TypeReg": "archive/tar",
"tar.TypeRegA": "archive/tar",
@@ -7745,10 +8231,14 @@
"testing.BenchmarkResult": "testing",
"testing.Cover": "testing",
"testing.CoverBlock": "testing",
+ "testing.Coverage": "testing",
"testing.InternalBenchmark": "testing",
"testing.InternalExample": "testing",
"testing.InternalTest": "testing",
+ "testing.M": "testing",
"testing.Main": "testing",
+ "testing.MainStart": "testing",
+ "testing.PB": "testing",
"testing.RegisterCover": "testing",
"testing.RunBenchmarks": "testing",
"testing.RunExamples": "testing",
@@ -7838,12 +8328,21 @@
"tls.Certificate": "crypto/tls",
"tls.Client": "crypto/tls",
"tls.ClientAuthType": "crypto/tls",
+ "tls.ClientHelloInfo": "crypto/tls",
+ "tls.ClientSessionCache": "crypto/tls",
+ "tls.ClientSessionState": "crypto/tls",
"tls.Config": "crypto/tls",
"tls.Conn": "crypto/tls",
"tls.ConnectionState": "crypto/tls",
+ "tls.CurveID": "crypto/tls",
+ "tls.CurveP256": "crypto/tls",
+ "tls.CurveP384": "crypto/tls",
+ "tls.CurveP521": "crypto/tls",
"tls.Dial": "crypto/tls",
+ "tls.DialWithDialer": "crypto/tls",
"tls.Listen": "crypto/tls",
"tls.LoadX509KeyPair": "crypto/tls",
+ "tls.NewLRUClientSessionCache": "crypto/tls",
"tls.NewListener": "crypto/tls",
"tls.NoClientCert": "crypto/tls",
"tls.RequestClientCert": "crypto/tls",
@@ -7853,12 +8352,15 @@
"tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": "crypto/tls",
"tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": "crypto/tls",
"tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": "crypto/tls",
+ "tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": "crypto/tls",
"tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": "crypto/tls",
"tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": "crypto/tls",
"tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": "crypto/tls",
"tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": "crypto/tls",
"tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": "crypto/tls",
+ "tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": "crypto/tls",
"tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA": "crypto/tls",
+ "tls.TLS_FALLBACK_SCSV": "crypto/tls",
"tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA": "crypto/tls",
"tls.TLS_RSA_WITH_AES_128_CBC_SHA": "crypto/tls",
"tls.TLS_RSA_WITH_AES_256_CBC_SHA": "crypto/tls",
@@ -7961,13 +8463,145 @@
"token.VAR": "go/token",
"token.XOR": "go/token",
"token.XOR_ASSIGN": "go/token",
+ "trace.Start": "runtime/trace",
+ "trace.Stop": "runtime/trace",
+ "types.Array": "go/types",
+ "types.AssertableTo": "go/types",
+ "types.AssignableTo": "go/types",
+ "types.Basic": "go/types",
+ "types.BasicInfo": "go/types",
+ "types.BasicKind": "go/types",
+ "types.Bool": "go/types",
+ "types.Builtin": "go/types",
+ "types.Byte": "go/types",
+ "types.Chan": "go/types",
+ "types.ChanDir": "go/types",
+ "types.Checker": "go/types",
+ "types.Comparable": "go/types",
+ "types.Complex128": "go/types",
+ "types.Complex64": "go/types",
+ "types.Config": "go/types",
+ "types.Const": "go/types",
+ "types.ConvertibleTo": "go/types",
+ "types.DefPredeclaredTestFuncs": "go/types",
+ "types.Error": "go/types",
+ "types.Eval": "go/types",
+ "types.ExprString": "go/types",
+ "types.FieldVal": "go/types",
+ "types.Float32": "go/types",
+ "types.Float64": "go/types",
+ "types.Func": "go/types",
+ "types.Id": "go/types",
+ "types.Identical": "go/types",
+ "types.Implements": "go/types",
+ "types.Importer": "go/types",
+ "types.Info": "go/types",
+ "types.Initializer": "go/types",
+ "types.Int": "go/types",
+ "types.Int16": "go/types",
+ "types.Int32": "go/types",
+ "types.Int64": "go/types",
+ "types.Int8": "go/types",
+ "types.Interface": "go/types",
+ "types.Invalid": "go/types",
+ "types.IsBoolean": "go/types",
+ "types.IsComplex": "go/types",
+ "types.IsConstType": "go/types",
+ "types.IsFloat": "go/types",
+ "types.IsInteger": "go/types",
+ "types.IsInterface": "go/types",
+ "types.IsNumeric": "go/types",
+ "types.IsOrdered": "go/types",
+ "types.IsString": "go/types",
+ "types.IsUnsigned": "go/types",
+ "types.IsUntyped": "go/types",
+ "types.Label": "go/types",
+ "types.LookupFieldOrMethod": "go/types",
+ "types.Map": "go/types",
+ "types.MethodExpr": "go/types",
+ "types.MethodSet": "go/types",
+ "types.MethodVal": "go/types",
+ "types.MissingMethod": "go/types",
+ "types.Named": "go/types",
+ "types.NewArray": "go/types",
+ "types.NewChan": "go/types",
+ "types.NewChecker": "go/types",
+ "types.NewConst": "go/types",
+ "types.NewField": "go/types",
+ "types.NewFunc": "go/types",
+ "types.NewInterface": "go/types",
+ "types.NewLabel": "go/types",
+ "types.NewMap": "go/types",
+ "types.NewMethodSet": "go/types",
+ "types.NewNamed": "go/types",
+ "types.NewPackage": "go/types",
+ "types.NewParam": "go/types",
+ "types.NewPkgName": "go/types",
+ "types.NewPointer": "go/types",
+ "types.NewScope": "go/types",
+ "types.NewSignature": "go/types",
+ "types.NewSlice": "go/types",
+ "types.NewStruct": "go/types",
+ "types.NewTuple": "go/types",
+ "types.NewTypeName": "go/types",
+ "types.NewVar": "go/types",
+ "types.Nil": "go/types",
+ "types.ObjectString": "go/types",
+ "types.Package": "go/types",
+ "types.PkgName": "go/types",
+ "types.Pointer": "go/types",
+ "types.Qualifier": "go/types",
+ "types.RecvOnly": "go/types",
+ "types.RelativeTo": "go/types",
+ "types.Rune": "go/types",
+ "types.Scope": "go/types",
+ "types.Selection": "go/types",
+ "types.SelectionKind": "go/types",
+ "types.SelectionString": "go/types",
+ "types.SendOnly": "go/types",
+ "types.SendRecv": "go/types",
+ "types.Signature": "go/types",
+ "types.Sizes": "go/types",
+ "types.Slice": "go/types",
+ "types.StdSizes": "go/types",
+ "types.String": "go/types",
+ "types.Struct": "go/types",
+ "types.Tuple": "go/types",
+ "types.Typ": "go/types",
+ "types.Type": "go/types",
+ "types.TypeAndValue": "go/types",
+ "types.TypeName": "go/types",
+ "types.TypeString": "go/types",
+ "types.Uint": "go/types",
+ "types.Uint16": "go/types",
+ "types.Uint32": "go/types",
+ "types.Uint64": "go/types",
+ "types.Uint8": "go/types",
+ "types.Uintptr": "go/types",
+ "types.Universe": "go/types",
+ "types.Unsafe": "go/types",
+ "types.UnsafePointer": "go/types",
+ "types.UntypedBool": "go/types",
+ "types.UntypedComplex": "go/types",
+ "types.UntypedFloat": "go/types",
+ "types.UntypedInt": "go/types",
+ "types.UntypedNil": "go/types",
+ "types.UntypedRune": "go/types",
+ "types.UntypedString": "go/types",
+ "types.Var": "go/types",
+ "types.WriteExpr": "go/types",
+ "types.WriteSignature": "go/types",
+ "types.WriteType": "go/types",
"unicode.ASCII_Hex_Digit": "unicode",
+ "unicode.Ahom": "unicode",
+ "unicode.Anatolian_Hieroglyphs": "unicode",
"unicode.Arabic": "unicode",
"unicode.Armenian": "unicode",
"unicode.Avestan": "unicode",
"unicode.AzeriCase": "unicode",
"unicode.Balinese": "unicode",
"unicode.Bamum": "unicode",
+ "unicode.Bassa_Vah": "unicode",
"unicode.Batak": "unicode",
"unicode.Bengali": "unicode",
"unicode.Bidi_Control": "unicode",
@@ -7982,6 +8616,7 @@
"unicode.CaseRange": "unicode",
"unicode.CaseRanges": "unicode",
"unicode.Categories": "unicode",
+ "unicode.Caucasian_Albanian": "unicode",
"unicode.Cc": "unicode",
"unicode.Cf": "unicode",
"unicode.Chakma": "unicode",
@@ -8000,7 +8635,9 @@
"unicode.Devanagari": "unicode",
"unicode.Diacritic": "unicode",
"unicode.Digit": "unicode",
+ "unicode.Duployan": "unicode",
"unicode.Egyptian_Hieroglyphs": "unicode",
+ "unicode.Elbasan": "unicode",
"unicode.Ethiopic": "unicode",
"unicode.Extender": "unicode",
"unicode.FoldCategory": "unicode",
@@ -8008,6 +8645,7 @@
"unicode.Georgian": "unicode",
"unicode.Glagolitic": "unicode",
"unicode.Gothic": "unicode",
+ "unicode.Grantha": "unicode",
"unicode.GraphicRanges": "unicode",
"unicode.Greek": "unicode",
"unicode.Gujarati": "unicode",
@@ -8015,6 +8653,7 @@
"unicode.Han": "unicode",
"unicode.Hangul": "unicode",
"unicode.Hanunoo": "unicode",
+ "unicode.Hatran": "unicode",
"unicode.Hebrew": "unicode",
"unicode.Hex_Digit": "unicode",
"unicode.Hiragana": "unicode",
@@ -8050,12 +8689,15 @@
"unicode.Kayah_Li": "unicode",
"unicode.Kharoshthi": "unicode",
"unicode.Khmer": "unicode",
+ "unicode.Khojki": "unicode",
+ "unicode.Khudawadi": "unicode",
"unicode.L": "unicode",
"unicode.Lao": "unicode",
"unicode.Latin": "unicode",
"unicode.Lepcha": "unicode",
"unicode.Letter": "unicode",
"unicode.Limbu": "unicode",
+ "unicode.Linear_A": "unicode",
"unicode.Linear_B": "unicode",
"unicode.Lisu": "unicode",
"unicode.Ll": "unicode",
@@ -8069,8 +8711,10 @@
"unicode.Lycian": "unicode",
"unicode.Lydian": "unicode",
"unicode.M": "unicode",
+ "unicode.Mahajani": "unicode",
"unicode.Malayalam": "unicode",
"unicode.Mandaic": "unicode",
+ "unicode.Manichaean": "unicode",
"unicode.Mark": "unicode",
"unicode.MaxASCII": "unicode",
"unicode.MaxCase": "unicode",
@@ -8079,13 +8723,18 @@
"unicode.Mc": "unicode",
"unicode.Me": "unicode",
"unicode.Meetei_Mayek": "unicode",
+ "unicode.Mende_Kikakui": "unicode",
"unicode.Meroitic_Cursive": "unicode",
"unicode.Meroitic_Hieroglyphs": "unicode",
"unicode.Miao": "unicode",
"unicode.Mn": "unicode",
+ "unicode.Modi": "unicode",
"unicode.Mongolian": "unicode",
+ "unicode.Mro": "unicode",
+ "unicode.Multani": "unicode",
"unicode.Myanmar": "unicode",
"unicode.N": "unicode",
+ "unicode.Nabataean": "unicode",
"unicode.Nd": "unicode",
"unicode.New_Tai_Lue": "unicode",
"unicode.Nko": "unicode",
@@ -8095,7 +8744,10 @@
"unicode.Number": "unicode",
"unicode.Ogham": "unicode",
"unicode.Ol_Chiki": "unicode",
+ "unicode.Old_Hungarian": "unicode",
"unicode.Old_Italic": "unicode",
+ "unicode.Old_North_Arabian": "unicode",
+ "unicode.Old_Permic": "unicode",
"unicode.Old_Persian": "unicode",
"unicode.Old_South_Arabian": "unicode",
"unicode.Old_Turkic": "unicode",
@@ -8111,8 +8763,11 @@
"unicode.Other_Math": "unicode",
"unicode.Other_Uppercase": "unicode",
"unicode.P": "unicode",
+ "unicode.Pahawh_Hmong": "unicode",
+ "unicode.Palmyrene": "unicode",
"unicode.Pattern_Syntax": "unicode",
"unicode.Pattern_White_Space": "unicode",
+ "unicode.Pau_Cin_Hau": "unicode",
"unicode.Pc": "unicode",
"unicode.Pd": "unicode",
"unicode.Pe": "unicode",
@@ -8124,6 +8779,7 @@
"unicode.PrintRanges": "unicode",
"unicode.Properties": "unicode",
"unicode.Ps": "unicode",
+ "unicode.Psalter_Pahlavi": "unicode",
"unicode.Punct": "unicode",
"unicode.Quotation_Mark": "unicode",
"unicode.Radical": "unicode",
@@ -8141,6 +8797,8 @@
"unicode.Scripts": "unicode",
"unicode.Sharada": "unicode",
"unicode.Shavian": "unicode",
+ "unicode.Siddham": "unicode",
+ "unicode.SignWriting": "unicode",
"unicode.SimpleFold": "unicode",
"unicode.Sinhala": "unicode",
"unicode.Sk": "unicode",
@@ -8167,6 +8825,7 @@
"unicode.Thai": "unicode",
"unicode.Tibetan": "unicode",
"unicode.Tifinagh": "unicode",
+ "unicode.Tirhuta": "unicode",
"unicode.Title": "unicode",
"unicode.TitleCase": "unicode",
"unicode.To": "unicode",
@@ -8182,6 +8841,7 @@
"unicode.Vai": "unicode",
"unicode.Variation_Selector": "unicode",
"unicode.Version": "unicode",
+ "unicode.Warang_Citi": "unicode",
"unicode.White_Space": "unicode",
"unicode.Yi": "unicode",
"unicode.Z": "unicode",
@@ -8233,8 +8893,10 @@
"x509.CertPool": "crypto/x509",
"x509.Certificate": "crypto/x509",
"x509.CertificateInvalidError": "crypto/x509",
+ "x509.CertificateRequest": "crypto/x509",
"x509.ConstraintViolationError": "crypto/x509",
"x509.CreateCertificate": "crypto/x509",
+ "x509.CreateCertificateRequest": "crypto/x509",
"x509.DSA": "crypto/x509",
"x509.DSAWithSHA1": "crypto/x509",
"x509.DSAWithSHA256": "crypto/x509",
@@ -8290,6 +8952,7 @@
"x509.PEMCipherDES": "crypto/x509",
"x509.ParseCRL": "crypto/x509",
"x509.ParseCertificate": "crypto/x509",
+ "x509.ParseCertificateRequest": "crypto/x509",
"x509.ParseCertificates": "crypto/x509",
"x509.ParseDERCRL": "crypto/x509",
"x509.ParseECPrivateKey": "crypto/x509",
@@ -8370,5 +9033,6 @@
"zlib.NewWriterLevel": "compress/zlib",
"zlib.NewWriterLevelDict": "compress/zlib",
"zlib.NoCompression": "compress/zlib",
+ "zlib.Resetter": "compress/zlib",
"zlib.Writer": "compress/zlib",
}
diff --git a/go/src/golang.org/x/tools/oracle/callees.go b/go/src/golang.org/x/tools/oracle/callees.go
index 56e45e1..06c2c15 100644
--- a/go/src/golang.org/x/tools/oracle/callees.go
+++ b/go/src/golang.org/x/tools/oracle/callees.go
@@ -2,19 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"sort"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -90,12 +92,17 @@
return nil
}
} else if sel.Kind() == types.MethodVal {
- recvtype := sel.Recv()
+ // Inspect the receiver type of the selected method.
+ // If it is concrete, the call is statically dispatched.
+ // (Due to implicit field selections, it is not enough to look
+ // at sel.Recv(), the type of the actual receiver expression.)
+ method := sel.Obj().(*types.Func)
+ recvtype := method.Type().(*types.Signature).Recv().Type()
if !types.IsInterface(recvtype) {
// static method call
q.result = &calleesTypesResult{
site: e,
- callee: sel.Obj().(*types.Func),
+ callee: method,
}
return nil
}
@@ -115,7 +122,7 @@
}
// Defer SSA construction till after errors are reported.
- prog.BuildAll()
+ prog.Build()
// Ascertain calling function and call site.
callerFn := ssa.EnclosingFunction(pkg, qpos.path)
diff --git a/go/src/golang.org/x/tools/oracle/callees14.go b/go/src/golang.org/x/tools/oracle/callees14.go
new file mode 100644
index 0000000..b6d0ffe
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/callees14.go
@@ -0,0 +1,260 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "sort"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// Callees reports the possible callees of the function call site
+// identified by the specified source location.
+func callees(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+
+ if err := setPTAScope(&lconf, q.Scope); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
+ if err != nil {
+ return err
+ }
+
+ // Determine the enclosing call for the specified position.
+ var e *ast.CallExpr
+ for _, n := range qpos.path {
+ if e, _ = n.(*ast.CallExpr); e != nil {
+ break
+ }
+ }
+ if e == nil {
+ return fmt.Errorf("there is no function call here")
+ }
+ // TODO(adonovan): issue an error if the call is "too far
+ // away" from the current selection, as this most likely is
+ // not what the user intended.
+
+ // Reject type conversions.
+ if qpos.info.Types[e.Fun].IsType() {
+ return fmt.Errorf("this is a type conversion, not a function call")
+ }
+
+ // Deal with obviously static calls before constructing SSA form.
+ // Some static calls may yet require SSA construction,
+ // e.g. f := func(){}; f().
+ switch funexpr := unparen(e.Fun).(type) {
+ case *ast.Ident:
+ switch obj := qpos.info.Uses[funexpr].(type) {
+ case *types.Builtin:
+ // Reject calls to built-ins.
+ return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
+ case *types.Func:
+ // This is a static function call
+ q.result = &calleesTypesResult{
+ site: e,
+ callee: obj,
+ }
+ return nil
+ }
+ case *ast.SelectorExpr:
+ sel := qpos.info.Selections[funexpr]
+ if sel == nil {
+ // qualified identifier.
+ // May refer to top level function variable
+ // or to top level function.
+ callee := qpos.info.Uses[funexpr.Sel]
+ if obj, ok := callee.(*types.Func); ok {
+ q.result = &calleesTypesResult{
+ site: e,
+ callee: obj,
+ }
+ return nil
+ }
+ } else if sel.Kind() == types.MethodVal {
+ // Inspect the receiver type of the selected method.
+ // If it is concrete, the call is statically dispatched.
+ // (Due to implicit field selections, it is not enough to look
+ // at sel.Recv(), the type of the actual receiver expression.)
+ method := sel.Obj().(*types.Func)
+ recvtype := method.Type().(*types.Signature).Recv().Type()
+ if !types.IsInterface(recvtype) {
+ // static method call
+ q.result = &calleesTypesResult{
+ site: e,
+ callee: method,
+ }
+ return nil
+ }
+ }
+ }
+
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
+
+ ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
+ if err != nil {
+ return err
+ }
+
+ pkg := prog.Package(qpos.info.Pkg)
+ if pkg == nil {
+ return fmt.Errorf("no SSA package")
+ }
+
+ // Defer SSA construction till after errors are reported.
+ prog.Build()
+
+ // Ascertain calling function and call site.
+ callerFn := ssa.EnclosingFunction(pkg, qpos.path)
+ if callerFn == nil {
+ return fmt.Errorf("no SSA function built for this location (dead code?)")
+ }
+
+ // Find the call site.
+ site, err := findCallSite(callerFn, e)
+ if err != nil {
+ return err
+ }
+
+ funcs, err := findCallees(ptaConfig, site)
+ if err != nil {
+ return err
+ }
+
+ q.result = &calleesSSAResult{
+ site: site,
+ funcs: funcs,
+ }
+ return nil
+}
+
+func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
+ instr, _ := fn.ValueForExpr(call)
+ callInstr, _ := instr.(ssa.CallInstruction)
+ if instr == nil {
+ return nil, fmt.Errorf("this call site is unreachable in this analysis")
+ }
+ return callInstr, nil
+}
+
+func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) {
+ // Avoid running the pointer analysis for static calls.
+ if callee := site.Common().StaticCallee(); callee != nil {
+ switch callee.String() {
+ case "runtime.SetFinalizer", "(reflect.Value).Call":
+ // The PTA treats calls to these intrinsics as dynamic.
+ // TODO(adonovan): avoid reliance on PTA internals.
+
+ default:
+ return []*ssa.Function{callee}, nil // singleton
+ }
+ }
+
+ // Dynamic call: use pointer analysis.
+ conf.BuildCallGraph = true
+ cg := ptrAnalysis(conf).CallGraph
+ cg.DeleteSyntheticNodes()
+
+ // Find all call edges from the site.
+ n := cg.Nodes[site.Parent()]
+ if n == nil {
+ return nil, fmt.Errorf("this call site is unreachable in this analysis")
+ }
+ calleesMap := make(map[*ssa.Function]bool)
+ for _, edge := range n.Out {
+ if edge.Site == site {
+ calleesMap[edge.Callee.Func] = true
+ }
+ }
+
+ // De-duplicate and sort.
+ funcs := make([]*ssa.Function, 0, len(calleesMap))
+ for f := range calleesMap {
+ funcs = append(funcs, f)
+ }
+ sort.Sort(byFuncPos(funcs))
+ return funcs, nil
+}
+
+type calleesSSAResult struct {
+ site ssa.CallInstruction
+ funcs []*ssa.Function
+}
+
+type calleesTypesResult struct {
+ site *ast.CallExpr
+ callee *types.Func
+}
+
+func (r *calleesSSAResult) display(printf printfFunc) {
+ if len(r.funcs) == 0 {
+ // dynamic call on a provably nil func/interface
+ printf(r.site, "%s on nil value", r.site.Common().Description())
+ } else {
+ printf(r.site, "this %s dispatches to:", r.site.Common().Description())
+ for _, callee := range r.funcs {
+ printf(callee, "\t%s", callee)
+ }
+ }
+}
+
+func (r *calleesSSAResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ j := &serial.Callees{
+ Pos: fset.Position(r.site.Pos()).String(),
+ Desc: r.site.Common().Description(),
+ }
+ for _, callee := range r.funcs {
+ j.Callees = append(j.Callees, &serial.CalleesItem{
+ Name: callee.String(),
+ Pos: fset.Position(callee.Pos()).String(),
+ })
+ }
+ res.Callees = j
+}
+
+func (r *calleesTypesResult) display(printf printfFunc) {
+ printf(r.site, "this static function call dispatches to:")
+ printf(r.callee, "\t%s", r.callee.FullName())
+}
+
+func (r *calleesTypesResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ j := &serial.Callees{
+ Pos: fset.Position(r.site.Pos()).String(),
+ Desc: "static function call",
+ }
+ j.Callees = []*serial.CalleesItem{
+ &serial.CalleesItem{
+ Name: r.callee.FullName(),
+ Pos: fset.Position(r.callee.Pos()).String(),
+ },
+ }
+ res.Callees = j
+}
+
+// NB: byFuncPos is not deterministic across packages since it depends on load order.
+// Use lessPos if the tests need it.
+type byFuncPos []*ssa.Function
+
+func (a byFuncPos) Len() int { return len(a) }
+func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
+func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/go/src/golang.org/x/tools/oracle/callers.go b/go/src/golang.org/x/tools/oracle/callers.go
index 159a403..e7f3c8f 100644
--- a/go/src/golang.org/x/tools/oracle/callers.go
+++ b/go/src/golang.org/x/tools/oracle/callers.go
@@ -53,7 +53,7 @@
}
// Defer SSA construction till after errors are reported.
- prog.BuildAll()
+ prog.Build()
target := ssa.EnclosingFunction(pkg, qpos.path)
if target == nil {
diff --git a/go/src/golang.org/x/tools/oracle/callstack.go b/go/src/golang.org/x/tools/oracle/callstack.go
index 6f04b60..ba88e99 100644
--- a/go/src/golang.org/x/tools/oracle/callstack.go
+++ b/go/src/golang.org/x/tools/oracle/callstack.go
@@ -61,7 +61,7 @@
}
// Defer SSA construction till after errors are reported.
- prog.BuildAll()
+ prog.Build()
target := ssa.EnclosingFunction(pkg, qpos.path)
if target == nil {
diff --git a/go/src/golang.org/x/tools/oracle/definition.go b/go/src/golang.org/x/tools/oracle/definition.go
index a0340c6..67f65f9 100644
--- a/go/src/golang.org/x/tools/oracle/definition.go
+++ b/go/src/golang.org/x/tools/oracle/definition.go
@@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -23,7 +25,7 @@
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
- if err := importQueryPackage(q.Pos, &lconf); err != nil {
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err
}
diff --git a/go/src/golang.org/x/tools/oracle/definition14.go b/go/src/golang.org/x/tools/oracle/definition14.go
new file mode 100644
index 0000000..c22b1fd
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/definition14.go
@@ -0,0 +1,78 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// definition reports the location of the definition of an identifier.
+//
+// TODO(adonovan): opt: for intra-file references, the parser's
+// resolution might be enough; we should start with that.
+//
+func definition(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+ allowErrors(&lconf)
+
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, false)
+ if err != nil {
+ return err
+ }
+
+ id, _ := qpos.path[0].(*ast.Ident)
+ if id == nil {
+ return fmt.Errorf("no identifier here")
+ }
+
+ obj := qpos.info.ObjectOf(id)
+ if obj == nil {
+ // Happens for y in "switch y := x.(type)",
+ // and the package declaration,
+ // but I think that's all.
+ return fmt.Errorf("no object for identifier")
+ }
+
+ q.result = &definitionResult{qpos, obj}
+ return nil
+}
+
+type definitionResult struct {
+ qpos *queryPos
+ obj types.Object // object it denotes
+}
+
+func (r *definitionResult) display(printf printfFunc) {
+ printf(r.obj, "defined here as %s", r.qpos.objectString(r.obj))
+}
+
+func (r *definitionResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ definition := &serial.Definition{
+ Desc: r.obj.String(),
+ }
+ if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos()
+ definition.ObjPos = fset.Position(pos).String()
+ }
+ res.Definition = definition
+}
diff --git a/go/src/golang.org/x/tools/oracle/describe.go b/go/src/golang.org/x/tools/oracle/describe.go
index ea1c5ec..70ddacf 100644
--- a/go/src/golang.org/x/tools/oracle/describe.go
+++ b/go/src/golang.org/x/tools/oracle/describe.go
@@ -2,21 +2,23 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.6
+
package oracle
import (
"bytes"
"fmt"
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"log"
"os"
"strings"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/oracle/serial"
)
@@ -31,7 +33,7 @@
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
- if err := importQueryPackage(q.Pos, &lconf); err != nil {
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err
}
@@ -513,11 +515,15 @@
var pkg *types.Package
switch n := path[0].(type) {
case *ast.ImportSpec:
- var pkgname *types.PkgName
+ var obj types.Object
if n.Name != nil {
- pkgname = qpos.info.Defs[n.Name].(*types.PkgName)
- } else if p := qpos.info.Implicits[n]; p != nil {
- pkgname = p.(*types.PkgName)
+ obj = qpos.info.Defs[n.Name]
+ } else {
+ obj = qpos.info.Implicits[n]
+ }
+ pkgname, _ := obj.(*types.PkgName)
+ if pkgname == nil {
+ return nil, fmt.Errorf("can't import package %s", n.Path.Value)
}
pkg = pkgname.Imported()
description = fmt.Sprintf("import of package %q", pkg.Path())
@@ -674,6 +680,12 @@
return "const"
case *types.PkgName:
return "package"
+ case *types.Builtin:
+ return "builtin" // e.g. when describing package "unsafe"
+ case *types.Nil:
+ return "nil"
+ case *types.Label:
+ return "label"
}
panic(o)
}
diff --git a/go/src/golang.org/x/tools/oracle/describe14.go b/go/src/golang.org/x/tools/oracle/describe14.go
new file mode 100644
index 0000000..c8ccc2d
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/describe14.go
@@ -0,0 +1,786 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "log"
+ "os"
+ "strings"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// describe describes the syntax node denoted by the query position,
+// including:
+// - its syntactic category
+// - the definition of its referent (for identifiers) [now redundant]
+// - its type and method set (for an expression or type expression)
+//
+func describe(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+ allowErrors(&lconf)
+
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos)
+ if err != nil {
+ return err
+ }
+
+ if false { // debugging
+ fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s",
+ astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
+ }
+
+ path, action := findInterestingNode(qpos.info, qpos.path)
+ switch action {
+ case actionExpr:
+ q.result, err = describeValue(qpos, path)
+
+ case actionType:
+ q.result, err = describeType(qpos, path)
+
+ case actionPackage:
+ q.result, err = describePackage(qpos, path)
+
+ case actionStmt:
+ q.result, err = describeStmt(qpos, path)
+
+ case actionUnknown:
+ q.result = &describeUnknownResult{path[0]}
+
+ default:
+ panic(action) // unreachable
+ }
+ return err
+}
+
+type describeUnknownResult struct {
+ node ast.Node
+}
+
+func (r *describeUnknownResult) display(printf printfFunc) {
+ // Nothing much to say about misc syntax.
+ printf(r.node, "%s", astutil.NodeDescription(r.node))
+}
+
+func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ res.Describe = &serial.Describe{
+ Desc: astutil.NodeDescription(r.node),
+ Pos: fset.Position(r.node.Pos()).String(),
+ }
+}
+
+type action int
+
+const (
+ actionUnknown action = iota // None of the below
+ actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var})
+ actionType // type Expr or Ident(types.TypeName).
+ actionStmt // Stmt or Ident(types.Label)
+ actionPackage // Ident(types.Package) or ImportSpec
+)
+
+// findInterestingNode classifies the syntax node denoted by path as one of:
+// - an expression, part of an expression or a reference to a constant
+// or variable;
+// - a type, part of a type, or a reference to a named type;
+// - a statement, part of a statement, or a label referring to a statement;
+// - part of a package declaration or import spec.
+// - none of the above.
+// and returns the most "interesting" associated node, which may be
+// the same node, an ancestor or a descendent.
+//
+func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
+ // TODO(adonovan): integrate with go/types/stdlib_test.go and
+ // apply this to every AST node we can find to make sure it
+ // doesn't crash.
+
+ // TODO(adonovan): audit for ParenExpr safety, esp. since we
+ // traverse up and down.
+
+ // TODO(adonovan): if the users selects the "." in
+ // "fmt.Fprintf()", they'll get an ambiguous selection error;
+ // we won't even reach here. Can we do better?
+
+ // TODO(adonovan): describing a field within 'type T struct {...}'
+ // describes the (anonymous) struct type and concludes "no methods".
+ // We should ascend to the enclosing type decl, if any.
+
+ for len(path) > 0 {
+ switch n := path[0].(type) {
+ case *ast.GenDecl:
+ if len(n.Specs) == 1 {
+ // Descend to sole {Import,Type,Value}Spec child.
+ path = append([]ast.Node{n.Specs[0]}, path...)
+ continue
+ }
+ return path, actionUnknown // uninteresting
+
+ case *ast.FuncDecl:
+ // Descend to function name.
+ path = append([]ast.Node{n.Name}, path...)
+ continue
+
+ case *ast.ImportSpec:
+ return path, actionPackage
+
+ case *ast.ValueSpec:
+ if len(n.Names) == 1 {
+ // Descend to sole Ident child.
+ path = append([]ast.Node{n.Names[0]}, path...)
+ continue
+ }
+ return path, actionUnknown // uninteresting
+
+ case *ast.TypeSpec:
+ // Descend to type name.
+ path = append([]ast.Node{n.Name}, path...)
+ continue
+
+ case ast.Stmt:
+ return path, actionStmt
+
+ case *ast.ArrayType,
+ *ast.StructType,
+ *ast.FuncType,
+ *ast.InterfaceType,
+ *ast.MapType,
+ *ast.ChanType:
+ return path, actionType
+
+ case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
+ return path, actionUnknown // uninteresting
+
+ case *ast.Ellipsis:
+ // Continue to enclosing node.
+ // e.g. [...]T in ArrayType
+ // f(x...) in CallExpr
+ // f(x...T) in FuncType
+
+ case *ast.Field:
+ // TODO(adonovan): this needs more thought,
+ // since fields can be so many things.
+ if len(n.Names) == 1 {
+ // Descend to sole Ident child.
+ path = append([]ast.Node{n.Names[0]}, path...)
+ continue
+ }
+ // Zero names (e.g. anon field in struct)
+ // or multiple field or param names:
+ // continue to enclosing field list.
+
+ case *ast.FieldList:
+ // Continue to enclosing node:
+ // {Struct,Func,Interface}Type or FuncDecl.
+
+ case *ast.BasicLit:
+ if _, ok := path[1].(*ast.ImportSpec); ok {
+ return path[1:], actionPackage
+ }
+ return path, actionExpr
+
+ case *ast.SelectorExpr:
+ // TODO(adonovan): use Selections info directly.
+ if pkginfo.Uses[n.Sel] == nil {
+ // TODO(adonovan): is this reachable?
+ return path, actionUnknown
+ }
+ // Descend to .Sel child.
+ path = append([]ast.Node{n.Sel}, path...)
+ continue
+
+ case *ast.Ident:
+ switch pkginfo.ObjectOf(n).(type) {
+ case *types.PkgName:
+ return path, actionPackage
+
+ case *types.Const:
+ return path, actionExpr
+
+ case *types.Label:
+ return path, actionStmt
+
+ case *types.TypeName:
+ return path, actionType
+
+ case *types.Var:
+ // For x in 'struct {x T}', return struct type, for now.
+ if _, ok := path[1].(*ast.Field); ok {
+ _ = path[2].(*ast.FieldList) // assertion
+ if _, ok := path[3].(*ast.StructType); ok {
+ return path[3:], actionType
+ }
+ }
+ return path, actionExpr
+
+ case *types.Func:
+ return path, actionExpr
+
+ case *types.Builtin:
+ // For reference to built-in function, return enclosing call.
+ path = path[1:] // ascend to enclosing function call
+ continue
+
+ case *types.Nil:
+ return path, actionExpr
+ }
+
+ // No object.
+ switch path[1].(type) {
+ case *ast.SelectorExpr:
+ // Return enclosing selector expression.
+ return path[1:], actionExpr
+
+ case *ast.Field:
+ // TODO(adonovan): test this.
+ // e.g. all f in:
+ // struct { f, g int }
+ // interface { f() }
+ // func (f T) method(f, g int) (f, g bool)
+ //
+ // switch path[3].(type) {
+ // case *ast.FuncDecl:
+ // case *ast.StructType:
+ // case *ast.InterfaceType:
+ // }
+ //
+ // return path[1:], actionExpr
+ //
+ // Unclear what to do with these.
+ // Struct.Fields -- field
+ // Interface.Methods -- field
+ // FuncType.{Params.Results} -- actionExpr
+ // FuncDecl.Recv -- actionExpr
+
+ case *ast.File:
+ // 'package foo'
+ return path, actionPackage
+
+ case *ast.ImportSpec:
+ // TODO(adonovan): fix: why no package object? go/types bug?
+ return path[1:], actionPackage
+
+ default:
+ // e.g. blank identifier
+ // or y in "switch y := x.(type)"
+ // or code in a _test.go file that's not part of the package.
+ log.Printf("unknown reference %s in %T\n", n, path[1])
+ return path, actionUnknown
+ }
+
+ case *ast.StarExpr:
+ if pkginfo.Types[n].IsType() {
+ return path, actionType
+ }
+ return path, actionExpr
+
+ case ast.Expr:
+ // All Expr but {BasicLit,Ident,StarExpr} are
+ // "true" expressions that evaluate to a value.
+ return path, actionExpr
+ }
+
+ // Ascend to parent.
+ path = path[1:]
+ }
+
+ return nil, actionUnknown // unreachable
+}
+
+func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) {
+ var expr ast.Expr
+ var obj types.Object
+ switch n := path[0].(type) {
+ case *ast.ValueSpec:
+ // ambiguous ValueSpec containing multiple names
+ return nil, fmt.Errorf("multiple value specification")
+ case *ast.Ident:
+ obj = qpos.info.ObjectOf(n)
+ expr = n
+ case ast.Expr:
+ expr = n
+ default:
+ // TODO(adonovan): is this reachable?
+ return nil, fmt.Errorf("unexpected AST for expr: %T", n)
+ }
+
+ typ := qpos.info.TypeOf(expr)
+ constVal := qpos.info.Types[expr].Value
+
+ return &describeValueResult{
+ qpos: qpos,
+ expr: expr,
+ typ: typ,
+ constVal: constVal,
+ obj: obj,
+ }, nil
+}
+
+type describeValueResult struct {
+ qpos *queryPos
+ expr ast.Expr // query node
+ typ types.Type // type of expression
+ constVal exact.Value // value of expression, if constant
+ obj types.Object // var/func/const object, if expr was Ident
+}
+
+func (r *describeValueResult) display(printf printfFunc) {
+ var prefix, suffix string
+ if r.constVal != nil {
+ suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal))
+ }
+ switch obj := r.obj.(type) {
+ case *types.Func:
+ if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
+ if _, ok := recv.Type().Underlying().(*types.Interface); ok {
+ prefix = "interface method "
+ } else {
+ prefix = "method "
+ }
+ }
+ }
+
+ // Describe the expression.
+ if r.obj != nil {
+ if r.obj.Pos() == r.expr.Pos() {
+ // defining ident
+ printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
+ } else {
+ // referring ident
+ printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
+ if def := r.obj.Pos(); def != token.NoPos {
+ printf(def, "defined here")
+ }
+ }
+ } else {
+ desc := astutil.NodeDescription(r.expr)
+ if suffix != "" {
+ // constant expression
+ printf(r.expr, "%s%s", desc, suffix)
+ } else {
+ // non-constant expression
+ printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ))
+ }
+ }
+}
+
+func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var value, objpos string
+ if r.constVal != nil {
+ value = r.constVal.String()
+ }
+ if r.obj != nil {
+ objpos = fset.Position(r.obj.Pos()).String()
+ }
+
+ res.Describe = &serial.Describe{
+ Desc: astutil.NodeDescription(r.expr),
+ Pos: fset.Position(r.expr.Pos()).String(),
+ Detail: "value",
+ Value: &serial.DescribeValue{
+ Type: r.qpos.typeString(r.typ),
+ Value: value,
+ ObjPos: objpos,
+ },
+ }
+}
+
+// ---- TYPE ------------------------------------------------------------
+
+func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) {
+ var description string
+ var t types.Type
+ switch n := path[0].(type) {
+ case *ast.Ident:
+ t = qpos.info.TypeOf(n)
+ switch t := t.(type) {
+ case *types.Basic:
+ description = "reference to built-in "
+
+ case *types.Named:
+ isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above
+ if isDef {
+ description = "definition of "
+ } else {
+ description = "reference to "
+ }
+ }
+
+ case ast.Expr:
+ t = qpos.info.TypeOf(n)
+
+ default:
+ // Unreachable?
+ return nil, fmt.Errorf("unexpected AST for type: %T", n)
+ }
+
+ description = description + "type " + qpos.typeString(t)
+
+ // Show sizes for structs and named types (it's fairly obvious for others).
+ switch t.(type) {
+ case *types.Named, *types.Struct:
+ szs := types.StdSizes{8, 8} // assume amd64
+ description = fmt.Sprintf("%s (size %d, align %d)", description,
+ szs.Sizeof(t), szs.Alignof(t))
+ }
+
+ return &describeTypeResult{
+ qpos: qpos,
+ node: path[0],
+ description: description,
+ typ: t,
+ methods: accessibleMethods(t, qpos.info.Pkg),
+ }, nil
+}
+
+type describeTypeResult struct {
+ qpos *queryPos
+ node ast.Node
+ description string
+ typ types.Type
+ methods []*types.Selection
+}
+
+func (r *describeTypeResult) display(printf printfFunc) {
+ printf(r.node, "%s", r.description)
+
+ // Show the underlying type for a reference to a named type.
+ if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
+ printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying()))
+ }
+
+ // Print the method set, if the type kind is capable of bearing methods.
+ switch r.typ.(type) {
+ case *types.Interface, *types.Struct, *types.Named:
+ if len(r.methods) > 0 {
+ printf(r.node, "Method set:")
+ for _, meth := range r.methods {
+ // TODO(adonovan): print these relative
+ // to the owning package, not the
+ // query package.
+ printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth))
+ }
+ } else {
+ printf(r.node, "No methods.")
+ }
+ }
+}
+
+func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var namePos, nameDef string
+ if nt, ok := r.typ.(*types.Named); ok {
+ namePos = fset.Position(nt.Obj().Pos()).String()
+ nameDef = nt.Underlying().String()
+ }
+ res.Describe = &serial.Describe{
+ Desc: r.description,
+ Pos: fset.Position(r.node.Pos()).String(),
+ Detail: "type",
+ Type: &serial.DescribeType{
+ Type: r.qpos.typeString(r.typ),
+ NamePos: namePos,
+ NameDef: nameDef,
+ Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset),
+ },
+ }
+}
+
+// ---- PACKAGE ------------------------------------------------------------
+
+func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) {
+ var description string
+ var pkg *types.Package
+ switch n := path[0].(type) {
+ case *ast.ImportSpec:
+ var obj types.Object
+ if n.Name != nil {
+ obj = qpos.info.Defs[n.Name]
+ } else {
+ obj = qpos.info.Implicits[n]
+ }
+ pkgname, _ := obj.(*types.PkgName)
+ if pkgname == nil {
+ return nil, fmt.Errorf("can't import package %s", n.Path.Value)
+ }
+ pkg = pkgname.Imported()
+ description = fmt.Sprintf("import of package %q", pkg.Path())
+
+ case *ast.Ident:
+ if _, isDef := path[1].(*ast.File); isDef {
+ // e.g. package id
+ pkg = qpos.info.Pkg
+ description = fmt.Sprintf("definition of package %q", pkg.Path())
+ } else {
+ // e.g. import id "..."
+ // or id.F()
+ pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported()
+ description = fmt.Sprintf("reference to package %q", pkg.Path())
+ }
+
+ default:
+ // Unreachable?
+ return nil, fmt.Errorf("unexpected AST for package: %T", n)
+ }
+
+ var members []*describeMember
+ // NB: "unsafe" has no types.Package
+ if pkg != nil {
+ // Enumerate the accessible package members
+ // in lexicographic order.
+ for _, name := range pkg.Scope().Names() {
+ if pkg == qpos.info.Pkg || ast.IsExported(name) {
+ mem := pkg.Scope().Lookup(name)
+ var methods []*types.Selection
+ if mem, ok := mem.(*types.TypeName); ok {
+ methods = accessibleMethods(mem.Type(), qpos.info.Pkg)
+ }
+ members = append(members, &describeMember{
+ mem,
+ methods,
+ })
+
+ }
+ }
+ }
+
+ return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil
+}
+
+type describePackageResult struct {
+ fset *token.FileSet
+ node ast.Node
+ description string
+ pkg *types.Package
+ members []*describeMember // in lexicographic name order
+}
+
+type describeMember struct {
+ obj types.Object
+ methods []*types.Selection // in types.MethodSet order
+}
+
+func (r *describePackageResult) display(printf printfFunc) {
+ printf(r.node, "%s", r.description)
+
+ // Compute max width of name "column".
+ maxname := 0
+ for _, mem := range r.members {
+ if l := len(mem.obj.Name()); l > maxname {
+ maxname = l
+ }
+ }
+
+ for _, mem := range r.members {
+ printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
+ for _, meth := range mem.methods {
+ printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg)))
+ }
+ }
+}
+
+func formatMember(obj types.Object, maxname int) string {
+ qualifier := types.RelativeTo(obj.Pkg())
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
+ switch obj := obj.(type) {
+ case *types.Const:
+ fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val()))
+
+ case *types.Func:
+ fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
+
+ case *types.TypeName:
+ // Abbreviate long aggregate type names.
+ var abbrev string
+ switch t := obj.Type().Underlying().(type) {
+ case *types.Interface:
+ if t.NumMethods() > 1 {
+ abbrev = "interface{...}"
+ }
+ case *types.Struct:
+ if t.NumFields() > 1 {
+ abbrev = "struct{...}"
+ }
+ }
+ if abbrev == "" {
+ fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier))
+ } else {
+ fmt.Fprintf(&buf, " %s", abbrev)
+ }
+
+ case *types.Var:
+ fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
+ }
+ return buf.String()
+}
+
+func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var members []*serial.DescribeMember
+ for _, mem := range r.members {
+ typ := mem.obj.Type()
+ var val string
+ switch mem := mem.obj.(type) {
+ case *types.Const:
+ val = constValString(mem.Val())
+ case *types.TypeName:
+ typ = typ.Underlying()
+ }
+ members = append(members, &serial.DescribeMember{
+ Name: mem.obj.Name(),
+ Type: typ.String(),
+ Value: val,
+ Pos: fset.Position(mem.obj.Pos()).String(),
+ Kind: tokenOf(mem.obj),
+ Methods: methodsToSerial(r.pkg, mem.methods, fset),
+ })
+ }
+ res.Describe = &serial.Describe{
+ Desc: r.description,
+ Pos: fset.Position(r.node.Pos()).String(),
+ Detail: "package",
+ Package: &serial.DescribePackage{
+ Path: r.pkg.Path(),
+ Members: members,
+ },
+ }
+}
+
+func tokenOf(o types.Object) string {
+ switch o.(type) {
+ case *types.Func:
+ return "func"
+ case *types.Var:
+ return "var"
+ case *types.TypeName:
+ return "type"
+ case *types.Const:
+ return "const"
+ case *types.PkgName:
+ return "package"
+ case *types.Builtin:
+ return "builtin" // e.g. when describing package "unsafe"
+ case *types.Nil:
+ return "nil"
+ case *types.Label:
+ return "label"
+ }
+ panic(o)
+}
+
+// ---- STATEMENT ------------------------------------------------------------
+
+func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) {
+ var description string
+ switch n := path[0].(type) {
+ case *ast.Ident:
+ if qpos.info.Defs[n] != nil {
+ description = "labelled statement"
+ } else {
+ description = "reference to labelled statement"
+ }
+
+ default:
+ // Nothing much to say about statements.
+ description = astutil.NodeDescription(n)
+ }
+ return &describeStmtResult{qpos.fset, path[0], description}, nil
+}
+
+type describeStmtResult struct {
+ fset *token.FileSet
+ node ast.Node
+ description string
+}
+
+func (r *describeStmtResult) display(printf printfFunc) {
+ printf(r.node, "%s", r.description)
+}
+
+func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ res.Describe = &serial.Describe{
+ Desc: r.description,
+ Pos: fset.Position(r.node.Pos()).String(),
+ Detail: "unknown",
+ }
+}
+
+// ------------------- Utilities -------------------
+
+// pathToString returns a string containing the concrete types of the
+// nodes in path.
+func pathToString(path []ast.Node) string {
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "[")
+ for i, n := range path {
+ if i > 0 {
+ fmt.Fprint(&buf, " ")
+ }
+ fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
+ }
+ fmt.Fprint(&buf, "]")
+ return buf.String()
+}
+
+func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
+ var methods []*types.Selection
+ for _, meth := range typeutil.IntuitiveMethodSet(t, nil) {
+ if isAccessibleFrom(meth.Obj(), from) {
+ methods = append(methods, meth)
+ }
+ }
+ return methods
+}
+
+func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
+ return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
+}
+
+func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
+ qualifier := types.RelativeTo(this)
+ var jmethods []serial.DescribeMethod
+ for _, meth := range methods {
+ var ser serial.DescribeMethod
+ if meth != nil { // may contain nils when called by implements (on a method)
+ ser = serial.DescribeMethod{
+ Name: types.SelectionString(meth, qualifier),
+ Pos: fset.Position(meth.Obj().Pos()).String(),
+ }
+ }
+ jmethods = append(jmethods, ser)
+ }
+ return jmethods
+}
+
+// constValString emulates Go 1.6's go/constant.ExactString well enough
+// to make the tests pass. This is just a stopgap until we throw away
+// all the *14.go files.
+func constValString(v exact.Value) string {
+ if v.Kind() == exact.Float {
+ f, _ := exact.Float64Val(v)
+ return fmt.Sprintf("%g", f)
+ }
+ return v.String()
+}
diff --git a/go/src/golang.org/x/tools/oracle/describe15.go b/go/src/golang.org/x/tools/oracle/describe15.go
new file mode 100644
index 0000000..1276f9a
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/describe15.go
@@ -0,0 +1,786 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5,!go1.6
+
+package oracle
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ exact "go/constant"
+ "go/token"
+ "go/types"
+ "log"
+ "os"
+ "strings"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// describe describes the syntax node denoted by the query position,
+// including:
+// - its syntactic category
+// - the definition of its referent (for identifiers) [now redundant]
+// - its type and method set (for an expression or type expression)
+//
+func describe(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+ allowErrors(&lconf)
+
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos)
+ if err != nil {
+ return err
+ }
+
+ if false { // debugging
+ fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s",
+ astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
+ }
+
+ path, action := findInterestingNode(qpos.info, qpos.path)
+ switch action {
+ case actionExpr:
+ q.result, err = describeValue(qpos, path)
+
+ case actionType:
+ q.result, err = describeType(qpos, path)
+
+ case actionPackage:
+ q.result, err = describePackage(qpos, path)
+
+ case actionStmt:
+ q.result, err = describeStmt(qpos, path)
+
+ case actionUnknown:
+ q.result = &describeUnknownResult{path[0]}
+
+ default:
+ panic(action) // unreachable
+ }
+ return err
+}
+
+type describeUnknownResult struct {
+ node ast.Node
+}
+
+func (r *describeUnknownResult) display(printf printfFunc) {
+ // Nothing much to say about misc syntax.
+ printf(r.node, "%s", astutil.NodeDescription(r.node))
+}
+
+func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ res.Describe = &serial.Describe{
+ Desc: astutil.NodeDescription(r.node),
+ Pos: fset.Position(r.node.Pos()).String(),
+ }
+}
+
+type action int
+
+const (
+ actionUnknown action = iota // None of the below
+ actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var})
+ actionType // type Expr or Ident(types.TypeName).
+ actionStmt // Stmt or Ident(types.Label)
+ actionPackage // Ident(types.Package) or ImportSpec
+)
+
+// findInterestingNode classifies the syntax node denoted by path as one of:
+// - an expression, part of an expression or a reference to a constant
+// or variable;
+// - a type, part of a type, or a reference to a named type;
+// - a statement, part of a statement, or a label referring to a statement;
+// - part of a package declaration or import spec.
+// - none of the above.
+// and returns the most "interesting" associated node, which may be
+// the same node, an ancestor or a descendent.
+//
+func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
+ // TODO(adonovan): integrate with go/types/stdlib_test.go and
+ // apply this to every AST node we can find to make sure it
+ // doesn't crash.
+
+ // TODO(adonovan): audit for ParenExpr safety, esp. since we
+ // traverse up and down.
+
+ // TODO(adonovan): if the users selects the "." in
+ // "fmt.Fprintf()", they'll get an ambiguous selection error;
+ // we won't even reach here. Can we do better?
+
+ // TODO(adonovan): describing a field within 'type T struct {...}'
+ // describes the (anonymous) struct type and concludes "no methods".
+ // We should ascend to the enclosing type decl, if any.
+
+ for len(path) > 0 {
+ switch n := path[0].(type) {
+ case *ast.GenDecl:
+ if len(n.Specs) == 1 {
+ // Descend to sole {Import,Type,Value}Spec child.
+ path = append([]ast.Node{n.Specs[0]}, path...)
+ continue
+ }
+ return path, actionUnknown // uninteresting
+
+ case *ast.FuncDecl:
+ // Descend to function name.
+ path = append([]ast.Node{n.Name}, path...)
+ continue
+
+ case *ast.ImportSpec:
+ return path, actionPackage
+
+ case *ast.ValueSpec:
+ if len(n.Names) == 1 {
+ // Descend to sole Ident child.
+ path = append([]ast.Node{n.Names[0]}, path...)
+ continue
+ }
+ return path, actionUnknown // uninteresting
+
+ case *ast.TypeSpec:
+ // Descend to type name.
+ path = append([]ast.Node{n.Name}, path...)
+ continue
+
+ case ast.Stmt:
+ return path, actionStmt
+
+ case *ast.ArrayType,
+ *ast.StructType,
+ *ast.FuncType,
+ *ast.InterfaceType,
+ *ast.MapType,
+ *ast.ChanType:
+ return path, actionType
+
+ case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
+ return path, actionUnknown // uninteresting
+
+ case *ast.Ellipsis:
+ // Continue to enclosing node.
+ // e.g. [...]T in ArrayType
+ // f(x...) in CallExpr
+ // f(x...T) in FuncType
+
+ case *ast.Field:
+ // TODO(adonovan): this needs more thought,
+ // since fields can be so many things.
+ if len(n.Names) == 1 {
+ // Descend to sole Ident child.
+ path = append([]ast.Node{n.Names[0]}, path...)
+ continue
+ }
+ // Zero names (e.g. anon field in struct)
+ // or multiple field or param names:
+ // continue to enclosing field list.
+
+ case *ast.FieldList:
+ // Continue to enclosing node:
+ // {Struct,Func,Interface}Type or FuncDecl.
+
+ case *ast.BasicLit:
+ if _, ok := path[1].(*ast.ImportSpec); ok {
+ return path[1:], actionPackage
+ }
+ return path, actionExpr
+
+ case *ast.SelectorExpr:
+ // TODO(adonovan): use Selections info directly.
+ if pkginfo.Uses[n.Sel] == nil {
+ // TODO(adonovan): is this reachable?
+ return path, actionUnknown
+ }
+ // Descend to .Sel child.
+ path = append([]ast.Node{n.Sel}, path...)
+ continue
+
+ case *ast.Ident:
+ switch pkginfo.ObjectOf(n).(type) {
+ case *types.PkgName:
+ return path, actionPackage
+
+ case *types.Const:
+ return path, actionExpr
+
+ case *types.Label:
+ return path, actionStmt
+
+ case *types.TypeName:
+ return path, actionType
+
+ case *types.Var:
+ // For x in 'struct {x T}', return struct type, for now.
+ if _, ok := path[1].(*ast.Field); ok {
+ _ = path[2].(*ast.FieldList) // assertion
+ if _, ok := path[3].(*ast.StructType); ok {
+ return path[3:], actionType
+ }
+ }
+ return path, actionExpr
+
+ case *types.Func:
+ return path, actionExpr
+
+ case *types.Builtin:
+ // For reference to built-in function, return enclosing call.
+ path = path[1:] // ascend to enclosing function call
+ continue
+
+ case *types.Nil:
+ return path, actionExpr
+ }
+
+ // No object.
+ switch path[1].(type) {
+ case *ast.SelectorExpr:
+ // Return enclosing selector expression.
+ return path[1:], actionExpr
+
+ case *ast.Field:
+ // TODO(adonovan): test this.
+ // e.g. all f in:
+ // struct { f, g int }
+ // interface { f() }
+ // func (f T) method(f, g int) (f, g bool)
+ //
+ // switch path[3].(type) {
+ // case *ast.FuncDecl:
+ // case *ast.StructType:
+ // case *ast.InterfaceType:
+ // }
+ //
+ // return path[1:], actionExpr
+ //
+ // Unclear what to do with these.
+ // Struct.Fields -- field
+ // Interface.Methods -- field
+ // FuncType.{Params.Results} -- actionExpr
+ // FuncDecl.Recv -- actionExpr
+
+ case *ast.File:
+ // 'package foo'
+ return path, actionPackage
+
+ case *ast.ImportSpec:
+ // TODO(adonovan): fix: why no package object? go/types bug?
+ return path[1:], actionPackage
+
+ default:
+ // e.g. blank identifier
+ // or y in "switch y := x.(type)"
+ // or code in a _test.go file that's not part of the package.
+ log.Printf("unknown reference %s in %T\n", n, path[1])
+ return path, actionUnknown
+ }
+
+ case *ast.StarExpr:
+ if pkginfo.Types[n].IsType() {
+ return path, actionType
+ }
+ return path, actionExpr
+
+ case ast.Expr:
+ // All Expr but {BasicLit,Ident,StarExpr} are
+ // "true" expressions that evaluate to a value.
+ return path, actionExpr
+ }
+
+ // Ascend to parent.
+ path = path[1:]
+ }
+
+ return nil, actionUnknown // unreachable
+}
+
+func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) {
+ var expr ast.Expr
+ var obj types.Object
+ switch n := path[0].(type) {
+ case *ast.ValueSpec:
+ // ambiguous ValueSpec containing multiple names
+ return nil, fmt.Errorf("multiple value specification")
+ case *ast.Ident:
+ obj = qpos.info.ObjectOf(n)
+ expr = n
+ case ast.Expr:
+ expr = n
+ default:
+ // TODO(adonovan): is this reachable?
+ return nil, fmt.Errorf("unexpected AST for expr: %T", n)
+ }
+
+ typ := qpos.info.TypeOf(expr)
+ constVal := qpos.info.Types[expr].Value
+
+ return &describeValueResult{
+ qpos: qpos,
+ expr: expr,
+ typ: typ,
+ constVal: constVal,
+ obj: obj,
+ }, nil
+}
+
+type describeValueResult struct {
+ qpos *queryPos
+ expr ast.Expr // query node
+ typ types.Type // type of expression
+ constVal exact.Value // value of expression, if constant
+ obj types.Object // var/func/const object, if expr was Ident
+}
+
+func (r *describeValueResult) display(printf printfFunc) {
+ var prefix, suffix string
+ if r.constVal != nil {
+ suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal))
+ }
+ switch obj := r.obj.(type) {
+ case *types.Func:
+ if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
+ if _, ok := recv.Type().Underlying().(*types.Interface); ok {
+ prefix = "interface method "
+ } else {
+ prefix = "method "
+ }
+ }
+ }
+
+ // Describe the expression.
+ if r.obj != nil {
+ if r.obj.Pos() == r.expr.Pos() {
+ // defining ident
+ printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
+ } else {
+ // referring ident
+ printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
+ if def := r.obj.Pos(); def != token.NoPos {
+ printf(def, "defined here")
+ }
+ }
+ } else {
+ desc := astutil.NodeDescription(r.expr)
+ if suffix != "" {
+ // constant expression
+ printf(r.expr, "%s%s", desc, suffix)
+ } else {
+ // non-constant expression
+ printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ))
+ }
+ }
+}
+
+func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var value, objpos string
+ if r.constVal != nil {
+ value = r.constVal.String()
+ }
+ if r.obj != nil {
+ objpos = fset.Position(r.obj.Pos()).String()
+ }
+
+ res.Describe = &serial.Describe{
+ Desc: astutil.NodeDescription(r.expr),
+ Pos: fset.Position(r.expr.Pos()).String(),
+ Detail: "value",
+ Value: &serial.DescribeValue{
+ Type: r.qpos.typeString(r.typ),
+ Value: value,
+ ObjPos: objpos,
+ },
+ }
+}
+
+// ---- TYPE ------------------------------------------------------------
+
+func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) {
+ var description string
+ var t types.Type
+ switch n := path[0].(type) {
+ case *ast.Ident:
+ t = qpos.info.TypeOf(n)
+ switch t := t.(type) {
+ case *types.Basic:
+ description = "reference to built-in "
+
+ case *types.Named:
+ isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above
+ if isDef {
+ description = "definition of "
+ } else {
+ description = "reference to "
+ }
+ }
+
+ case ast.Expr:
+ t = qpos.info.TypeOf(n)
+
+ default:
+ // Unreachable?
+ return nil, fmt.Errorf("unexpected AST for type: %T", n)
+ }
+
+ description = description + "type " + qpos.typeString(t)
+
+ // Show sizes for structs and named types (it's fairly obvious for others).
+ switch t.(type) {
+ case *types.Named, *types.Struct:
+ szs := types.StdSizes{8, 8} // assume amd64
+ description = fmt.Sprintf("%s (size %d, align %d)", description,
+ szs.Sizeof(t), szs.Alignof(t))
+ }
+
+ return &describeTypeResult{
+ qpos: qpos,
+ node: path[0],
+ description: description,
+ typ: t,
+ methods: accessibleMethods(t, qpos.info.Pkg),
+ }, nil
+}
+
+type describeTypeResult struct {
+ qpos *queryPos
+ node ast.Node
+ description string
+ typ types.Type
+ methods []*types.Selection
+}
+
+func (r *describeTypeResult) display(printf printfFunc) {
+ printf(r.node, "%s", r.description)
+
+ // Show the underlying type for a reference to a named type.
+ if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
+ printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying()))
+ }
+
+ // Print the method set, if the type kind is capable of bearing methods.
+ switch r.typ.(type) {
+ case *types.Interface, *types.Struct, *types.Named:
+ if len(r.methods) > 0 {
+ printf(r.node, "Method set:")
+ for _, meth := range r.methods {
+ // TODO(adonovan): print these relative
+ // to the owning package, not the
+ // query package.
+ printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth))
+ }
+ } else {
+ printf(r.node, "No methods.")
+ }
+ }
+}
+
+func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var namePos, nameDef string
+ if nt, ok := r.typ.(*types.Named); ok {
+ namePos = fset.Position(nt.Obj().Pos()).String()
+ nameDef = nt.Underlying().String()
+ }
+ res.Describe = &serial.Describe{
+ Desc: r.description,
+ Pos: fset.Position(r.node.Pos()).String(),
+ Detail: "type",
+ Type: &serial.DescribeType{
+ Type: r.qpos.typeString(r.typ),
+ NamePos: namePos,
+ NameDef: nameDef,
+ Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset),
+ },
+ }
+}
+
+// ---- PACKAGE ------------------------------------------------------------
+
+func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) {
+ var description string
+ var pkg *types.Package
+ switch n := path[0].(type) {
+ case *ast.ImportSpec:
+ var obj types.Object
+ if n.Name != nil {
+ obj = qpos.info.Defs[n.Name]
+ } else {
+ obj = qpos.info.Implicits[n]
+ }
+ pkgname, _ := obj.(*types.PkgName)
+ if pkgname == nil {
+ return nil, fmt.Errorf("can't import package %s", n.Path.Value)
+ }
+ pkg = pkgname.Imported()
+ description = fmt.Sprintf("import of package %q", pkg.Path())
+
+ case *ast.Ident:
+ if _, isDef := path[1].(*ast.File); isDef {
+ // e.g. package id
+ pkg = qpos.info.Pkg
+ description = fmt.Sprintf("definition of package %q", pkg.Path())
+ } else {
+ // e.g. import id "..."
+ // or id.F()
+ pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported()
+ description = fmt.Sprintf("reference to package %q", pkg.Path())
+ }
+
+ default:
+ // Unreachable?
+ return nil, fmt.Errorf("unexpected AST for package: %T", n)
+ }
+
+ var members []*describeMember
+ // NB: "unsafe" has no types.Package
+ if pkg != nil {
+ // Enumerate the accessible package members
+ // in lexicographic order.
+ for _, name := range pkg.Scope().Names() {
+ if pkg == qpos.info.Pkg || ast.IsExported(name) {
+ mem := pkg.Scope().Lookup(name)
+ var methods []*types.Selection
+ if mem, ok := mem.(*types.TypeName); ok {
+ methods = accessibleMethods(mem.Type(), qpos.info.Pkg)
+ }
+ members = append(members, &describeMember{
+ mem,
+ methods,
+ })
+
+ }
+ }
+ }
+
+ return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil
+}
+
+type describePackageResult struct {
+ fset *token.FileSet
+ node ast.Node
+ description string
+ pkg *types.Package
+ members []*describeMember // in lexicographic name order
+}
+
+type describeMember struct {
+ obj types.Object
+ methods []*types.Selection // in types.MethodSet order
+}
+
+func (r *describePackageResult) display(printf printfFunc) {
+ printf(r.node, "%s", r.description)
+
+ // Compute max width of name "column".
+ maxname := 0
+ for _, mem := range r.members {
+ if l := len(mem.obj.Name()); l > maxname {
+ maxname = l
+ }
+ }
+
+ for _, mem := range r.members {
+ printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
+ for _, meth := range mem.methods {
+ printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg)))
+ }
+ }
+}
+
+func formatMember(obj types.Object, maxname int) string {
+ qualifier := types.RelativeTo(obj.Pkg())
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
+ switch obj := obj.(type) {
+ case *types.Const:
+ fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val()))
+
+ case *types.Func:
+ fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
+
+ case *types.TypeName:
+ // Abbreviate long aggregate type names.
+ var abbrev string
+ switch t := obj.Type().Underlying().(type) {
+ case *types.Interface:
+ if t.NumMethods() > 1 {
+ abbrev = "interface{...}"
+ }
+ case *types.Struct:
+ if t.NumFields() > 1 {
+ abbrev = "struct{...}"
+ }
+ }
+ if abbrev == "" {
+ fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier))
+ } else {
+ fmt.Fprintf(&buf, " %s", abbrev)
+ }
+
+ case *types.Var:
+ fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
+ }
+ return buf.String()
+}
+
+func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var members []*serial.DescribeMember
+ for _, mem := range r.members {
+ typ := mem.obj.Type()
+ var val string
+ switch mem := mem.obj.(type) {
+ case *types.Const:
+ val = constValString(mem.Val())
+ case *types.TypeName:
+ typ = typ.Underlying()
+ }
+ members = append(members, &serial.DescribeMember{
+ Name: mem.obj.Name(),
+ Type: typ.String(),
+ Value: val,
+ Pos: fset.Position(mem.obj.Pos()).String(),
+ Kind: tokenOf(mem.obj),
+ Methods: methodsToSerial(r.pkg, mem.methods, fset),
+ })
+ }
+ res.Describe = &serial.Describe{
+ Desc: r.description,
+ Pos: fset.Position(r.node.Pos()).String(),
+ Detail: "package",
+ Package: &serial.DescribePackage{
+ Path: r.pkg.Path(),
+ Members: members,
+ },
+ }
+}
+
+func tokenOf(o types.Object) string {
+ switch o.(type) {
+ case *types.Func:
+ return "func"
+ case *types.Var:
+ return "var"
+ case *types.TypeName:
+ return "type"
+ case *types.Const:
+ return "const"
+ case *types.PkgName:
+ return "package"
+ case *types.Builtin:
+ return "builtin" // e.g. when describing package "unsafe"
+ case *types.Nil:
+ return "nil"
+ case *types.Label:
+ return "label"
+ }
+ panic(o)
+}
+
+// ---- STATEMENT ------------------------------------------------------------
+
+func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) {
+ var description string
+ switch n := path[0].(type) {
+ case *ast.Ident:
+ if qpos.info.Defs[n] != nil {
+ description = "labelled statement"
+ } else {
+ description = "reference to labelled statement"
+ }
+
+ default:
+ // Nothing much to say about statements.
+ description = astutil.NodeDescription(n)
+ }
+ return &describeStmtResult{qpos.fset, path[0], description}, nil
+}
+
+type describeStmtResult struct {
+ fset *token.FileSet
+ node ast.Node
+ description string
+}
+
+func (r *describeStmtResult) display(printf printfFunc) {
+ printf(r.node, "%s", r.description)
+}
+
+func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ res.Describe = &serial.Describe{
+ Desc: r.description,
+ Pos: fset.Position(r.node.Pos()).String(),
+ Detail: "unknown",
+ }
+}
+
+// ------------------- Utilities -------------------
+
+// pathToString returns a string containing the concrete types of the
+// nodes in path.
+func pathToString(path []ast.Node) string {
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "[")
+ for i, n := range path {
+ if i > 0 {
+ fmt.Fprint(&buf, " ")
+ }
+ fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
+ }
+ fmt.Fprint(&buf, "]")
+ return buf.String()
+}
+
+func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
+ var methods []*types.Selection
+ for _, meth := range typeutil.IntuitiveMethodSet(t, nil) {
+ if isAccessibleFrom(meth.Obj(), from) {
+ methods = append(methods, meth)
+ }
+ }
+ return methods
+}
+
+func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
+ return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
+}
+
+func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
+ qualifier := types.RelativeTo(this)
+ var jmethods []serial.DescribeMethod
+ for _, meth := range methods {
+ var ser serial.DescribeMethod
+ if meth != nil { // may contain nils when called by implements (on a method)
+ ser = serial.DescribeMethod{
+ Name: types.SelectionString(meth, qualifier),
+ Pos: fset.Position(meth.Obj().Pos()).String(),
+ }
+ }
+ jmethods = append(jmethods, ser)
+ }
+ return jmethods
+}
+
+// constValString emulates Go 1.6's go/constant.ExactString well enough
+// to make the tests pass. This is just a stopgap until we throw away
+// all the *15.go files.
+func constValString(v exact.Value) string {
+ if v.Kind() == exact.Float {
+ f, _ := exact.Float64Val(v)
+ return fmt.Sprintf("%g", f)
+ }
+ return v.String()
+}
diff --git a/go/src/golang.org/x/tools/oracle/freevars.go b/go/src/golang.org/x/tools/oracle/freevars.go
index 400a118..6b89cb3 100644
--- a/go/src/golang.org/x/tools/oracle/freevars.go
+++ b/go/src/golang.org/x/tools/oracle/freevars.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
@@ -9,10 +11,10 @@
"go/ast"
"go/printer"
"go/token"
+ "go/types"
"sort"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -33,7 +35,7 @@
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
- if err := importQueryPackage(q.Pos, &lconf); err != nil {
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err
}
diff --git a/go/src/golang.org/x/tools/oracle/freevars14.go b/go/src/golang.org/x/tools/oracle/freevars14.go
new file mode 100644
index 0000000..760a9e6
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/freevars14.go
@@ -0,0 +1,224 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "bytes"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "sort"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// freevars displays the lexical (not package-level) free variables of
+// the selection.
+//
+// It treats A.B.C as a separate variable from A to reveal the parts
+// of an aggregate type that are actually needed.
+// This aids refactoring.
+//
+// TODO(adonovan): optionally display the free references to
+// file/package scope objects, and to objects from other packages.
+// Depending on where the resulting function abstraction will go,
+// these might be interesting. Perhaps group the results into three
+// bands.
+//
+func freevars(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+ allowErrors(&lconf)
+
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, false)
+ if err != nil {
+ return err
+ }
+
+ file := qpos.path[len(qpos.path)-1] // the enclosing file
+ fileScope := qpos.info.Scopes[file]
+ pkgScope := fileScope.Parent()
+
+ // The id and sel functions return non-nil if they denote an
+ // object o or selection o.x.y that is referenced by the
+ // selection but defined neither within the selection nor at
+ // file scope, i.e. it is in the lexical environment.
+ var id func(n *ast.Ident) types.Object
+ var sel func(n *ast.SelectorExpr) types.Object
+
+ sel = func(n *ast.SelectorExpr) types.Object {
+ switch x := unparen(n.X).(type) {
+ case *ast.SelectorExpr:
+ return sel(x)
+ case *ast.Ident:
+ return id(x)
+ }
+ return nil
+ }
+
+ id = func(n *ast.Ident) types.Object {
+ obj := qpos.info.Uses[n]
+ if obj == nil {
+ return nil // not a reference
+ }
+ if _, ok := obj.(*types.PkgName); ok {
+ return nil // imported package
+ }
+ if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {
+ return nil // not defined in this file
+ }
+ scope := obj.Parent()
+ if scope == nil {
+ return nil // e.g. interface method, struct field
+ }
+ if scope == fileScope || scope == pkgScope {
+ return nil // defined at file or package scope
+ }
+ if qpos.start <= obj.Pos() && obj.Pos() <= qpos.end {
+ return nil // defined within selection => not free
+ }
+ return obj
+ }
+
+ // Maps each reference that is free in the selection
+ // to the object it refers to.
+ // The map de-duplicates repeated references.
+ refsMap := make(map[string]freevarsRef)
+
+ // Visit all the identifiers in the selected ASTs.
+ ast.Inspect(qpos.path[0], func(n ast.Node) bool {
+ if n == nil {
+ return true // popping DFS stack
+ }
+
+ // Is this node contained within the selection?
+ // (freevars permits inexact selections,
+ // like two stmts in a block.)
+ if qpos.start <= n.Pos() && n.End() <= qpos.end {
+ var obj types.Object
+ var prune bool
+ switch n := n.(type) {
+ case *ast.Ident:
+ obj = id(n)
+
+ case *ast.SelectorExpr:
+ obj = sel(n)
+ prune = true
+ }
+
+ if obj != nil {
+ var kind string
+ switch obj.(type) {
+ case *types.Var:
+ kind = "var"
+ case *types.Func:
+ kind = "func"
+ case *types.TypeName:
+ kind = "type"
+ case *types.Const:
+ kind = "const"
+ case *types.Label:
+ kind = "label"
+ default:
+ panic(obj)
+ }
+
+ typ := qpos.info.TypeOf(n.(ast.Expr))
+ ref := freevarsRef{kind, printNode(lprog.Fset, n), typ, obj}
+ refsMap[ref.ref] = ref
+
+ if prune {
+ return false // don't descend
+ }
+ }
+ }
+
+ return true // descend
+ })
+
+ refs := make([]freevarsRef, 0, len(refsMap))
+ for _, ref := range refsMap {
+ refs = append(refs, ref)
+ }
+ sort.Sort(byRef(refs))
+
+ q.result = &freevarsResult{
+ qpos: qpos,
+ refs: refs,
+ }
+ return nil
+}
+
+type freevarsResult struct {
+ qpos *queryPos
+ refs []freevarsRef
+}
+
+type freevarsRef struct {
+ kind string
+ ref string
+ typ types.Type
+ obj types.Object
+}
+
+func (r *freevarsResult) display(printf printfFunc) {
+ if len(r.refs) == 0 {
+ printf(r.qpos, "No free identifiers.")
+ } else {
+ printf(r.qpos, "Free identifiers:")
+ qualifier := types.RelativeTo(r.qpos.info.Pkg)
+ for _, ref := range r.refs {
+ // Avoid printing "type T T".
+ var typstr string
+ if ref.kind != "type" {
+ typstr = " " + types.TypeString(ref.typ, qualifier)
+ }
+ printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr)
+ }
+ }
+}
+
+func (r *freevarsResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var refs []*serial.FreeVar
+ for _, ref := range r.refs {
+ refs = append(refs,
+ &serial.FreeVar{
+ Pos: fset.Position(ref.obj.Pos()).String(),
+ Kind: ref.kind,
+ Ref: ref.ref,
+ Type: ref.typ.String(),
+ })
+ }
+ res.Freevars = refs
+}
+
+// -------- utils --------
+
+type byRef []freevarsRef
+
+func (p byRef) Len() int { return len(p) }
+func (p byRef) Less(i, j int) bool { return p[i].ref < p[j].ref }
+func (p byRef) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+// printNode returns the pretty-printed syntax of n.
+func printNode(fset *token.FileSet, n ast.Node) string {
+ var buf bytes.Buffer
+ printer.Fprint(&buf, fset, n)
+ return buf.String()
+}
diff --git a/go/src/golang.org/x/tools/oracle/implements.go b/go/src/golang.org/x/tools/oracle/implements.go
index 3155ca2..7c9da1d 100644
--- a/go/src/golang.org/x/tools/oracle/implements.go
+++ b/go/src/golang.org/x/tools/oracle/implements.go
@@ -2,24 +2,27 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"reflect"
"sort"
"strings"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/oracle/serial"
+ "golang.org/x/tools/refactor/importgraph"
)
// Implements displays the "implements" relation as it pertains to the
-// selected type within a single package.
+// selected type.
// If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported
// by an implements query on the receiver type.
@@ -28,10 +31,35 @@
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
- if err := importQueryPackage(q.Pos, &lconf); err != nil {
+ qpkg, err := importQueryPackage(q.Pos, &lconf)
+ if err != nil {
return err
}
+ // Set the packages to search.
+ if len(q.Scope) > 0 {
+ // Inspect all packages in the analysis scope, if specified.
+ if err := setPTAScope(&lconf, q.Scope); err != nil {
+ return err
+ }
+ } else {
+ // Otherwise inspect the forward and reverse
+ // transitive closure of the selected package.
+ // (In theory even this is incomplete.)
+ _, rev, _ := importgraph.Build(q.Build)
+ for path := range rev.Search(qpkg) {
+ lconf.ImportWithTests(path)
+ }
+
+ // TODO(adonovan): for completeness, we should also
+ // type-check and inspect function bodies in all
+ // imported packages. This would be expensive, but we
+ // could optimize by skipping functions that do not
+ // contain type declarations. This would require
+ // changing the loader's TypeCheckFuncBodies hook to
+ // provide the []*ast.File.
+ }
+
// Load/parse/type-check the program.
lprog, err := lconf.Load()
if err != nil {
@@ -72,10 +100,6 @@
// Find all named types, even local types (which can have
// methods via promotion) and the built-in "error".
- //
- // TODO(adonovan): include all packages in PTA scope too?
- // i.e. don't reduceScope?
- //
var allNamed []types.Type
for _, info := range lprog.AllPackages {
for _, obj := range info.Defs {
diff --git a/go/src/golang.org/x/tools/oracle/implements14.go b/go/src/golang.org/x/tools/oracle/implements14.go
new file mode 100644
index 0000000..9f4c370
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/implements14.go
@@ -0,0 +1,354 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "reflect"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/oracle/serial"
+ "golang.org/x/tools/refactor/importgraph"
+)
+
+// Implements displays the "implements" relation as it pertains to the
+// selected type.
+// If the selection is a method, 'implements' displays
+// the corresponding methods of the types that would have been reported
+// by an implements query on the receiver type.
+//
+func implements(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+ allowErrors(&lconf)
+
+ qpkg, err := importQueryPackage(q.Pos, &lconf)
+ if err != nil {
+ return err
+ }
+
+ // Set the packages to search.
+ if len(q.Scope) > 0 {
+ // Inspect all packages in the analysis scope, if specified.
+ if err := setPTAScope(&lconf, q.Scope); err != nil {
+ return err
+ }
+ } else {
+ // Otherwise inspect the forward and reverse
+ // transitive closure of the selected package.
+ // (In theory even this is incomplete.)
+ _, rev, _ := importgraph.Build(q.Build)
+ for path := range rev.Search(qpkg) {
+ lconf.ImportWithTests(path)
+ }
+
+ // TODO(adonovan): for completeness, we should also
+ // type-check and inspect function bodies in all
+ // imported packages. This would be expensive, but we
+ // could optimize by skipping functions that do not
+ // contain type declarations. This would require
+ // changing the loader's TypeCheckFuncBodies hook to
+ // provide the []*ast.File.
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, false)
+ if err != nil {
+ return err
+ }
+
+ // Find the selected type.
+ path, action := findInterestingNode(qpos.info, qpos.path)
+
+ var method *types.Func
+ var T types.Type // selected type (receiver if method != nil)
+
+ switch action {
+ case actionExpr:
+ // method?
+ if id, ok := path[0].(*ast.Ident); ok {
+ if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
+ recv := obj.Type().(*types.Signature).Recv()
+ if recv == nil {
+ return fmt.Errorf("this function is not a method")
+ }
+ method = obj
+ T = recv.Type()
+ }
+ }
+ case actionType:
+ T = qpos.info.TypeOf(path[0].(ast.Expr))
+ }
+ if T == nil {
+ return fmt.Errorf("no type or method here")
+ }
+
+ // Find all named types, even local types (which can have
+ // methods via promotion) and the built-in "error".
+ var allNamed []types.Type
+ for _, info := range lprog.AllPackages {
+ for _, obj := range info.Defs {
+ if obj, ok := obj.(*types.TypeName); ok {
+ allNamed = append(allNamed, obj.Type())
+ }
+ }
+ }
+ allNamed = append(allNamed, types.Universe.Lookup("error").Type())
+
+ var msets typeutil.MethodSetCache
+
+ // Test each named type.
+ var to, from, fromPtr []types.Type
+ for _, U := range allNamed {
+ if isInterface(T) {
+ if msets.MethodSet(T).Len() == 0 {
+ continue // empty interface
+ }
+ if isInterface(U) {
+ if msets.MethodSet(U).Len() == 0 {
+ continue // empty interface
+ }
+
+ // T interface, U interface
+ if !types.Identical(T, U) {
+ if types.AssignableTo(U, T) {
+ to = append(to, U)
+ }
+ if types.AssignableTo(T, U) {
+ from = append(from, U)
+ }
+ }
+ } else {
+ // T interface, U concrete
+ if types.AssignableTo(U, T) {
+ to = append(to, U)
+ } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
+ to = append(to, pU)
+ }
+ }
+ } else if isInterface(U) {
+ if msets.MethodSet(U).Len() == 0 {
+ continue // empty interface
+ }
+
+ // T concrete, U interface
+ if types.AssignableTo(T, U) {
+ from = append(from, U)
+ } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
+ fromPtr = append(fromPtr, U)
+ }
+ }
+ }
+
+ var pos interface{} = qpos
+ if nt, ok := deref(T).(*types.Named); ok {
+ pos = nt.Obj()
+ }
+
+ // Sort types (arbitrarily) to ensure test determinism.
+ sort.Sort(typesByString(to))
+ sort.Sort(typesByString(from))
+ sort.Sort(typesByString(fromPtr))
+
+ var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
+ if method != nil {
+ for _, t := range to {
+ toMethod = append(toMethod,
+ types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
+ }
+ for _, t := range from {
+ fromMethod = append(fromMethod,
+ types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
+ }
+ for _, t := range fromPtr {
+ fromPtrMethod = append(fromPtrMethod,
+ types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
+ }
+ }
+
+ q.result = &implementsResult{
+ qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
+ }
+ return nil
+}
+
+type implementsResult struct {
+ qpos *queryPos
+
+ t types.Type // queried type (not necessarily named)
+ pos interface{} // pos of t (*types.Name or *QueryPos)
+ to []types.Type // named or ptr-to-named types assignable to interface T
+ from []types.Type // named interfaces assignable from T
+ fromPtr []types.Type // named interfaces assignable only from *T
+
+ // if a method was queried:
+ method *types.Func // queried method
+ toMethod []*types.Selection // method of type to[i], if any
+ fromMethod []*types.Selection // method of type from[i], if any
+ fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
+}
+
+func (r *implementsResult) display(printf printfFunc) {
+ relation := "is implemented by"
+
+ meth := func(sel *types.Selection) {
+ if sel != nil {
+ printf(sel.Obj(), "\t%s method (%s).%s",
+ relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name())
+ }
+ }
+
+ if isInterface(r.t) {
+ if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
+ printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t))
+ return
+ }
+
+ if r.method == nil {
+ printf(r.pos, "interface type %s", r.qpos.typeString(r.t))
+ } else {
+ printf(r.method, "abstract method %s", r.qpos.objectString(r.method))
+ }
+
+ // Show concrete types (or methods) first; use two passes.
+ for i, sub := range r.to {
+ if !isInterface(sub) {
+ if r.method == nil {
+ printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
+ relation, typeKind(sub), r.qpos.typeString(sub))
+ } else {
+ meth(r.toMethod[i])
+ }
+ }
+ }
+ for i, sub := range r.to {
+ if isInterface(sub) {
+ if r.method == nil {
+ printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
+ relation, typeKind(sub), r.qpos.typeString(sub))
+ } else {
+ meth(r.toMethod[i])
+ }
+ }
+ }
+
+ relation = "implements"
+ for i, super := range r.from {
+ if r.method == nil {
+ printf(super.(*types.Named).Obj(), "\t%s %s",
+ relation, r.qpos.typeString(super))
+ } else {
+ meth(r.fromMethod[i])
+ }
+ }
+ } else {
+ relation = "implements"
+
+ if r.from != nil {
+ if r.method == nil {
+ printf(r.pos, "%s type %s",
+ typeKind(r.t), r.qpos.typeString(r.t))
+ } else {
+ printf(r.method, "concrete method %s",
+ r.qpos.objectString(r.method))
+ }
+ for i, super := range r.from {
+ if r.method == nil {
+ printf(super.(*types.Named).Obj(), "\t%s %s",
+ relation, r.qpos.typeString(super))
+ } else {
+ meth(r.fromMethod[i])
+ }
+ }
+ }
+ if r.fromPtr != nil {
+ if r.method == nil {
+ printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t))
+ } else {
+ // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
+ printf(r.method, "concrete method %s",
+ r.qpos.objectString(r.method))
+ }
+
+ for i, psuper := range r.fromPtr {
+ if r.method == nil {
+ printf(psuper.(*types.Named).Obj(), "\t%s %s",
+ relation, r.qpos.typeString(psuper))
+ } else {
+ meth(r.fromPtrMethod[i])
+ }
+ }
+ } else if r.from == nil {
+ printf(r.pos, "%s type %s implements only interface{}",
+ typeKind(r.t), r.qpos.typeString(r.t))
+ }
+ }
+}
+
+func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ res.Implements = &serial.Implements{
+ T: makeImplementsType(r.t, fset),
+ AssignableTo: makeImplementsTypes(r.to, fset),
+ AssignableFrom: makeImplementsTypes(r.from, fset),
+ AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
+ AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
+ AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
+ AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
+ }
+ if r.method != nil {
+ res.Implements.Method = &serial.DescribeMethod{
+ Name: r.qpos.objectString(r.method),
+ Pos: fset.Position(r.method.Pos()).String(),
+ }
+ }
+}
+
+func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {
+ var r []serial.ImplementsType
+ for _, t := range tt {
+ r = append(r, makeImplementsType(t, fset))
+ }
+ return r
+}
+
+func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
+ var pos token.Pos
+ if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
+ pos = nt.Obj().Pos()
+ }
+ return serial.ImplementsType{
+ Name: T.String(),
+ Pos: fset.Position(pos).String(),
+ Kind: typeKind(T),
+ }
+}
+
+// typeKind returns a string describing the underlying kind of type,
+// e.g. "slice", "array", "struct".
+func typeKind(T types.Type) string {
+ s := reflect.TypeOf(T.Underlying()).String()
+ return strings.ToLower(strings.TrimPrefix(s, "*types."))
+}
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }
+
+type typesByString []types.Type
+
+func (p typesByString) Len() int { return len(p) }
+func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
+func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/go/src/golang.org/x/tools/oracle/oracle.go b/go/src/golang.org/x/tools/oracle/oracle.go
index 544cfa4..794cbe9 100644
--- a/go/src/golang.org/x/tools/oracle/oracle.go
+++ b/go/src/golang.org/x/tools/oracle/oracle.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package oracle contains the implementation of the oracle tool whose
// command-line is provided by golang.org/x/tools/cmd/oracle.
//
@@ -25,6 +27,7 @@
"go/build"
"go/parser"
"go/token"
+ "go/types"
"io"
"path/filepath"
@@ -32,7 +35,6 @@
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -190,10 +192,11 @@
// importQueryPackage finds the package P containing the
// query position and tells conf to import it.
-func importQueryPackage(pos string, conf *loader.Config) error {
+// It returns the package's path.
+func importQueryPackage(pos string, conf *loader.Config) (string, error) {
fqpos, err := fastQueryPos(pos)
if err != nil {
- return err // bad query
+ return "", err // bad query
}
filename := fqpos.fset.File(fqpos.start).Name()
@@ -202,10 +205,10 @@
// TODO(adonovan): ensure we report a clear error.
_, importPath, err := guessImportPath(filename, conf.Build)
if err != nil {
- return err // can't find GOPATH dir
+ return "", err // can't find GOPATH dir
}
if importPath == "" {
- return fmt.Errorf("can't guess import path from %s", filename)
+ return "", fmt.Errorf("can't guess import path from %s", filename)
}
// Check that it's possible to load the queried package.
@@ -215,7 +218,7 @@
cfg2.CgoEnabled = false
bp, err := cfg2.Import(importPath, "", 0)
if err != nil {
- return err // no files for package
+ return "", err // no files for package
}
switch pkgContainsFile(bp, filename) {
@@ -227,13 +230,13 @@
case 'G':
conf.Import(importPath)
default:
- return fmt.Errorf("package %q doesn't contain file %s",
+ return "", fmt.Errorf("package %q doesn't contain file %s",
importPath, filename)
}
conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
- return nil
+ return importPath, nil
}
// pkgContainsFile reports whether file was among the packages Go
diff --git a/go/src/golang.org/x/tools/oracle/oracle14.go b/go/src/golang.org/x/tools/oracle/oracle14.go
new file mode 100644
index 0000000..ed8a166
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/oracle14.go
@@ -0,0 +1,367 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package oracle contains the implementation of the oracle tool whose
+// command-line is provided by golang.org/x/tools/cmd/oracle.
+//
+// http://golang.org/s/oracle-design
+// http://golang.org/s/oracle-user-manual
+//
+package oracle // import "golang.org/x/tools/oracle"
+
+// This file defines oracle.Query, the entry point for the oracle tool.
+// The actual executable is defined in cmd/oracle.
+
+// TODO(adonovan): new queries
+// - show all statements that may update the selected lvalue
+// (local, global, field, etc).
+// - show all places where an object of type T is created
+// (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "io"
+ "path/filepath"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+type printfFunc func(pos interface{}, format string, args ...interface{})
+
+// queryResult is the interface of each query-specific result type.
+type queryResult interface {
+ toSerial(res *serial.Result, fset *token.FileSet)
+ display(printf printfFunc)
+}
+
+// A QueryPos represents the position provided as input to a query:
+// a textual extent in the program's source code, the AST node it
+// corresponds to, and the package to which it belongs.
+// Instances are created by parseQueryPos.
+type queryPos struct {
+ fset *token.FileSet
+ start, end token.Pos // source extent of query
+ path []ast.Node // AST path from query node to root of ast.File
+ exact bool // 2nd result of PathEnclosingInterval
+ info *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
+}
+
+// TypeString prints type T relative to the query position.
+func (qpos *queryPos) typeString(T types.Type) string {
+ return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
+}
+
+// ObjectString prints object obj relative to the query position.
+func (qpos *queryPos) objectString(obj types.Object) string {
+ return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
+}
+
+// SelectionString prints selection sel relative to the query position.
+func (qpos *queryPos) selectionString(sel *types.Selection) string {
+ return types.SelectionString(sel, types.RelativeTo(qpos.info.Pkg))
+}
+
+// A Query specifies a single oracle query.
+type Query struct {
+ Mode string // query mode ("callers", etc)
+ Pos string // query position
+ Build *build.Context // package loading configuration
+
+ // pointer analysis options
+ Scope []string // main packages in (*loader.Config).FromArgs syntax
+ PTALog io.Writer // (optional) pointer-analysis log file
+ Reflection bool // model reflection soundly (currently slow).
+
+ // Populated during Run()
+ Fset *token.FileSet
+ result queryResult
+}
+
+// Serial returns an instance of serial.Result, which implements the
+// {xml,json}.Marshaler interfaces so that query results can be
+// serialized as JSON or XML.
+//
+func (q *Query) Serial() *serial.Result {
+ resj := &serial.Result{Mode: q.Mode}
+ q.result.toSerial(resj, q.Fset)
+ return resj
+}
+
+// WriteTo writes the oracle query result res to out in a compiler diagnostic format.
+func (q *Query) WriteTo(out io.Writer) {
+ printf := func(pos interface{}, format string, args ...interface{}) {
+ fprintf(out, q.Fset, pos, format, args...)
+ }
+ q.result.display(printf)
+}
+
+// Run runs an oracle query and populates its Fset and Result.
+func Run(q *Query) error {
+ switch q.Mode {
+ case "callees":
+ return callees(q)
+ case "callers":
+ return callers(q)
+ case "callstack":
+ return callstack(q)
+ case "peers":
+ return peers(q)
+ case "pointsto":
+ return pointsto(q)
+ case "whicherrs":
+ return whicherrs(q)
+ case "definition":
+ return definition(q)
+ case "describe":
+ return describe(q)
+ case "freevars":
+ return freevars(q)
+ case "implements":
+ return implements(q)
+ case "referrers":
+ return referrers(q)
+ case "what":
+ return what(q)
+ default:
+ return fmt.Errorf("invalid mode: %q", q.Mode)
+ }
+}
+
+func setPTAScope(lconf *loader.Config, scope []string) error {
+ if len(scope) == 0 {
+ return fmt.Errorf("no packages specified for pointer analysis scope")
+ }
+
+ // Determine initial packages for PTA.
+ args, err := lconf.FromArgs(scope, true)
+ if err != nil {
+ return err
+ }
+ if len(args) > 0 {
+ return fmt.Errorf("surplus arguments: %q", args)
+ }
+ return nil
+}
+
+// Create a pointer.Config whose scope is the initial packages of lprog
+// and their dependencies.
+func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
+ // TODO(adonovan): the body of this function is essentially
+ // duplicated in all go/pointer clients. Refactor.
+
+ // For each initial package (specified on the command line),
+ // if it has a main function, analyze that,
+ // otherwise analyze its tests, if any.
+ var testPkgs, mains []*ssa.Package
+ for _, info := range lprog.InitialPackages() {
+ initialPkg := prog.Package(info.Pkg)
+
+ // Add package to the pointer analysis scope.
+ if initialPkg.Func("main") != nil {
+ mains = append(mains, initialPkg)
+ } else {
+ testPkgs = append(testPkgs, initialPkg)
+ }
+ }
+ if testPkgs != nil {
+ if p := prog.CreateTestMainPackage(testPkgs...); p != nil {
+ mains = append(mains, p)
+ }
+ }
+ if mains == nil {
+ return nil, fmt.Errorf("analysis scope has no main and no tests")
+ }
+ return &pointer.Config{
+ Log: ptaLog,
+ Reflection: reflection,
+ Mains: mains,
+ }, nil
+}
+
+// importQueryPackage finds the package P containing the
+// query position and tells conf to import it.
+// It returns the package's path.
+func importQueryPackage(pos string, conf *loader.Config) (string, error) {
+ fqpos, err := fastQueryPos(pos)
+ if err != nil {
+ return "", err // bad query
+ }
+ filename := fqpos.fset.File(fqpos.start).Name()
+
+ // This will not work for ad-hoc packages
+ // such as $GOROOT/src/net/http/triv.go.
+ // TODO(adonovan): ensure we report a clear error.
+ _, importPath, err := guessImportPath(filename, conf.Build)
+ if err != nil {
+ return "", err // can't find GOPATH dir
+ }
+ if importPath == "" {
+ return "", fmt.Errorf("can't guess import path from %s", filename)
+ }
+
+ // Check that it's possible to load the queried package.
+ // (e.g. oracle tests contain different 'package' decls in same dir.)
+ // Keep consistent with logic in loader/util.go!
+ cfg2 := *conf.Build
+ cfg2.CgoEnabled = false
+ bp, err := cfg2.Import(importPath, "", 0)
+ if err != nil {
+ return "", err // no files for package
+ }
+
+ switch pkgContainsFile(bp, filename) {
+ case 'T':
+ conf.ImportWithTests(importPath)
+ case 'X':
+ conf.ImportWithTests(importPath)
+ importPath += "_test" // for TypeCheckFuncBodies
+ case 'G':
+ conf.Import(importPath)
+ default:
+ return "", fmt.Errorf("package %q doesn't contain file %s",
+ importPath, filename)
+ }
+
+ conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
+
+ return importPath, nil
+}
+
+// pkgContainsFile reports whether file was among the packages Go
+// files, Test files, eXternal test files, or not found.
+func pkgContainsFile(bp *build.Package, filename string) byte {
+ for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
+ for _, file := range files {
+ if sameFile(filepath.Join(bp.Dir, file), filename) {
+ return "GTX"[i]
+ }
+ }
+ }
+ return 0 // not found
+}
+
+// ParseQueryPos parses the source query position pos and returns the
+// AST node of the loaded program lprog that it identifies.
+// If needExact, it must identify a single AST subtree;
+// this is appropriate for queries that allow fairly arbitrary syntax,
+// e.g. "describe".
+//
+func parseQueryPos(lprog *loader.Program, posFlag string, needExact bool) (*queryPos, error) {
+ filename, startOffset, endOffset, err := parsePosFlag(posFlag)
+ if err != nil {
+ return nil, err
+ }
+ start, end, err := findQueryPos(lprog.Fset, filename, startOffset, endOffset)
+ if err != nil {
+ return nil, err
+ }
+ info, path, exact := lprog.PathEnclosingInterval(start, end)
+ if path == nil {
+ return nil, fmt.Errorf("no syntax here")
+ }
+ if needExact && !exact {
+ return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
+ }
+ return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
+}
+
+// ---------- Utilities ----------
+
+// allowErrors causes type errors to be silently ignored.
+// (Not suitable if SSA construction follows.)
+func allowErrors(lconf *loader.Config) {
+ ctxt := *lconf.Build // copy
+ ctxt.CgoEnabled = false
+ lconf.Build = &ctxt
+ lconf.AllowErrors = true
+ // AllErrors makes the parser always return an AST instead of
+ // bailing out after 10 errors and returning an empty ast.File.
+ lconf.ParserMode = parser.AllErrors
+ lconf.TypeChecker.Error = func(err error) {}
+}
+
+// ptrAnalysis runs the pointer analysis and returns its result.
+func ptrAnalysis(conf *pointer.Config) *pointer.Result {
+ result, err := pointer.Analyze(conf)
+ if err != nil {
+ panic(err) // pointer analysis internal error
+ }
+ return result
+}
+
+func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
+
+// fprintf prints to w a message of the form "location: message\n"
+// where location is derived from pos.
+//
+// pos must be one of:
+// - a token.Pos, denoting a position
+// - an ast.Node, denoting an interval
+// - anything with a Pos() method:
+// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
+// - a QueryPos, denoting the extent of the user's query.
+// - nil, meaning no position at all.
+//
+// The output format is is compatible with the 'gnu'
+// compilation-error-regexp in Emacs' compilation mode.
+// TODO(adonovan): support other editors.
+//
+func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
+ var start, end token.Pos
+ switch pos := pos.(type) {
+ case ast.Node:
+ start = pos.Pos()
+ end = pos.End()
+ case token.Pos:
+ start = pos
+ end = start
+ case interface {
+ Pos() token.Pos
+ }:
+ start = pos.Pos()
+ end = start
+ case *queryPos:
+ start = pos.start
+ end = pos.end
+ case nil:
+ // no-op
+ default:
+ panic(fmt.Sprintf("invalid pos: %T", pos))
+ }
+
+ if sp := fset.Position(start); start == end {
+ // (prints "-: " for token.NoPos)
+ fmt.Fprintf(w, "%s: ", sp)
+ } else {
+ ep := fset.Position(end)
+ // The -1 below is a concession to Emacs's broken use of
+ // inclusive (not half-open) intervals.
+ // Other editors may not want it.
+ // TODO(adonovan): add an -editor=vim|emacs|acme|auto
+ // flag; auto uses EMACS=t / VIM=... / etc env vars.
+ fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
+ sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
+ }
+ fmt.Fprintf(w, format, args...)
+ io.WriteString(w, "\n")
+}
diff --git a/go/src/golang.org/x/tools/oracle/peers.go b/go/src/golang.org/x/tools/oracle/peers.go
index 9c2a497..0564516 100644
--- a/go/src/golang.org/x/tools/oracle/peers.go
+++ b/go/src/golang.org/x/tools/oracle/peers.go
@@ -2,18 +2,20 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"sort"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -55,7 +57,7 @@
}
// Defer SSA construction till after errors are reported.
- prog.BuildAll()
+ prog.Build()
var queryOp chanOp // the originating send or receive operation
var ops []chanOp // all sends/receives of opposite direction
diff --git a/go/src/golang.org/x/tools/oracle/peers14.go b/go/src/golang.org/x/tools/oracle/peers14.go
new file mode 100644
index 0000000..9b97cbf
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/peers14.go
@@ -0,0 +1,254 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "sort"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// peers enumerates, for a given channel send (or receive) operation,
+// the set of possible receives (or sends) that correspond to it.
+//
+// TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
+// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
+// or the implicit receive in "for v := range ch".
+func peers(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+
+ if err := setPTAScope(&lconf, q.Scope); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, false)
+ if err != nil {
+ return err
+ }
+
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
+
+ ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
+ if err != nil {
+ return err
+ }
+
+ opPos := findOp(qpos)
+ if opPos == token.NoPos {
+ return fmt.Errorf("there is no channel operation here")
+ }
+
+ // Defer SSA construction till after errors are reported.
+ prog.Build()
+
+ var queryOp chanOp // the originating send or receive operation
+ var ops []chanOp // all sends/receives of opposite direction
+
+ // Look at all channel operations in the whole ssa.Program.
+ // Build a list of those of same type as the query.
+ allFuncs := ssautil.AllFunctions(prog)
+ for fn := range allFuncs {
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ for _, op := range chanOps(instr) {
+ ops = append(ops, op)
+ if op.pos == opPos {
+ queryOp = op // we found the query op
+ }
+ }
+ }
+ }
+ }
+ if queryOp.ch == nil {
+ return fmt.Errorf("ssa.Instruction for send/receive not found")
+ }
+
+ // Discard operations of wrong channel element type.
+ // Build set of channel ssa.Values as query to pointer analysis.
+ // We compare channels by element types, not channel types, to
+ // ignore both directionality and type names.
+ queryType := queryOp.ch.Type()
+ queryElemType := queryType.Underlying().(*types.Chan).Elem()
+ ptaConfig.AddQuery(queryOp.ch)
+ i := 0
+ for _, op := range ops {
+ if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
+ ptaConfig.AddQuery(op.ch)
+ ops[i] = op
+ i++
+ }
+ }
+ ops = ops[:i]
+
+ // Run the pointer analysis.
+ ptares := ptrAnalysis(ptaConfig)
+
+ // Find the points-to set.
+ queryChanPtr := ptares.Queries[queryOp.ch]
+
+ // Ascertain which make(chan) labels the query's channel can alias.
+ var makes []token.Pos
+ for _, label := range queryChanPtr.PointsTo().Labels() {
+ makes = append(makes, label.Pos())
+ }
+ sort.Sort(byPos(makes))
+
+ // Ascertain which channel operations can alias the same make(chan) labels.
+ var sends, receives, closes []token.Pos
+ for _, op := range ops {
+ if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
+ switch op.dir {
+ case types.SendOnly:
+ sends = append(sends, op.pos)
+ case types.RecvOnly:
+ receives = append(receives, op.pos)
+ case types.SendRecv:
+ closes = append(closes, op.pos)
+ }
+ }
+ }
+ sort.Sort(byPos(sends))
+ sort.Sort(byPos(receives))
+ sort.Sort(byPos(closes))
+
+ q.result = &peersResult{
+ queryPos: opPos,
+ queryType: queryType,
+ makes: makes,
+ sends: sends,
+ receives: receives,
+ closes: closes,
+ }
+ return nil
+}
+
+// findOp returns the position of the enclosing send/receive/close op.
+// For send and receive operations, this is the position of the <- token;
+// for close operations, it's the Lparen of the function call.
+//
+// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
+func findOp(qpos *queryPos) token.Pos {
+ for _, n := range qpos.path {
+ switch n := n.(type) {
+ case *ast.UnaryExpr:
+ if n.Op == token.ARROW {
+ return n.OpPos
+ }
+ case *ast.SendStmt:
+ return n.Arrow
+ case *ast.CallExpr:
+ // close function call can only exist as a direct identifier
+ if close, ok := unparen(n.Fun).(*ast.Ident); ok {
+ if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
+ return n.Lparen
+ }
+ }
+ }
+ }
+ return token.NoPos
+}
+
+// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
+type chanOp struct {
+ ch ssa.Value
+ dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
+ pos token.Pos
+}
+
+// chanOps returns a slice of all the channel operations in the instruction.
+func chanOps(instr ssa.Instruction) []chanOp {
+ // TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
+ var ops []chanOp
+ switch instr := instr.(type) {
+ case *ssa.UnOp:
+ if instr.Op == token.ARROW {
+ ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
+ }
+ case *ssa.Send:
+ ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
+ case *ssa.Select:
+ for _, st := range instr.States {
+ ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
+ }
+ case ssa.CallInstruction:
+ cc := instr.Common()
+ if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
+ ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
+ }
+ }
+ return ops
+}
+
+type peersResult struct {
+ queryPos token.Pos // of queried channel op
+ queryType types.Type // type of queried channel
+ makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
+}
+
+func (r *peersResult) display(printf printfFunc) {
+ if len(r.makes) == 0 {
+ printf(r.queryPos, "This channel can't point to anything.")
+ return
+ }
+ printf(r.queryPos, "This channel of type %s may be:", r.queryType)
+ for _, alloc := range r.makes {
+ printf(alloc, "\tallocated here")
+ }
+ for _, send := range r.sends {
+ printf(send, "\tsent to, here")
+ }
+ for _, receive := range r.receives {
+ printf(receive, "\treceived from, here")
+ }
+ for _, clos := range r.closes {
+ printf(clos, "\tclosed, here")
+ }
+}
+
+func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ peers := &serial.Peers{
+ Pos: fset.Position(r.queryPos).String(),
+ Type: r.queryType.String(),
+ }
+ for _, alloc := range r.makes {
+ peers.Allocs = append(peers.Allocs, fset.Position(alloc).String())
+ }
+ for _, send := range r.sends {
+ peers.Sends = append(peers.Sends, fset.Position(send).String())
+ }
+ for _, receive := range r.receives {
+ peers.Receives = append(peers.Receives, fset.Position(receive).String())
+ }
+ for _, clos := range r.closes {
+ peers.Closes = append(peers.Closes, fset.Position(clos).String())
+ }
+ res.Peers = peers
+}
+
+// -------- utils --------
+
+// NB: byPos is not deterministic across packages since it depends on load order.
+// Use lessPos if the tests need it.
+type byPos []token.Pos
+
+func (p byPos) Len() int { return len(p) }
+func (p byPos) Less(i, j int) bool { return p[i] < p[j] }
+func (p byPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/go/src/golang.org/x/tools/oracle/pointsto.go b/go/src/golang.org/x/tools/oracle/pointsto.go
index d366dd3..9b862f5 100644
--- a/go/src/golang.org/x/tools/oracle/pointsto.go
+++ b/go/src/golang.org/x/tools/oracle/pointsto.go
@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"sort"
"golang.org/x/tools/go/ast/astutil"
@@ -15,7 +18,6 @@
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -95,7 +97,7 @@
}
// Defer SSA construction till after errors are reported.
- prog.BuildAll()
+ prog.Build()
// Run the pointer analysis.
ptrs, err := runPTA(ptaConfig, value, isAddr)
diff --git a/go/src/golang.org/x/tools/oracle/pointsto14.go b/go/src/golang.org/x/tools/oracle/pointsto14.go
new file mode 100644
index 0000000..1e40619
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/pointsto14.go
@@ -0,0 +1,293 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "sort"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+// pointsto runs the pointer analysis on the selected expression,
+// and reports its points-to set (for a pointer-like expression)
+// or its dynamic types (for an interface, reflect.Value, or
+// reflect.Type expression) and their points-to sets.
+//
+// All printed sets are sorted to ensure determinism.
+//
+func pointsto(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+
+ if err := setPTAScope(&lconf, q.Scope); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
+ if err != nil {
+ return err
+ }
+
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
+
+ ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
+ if err != nil {
+ return err
+ }
+
+ path, action := findInterestingNode(qpos.info, qpos.path)
+ if action != actionExpr {
+ return fmt.Errorf("pointer analysis wants an expression; got %s",
+ astutil.NodeDescription(qpos.path[0]))
+ }
+
+ var expr ast.Expr
+ var obj types.Object
+ switch n := path[0].(type) {
+ case *ast.ValueSpec:
+ // ambiguous ValueSpec containing multiple names
+ return fmt.Errorf("multiple value specification")
+ case *ast.Ident:
+ obj = qpos.info.ObjectOf(n)
+ expr = n
+ case ast.Expr:
+ expr = n
+ default:
+ // TODO(adonovan): is this reachable?
+ return fmt.Errorf("unexpected AST for expr: %T", n)
+ }
+
+ // Reject non-pointerlike types (includes all constants---except nil).
+ // TODO(adonovan): reject nil too.
+ typ := qpos.info.TypeOf(expr)
+ if !pointer.CanPoint(typ) {
+ return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ)
+ }
+
+ // Determine the ssa.Value for the expression.
+ var value ssa.Value
+ var isAddr bool
+ if obj != nil {
+ // def/ref of func/var object
+ value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path)
+ } else {
+ value, isAddr, err = ssaValueForExpr(prog, qpos.info, path)
+ }
+ if err != nil {
+ return err // e.g. trivially dead code
+ }
+
+ // Defer SSA construction till after errors are reported.
+ prog.Build()
+
+ // Run the pointer analysis.
+ ptrs, err := runPTA(ptaConfig, value, isAddr)
+ if err != nil {
+ return err // e.g. analytically unreachable
+ }
+
+ q.result = &pointstoResult{
+ qpos: qpos,
+ typ: typ,
+ ptrs: ptrs,
+ }
+ return nil
+}
+
+// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
+// to the root of the AST is path. isAddr reports whether the
+// ssa.Value is the address denoted by the ast.Ident, not its value.
+//
+func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
+ switch obj := obj.(type) {
+ case *types.Var:
+ pkg := prog.Package(qinfo.Pkg)
+ pkg.Build()
+ if v, addr := prog.VarValue(obj, pkg, path); v != nil {
+ return v, addr, nil
+ }
+ return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name())
+
+ case *types.Func:
+ fn := prog.FuncValue(obj)
+ if fn == nil {
+ return nil, false, fmt.Errorf("%s is an interface method", obj)
+ }
+ // TODO(adonovan): there's no point running PTA on a *Func ident.
+ // Eliminate this feature.
+ return fn, false, nil
+ }
+ panic(obj)
+}
+
+// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
+// expression whose path to the root of the AST is path.
+//
+func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
+ pkg := prog.Package(qinfo.Pkg)
+ pkg.SetDebugMode(true)
+ pkg.Build()
+
+ fn := ssa.EnclosingFunction(pkg, path)
+ if fn == nil {
+ return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)")
+ }
+
+ if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
+ return v, addr, nil
+ }
+
+ return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn)
+}
+
+// runPTA runs the pointer analysis of the selected SSA value or address.
+func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) {
+ T := v.Type()
+ if isAddr {
+ conf.AddIndirectQuery(v)
+ T = deref(T)
+ } else {
+ conf.AddQuery(v)
+ }
+ ptares := ptrAnalysis(conf)
+
+ var ptr pointer.Pointer
+ if isAddr {
+ ptr = ptares.IndirectQueries[v]
+ } else {
+ ptr = ptares.Queries[v]
+ }
+ if ptr == (pointer.Pointer{}) {
+ return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)")
+ }
+ pts := ptr.PointsTo()
+
+ if pointer.CanHaveDynamicTypes(T) {
+ // Show concrete types for interface/reflect.Value expression.
+ if concs := pts.DynamicTypes(); concs.Len() > 0 {
+ concs.Iterate(func(conc types.Type, pta interface{}) {
+ labels := pta.(pointer.PointsToSet).Labels()
+ sort.Sort(byPosAndString(labels)) // to ensure determinism
+ ptrs = append(ptrs, pointerResult{conc, labels})
+ })
+ }
+ } else {
+ // Show labels for other expressions.
+ labels := pts.Labels()
+ sort.Sort(byPosAndString(labels)) // to ensure determinism
+ ptrs = append(ptrs, pointerResult{T, labels})
+ }
+ sort.Sort(byTypeString(ptrs)) // to ensure determinism
+ return ptrs, nil
+}
+
+type pointerResult struct {
+ typ types.Type // type of the pointer (always concrete)
+ labels []*pointer.Label // set of labels
+}
+
+type pointstoResult struct {
+ qpos *queryPos
+ typ types.Type // type of expression
+ ptrs []pointerResult // pointer info (typ is concrete => len==1)
+}
+
+func (r *pointstoResult) display(printf printfFunc) {
+ if pointer.CanHaveDynamicTypes(r.typ) {
+ // Show concrete types for interface, reflect.Type or
+ // reflect.Value expression.
+
+ if len(r.ptrs) > 0 {
+ printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ))
+ for _, ptr := range r.ptrs {
+ var obj types.Object
+ if nt, ok := deref(ptr.typ).(*types.Named); ok {
+ obj = nt.Obj()
+ }
+ if len(ptr.labels) > 0 {
+ printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ))
+ printLabels(printf, ptr.labels, "\t\t")
+ } else {
+ printf(obj, "\t%s", r.qpos.typeString(ptr.typ))
+ }
+ }
+ } else {
+ printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ)
+ }
+ } else {
+ // Show labels for other expressions.
+ if ptr := r.ptrs[0]; len(ptr.labels) > 0 {
+ printf(r.qpos, "this %s may point to these objects:",
+ r.qpos.typeString(r.typ))
+ printLabels(printf, ptr.labels, "\t")
+ } else {
+ printf(r.qpos, "this %s may not point to anything.",
+ r.qpos.typeString(r.typ))
+ }
+ }
+}
+
+func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ var pts []serial.PointsTo
+ for _, ptr := range r.ptrs {
+ var namePos string
+ if nt, ok := deref(ptr.typ).(*types.Named); ok {
+ namePos = fset.Position(nt.Obj().Pos()).String()
+ }
+ var labels []serial.PointsToLabel
+ for _, l := range ptr.labels {
+ labels = append(labels, serial.PointsToLabel{
+ Pos: fset.Position(l.Pos()).String(),
+ Desc: l.String(),
+ })
+ }
+ pts = append(pts, serial.PointsTo{
+ Type: r.qpos.typeString(ptr.typ),
+ NamePos: namePos,
+ Labels: labels,
+ })
+ }
+ res.PointsTo = pts
+}
+
+type byTypeString []pointerResult
+
+func (a byTypeString) Len() int { return len(a) }
+func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() }
+func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type byPosAndString []*pointer.Label
+
+func (a byPosAndString) Len() int { return len(a) }
+func (a byPosAndString) Less(i, j int) bool {
+ cmp := a[i].Pos() - a[j].Pos()
+ return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
+}
+func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) {
+ // TODO(adonovan): due to context-sensitivity, many of these
+ // labels may differ only by context, which isn't apparent.
+ for _, label := range labels {
+ printf(label, "%s%s", prefix, label)
+ }
+}
diff --git a/go/src/golang.org/x/tools/oracle/referrers.go b/go/src/golang.org/x/tools/oracle/referrers.go
index 7383d1f..3545406 100644
--- a/go/src/golang.org/x/tools/oracle/referrers.go
+++ b/go/src/golang.org/x/tools/oracle/referrers.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
@@ -9,11 +11,11 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"io/ioutil"
"sort"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
"golang.org/x/tools/refactor/importgraph"
)
@@ -24,7 +26,7 @@
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
- if err := importQueryPackage(q.Pos, &lconf); err != nil {
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
return err
}
@@ -168,29 +170,33 @@
// Show referring lines, like grep.
type fileinfo struct {
refs []*ast.Ident
- linenums []int // line number of refs[i]
- data chan []byte // file contents
+ linenums []int // line number of refs[i]
+ data chan interface{} // file contents or error
}
var fileinfos []*fileinfo
fileinfosByName := make(map[string]*fileinfo)
// First pass: start the file reads concurrently.
+ sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
for _, ref := range r.refs {
posn := r.qpos.fset.Position(ref.Pos())
fi := fileinfosByName[posn.Filename]
if fi == nil {
- fi = &fileinfo{data: make(chan []byte)}
+ fi = &fileinfo{data: make(chan interface{})}
fileinfosByName[posn.Filename] = fi
fileinfos = append(fileinfos, fi)
// First request for this file:
// start asynchronous read.
go func() {
+ sema <- struct{}{} // acquire token
content, err := ioutil.ReadFile(posn.Filename)
+ <-sema // release token
if err != nil {
- content = []byte(fmt.Sprintf("error: %v", err))
+ fi.data <- err
+ } else {
+ fi.data <- content
}
- fi.data <- content
}()
}
fi.refs = append(fi.refs, ref)
@@ -200,8 +206,20 @@
// Second pass: print refs in original order.
// One line may have several refs at different columns.
for _, fi := range fileinfos {
- content := <-fi.data // wait for I/O completion
- lines := bytes.Split(content, []byte("\n"))
+ v := <-fi.data // wait for I/O completion
+
+ // Print one item for all refs in a file that could not
+ // be loaded (perhaps due to //line directives).
+ if err, ok := v.(error); ok {
+ var suffix string
+ if more := len(fi.refs) - 1; more > 0 {
+ suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
+ }
+ printf(fi.refs[0], "%v%s", err, suffix)
+ continue
+ }
+
+ lines := bytes.Split(v.([]byte), []byte("\n"))
for i, ref := range fi.refs {
printf(ref, "%s", lines[fi.linenums[i]-1])
}
diff --git a/go/src/golang.org/x/tools/oracle/referrers14.go b/go/src/golang.org/x/tools/oracle/referrers14.go
new file mode 100644
index 0000000..17adf02
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/referrers14.go
@@ -0,0 +1,243 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "io/ioutil"
+ "sort"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+ "golang.org/x/tools/refactor/importgraph"
+)
+
+// Referrers reports all identifiers that resolve to the same object
+// as the queried identifier, within any package in the analysis scope.
+func referrers(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+ allowErrors(&lconf)
+
+ if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
+ return err
+ }
+
+ var id *ast.Ident
+ var obj types.Object
+ var lprog *loader.Program
+ var pass2 bool
+ var qpos *queryPos
+ for {
+ // Load/parse/type-check the program.
+ var err error
+ lprog, err = lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err = parseQueryPos(lprog, q.Pos, false)
+ if err != nil {
+ return err
+ }
+
+ id, _ = qpos.path[0].(*ast.Ident)
+ if id == nil {
+ return fmt.Errorf("no identifier here")
+ }
+
+ obj = qpos.info.ObjectOf(id)
+ if obj == nil {
+ // Happens for y in "switch y := x.(type)",
+ // the package declaration,
+ // and unresolved identifiers.
+ if _, ok := qpos.path[1].(*ast.File); ok { // package decl?
+ pkg := qpos.info.Pkg
+ obj = types.NewPkgName(id.Pos(), pkg, pkg.Name(), pkg)
+ } else {
+ return fmt.Errorf("no object for identifier: %T", qpos.path[1])
+ }
+ }
+
+ if pass2 {
+ break
+ }
+
+ // If the identifier is exported, we must load all packages that
+ // depend transitively upon the package that defines it.
+ // Treat PkgNames as exported, even though they're lowercase.
+ if _, isPkg := obj.(*types.PkgName); !(isPkg || obj.Exported()) {
+ break // not exported
+ }
+
+ // Scan the workspace and build the import graph.
+ // Ignore broken packages.
+ _, rev, _ := importgraph.Build(q.Build)
+
+ // Re-load the larger program.
+ // Create a new file set so that ...
+ // External test packages are never imported,
+ // so they will never appear in the graph.
+ // (We must reset the Config here, not just reset the Fset field.)
+ lconf = loader.Config{
+ Fset: token.NewFileSet(),
+ Build: q.Build,
+ }
+ allowErrors(&lconf)
+ for path := range rev.Search(obj.Pkg().Path()) {
+ lconf.ImportWithTests(path)
+ }
+ pass2 = true
+ }
+
+ // Iterate over all go/types' Uses facts for the entire program.
+ var refs []*ast.Ident
+ for _, info := range lprog.AllPackages {
+ for id2, obj2 := range info.Uses {
+ if sameObj(obj, obj2) {
+ refs = append(refs, id2)
+ }
+ }
+ }
+ sort.Sort(byNamePos{q.Fset, refs})
+
+ q.result = &referrersResult{
+ qpos: qpos,
+ query: id,
+ obj: obj,
+ refs: refs,
+ }
+ return nil
+}
+
+// same reports whether x and y are identical, or both are PkgNames
+// that import the same Package.
+//
+func sameObj(x, y types.Object) bool {
+ if x == y {
+ return true
+ }
+ if x, ok := x.(*types.PkgName); ok {
+ if y, ok := y.(*types.PkgName); ok {
+ return x.Imported() == y.Imported()
+ }
+ }
+ return false
+}
+
+// -------- utils --------
+
+// An deterministic ordering for token.Pos that doesn't
+// depend on the order in which packages were loaded.
+func lessPos(fset *token.FileSet, x, y token.Pos) bool {
+ fx := fset.File(x)
+ fy := fset.File(y)
+ if fx != fy {
+ return fx.Name() < fy.Name()
+ }
+ return x < y
+}
+
+type byNamePos struct {
+ fset *token.FileSet
+ ids []*ast.Ident
+}
+
+func (p byNamePos) Len() int { return len(p.ids) }
+func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
+func (p byNamePos) Less(i, j int) bool {
+ return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
+}
+
+type referrersResult struct {
+ qpos *queryPos
+ query *ast.Ident // identifier of query
+ obj types.Object // object it denotes
+ refs []*ast.Ident // set of all other references to it
+}
+
+func (r *referrersResult) display(printf printfFunc) {
+ printf(r.obj, "%d references to %s", len(r.refs), r.qpos.objectString(r.obj))
+
+ // Show referring lines, like grep.
+ type fileinfo struct {
+ refs []*ast.Ident
+ linenums []int // line number of refs[i]
+ data chan interface{} // file contents or error
+ }
+ var fileinfos []*fileinfo
+ fileinfosByName := make(map[string]*fileinfo)
+
+ // First pass: start the file reads concurrently.
+ sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
+ for _, ref := range r.refs {
+ posn := r.qpos.fset.Position(ref.Pos())
+ fi := fileinfosByName[posn.Filename]
+ if fi == nil {
+ fi = &fileinfo{data: make(chan interface{})}
+ fileinfosByName[posn.Filename] = fi
+ fileinfos = append(fileinfos, fi)
+
+ // First request for this file:
+ // start asynchronous read.
+ go func() {
+ sema <- struct{}{} // acquire token
+ content, err := ioutil.ReadFile(posn.Filename)
+ <-sema // release token
+ if err != nil {
+ fi.data <- err
+ } else {
+ fi.data <- content
+ }
+ }()
+ }
+ fi.refs = append(fi.refs, ref)
+ fi.linenums = append(fi.linenums, posn.Line)
+ }
+
+ // Second pass: print refs in original order.
+ // One line may have several refs at different columns.
+ for _, fi := range fileinfos {
+ v := <-fi.data // wait for I/O completion
+
+ // Print one item for all refs in a file that could not
+ // be loaded (perhaps due to //line directives).
+ if err, ok := v.(error); ok {
+ var suffix string
+ if more := len(fi.refs) - 1; more > 0 {
+ suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
+ }
+ printf(fi.refs[0], "%v%s", err, suffix)
+ continue
+ }
+
+ lines := bytes.Split(v.([]byte), []byte("\n"))
+ for i, ref := range fi.refs {
+ printf(ref, "%s", lines[fi.linenums[i]-1])
+ }
+ }
+}
+
+// TODO(adonovan): encode extent, not just Pos info, in Serial form.
+
+func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ referrers := &serial.Referrers{
+ Pos: fset.Position(r.query.Pos()).String(),
+ Desc: r.obj.String(),
+ }
+ if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos()
+ referrers.ObjPos = fset.Position(pos).String()
+ }
+ for _, ref := range r.refs {
+ referrers.Refs = append(referrers.Refs, fset.Position(ref.NamePos).String())
+ }
+ res.Referrers = referrers
+}
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.go b/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.go
index 54bb895..849fb6e 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.go
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.go
@@ -83,6 +83,9 @@
fmt.Println() // @callees callees-qualified-call "Println"
m := new(method)
m.f() // @callees callees-static-method-call "f"
+ g := new(embeddedIface)
+ g.iface = m
+ g.f() // @callees callees-implicit-selection-method-call "f"
}
type myint int
@@ -96,6 +99,14 @@
func (method) f() {
}
+type embeddedIface struct {
+ iface
+}
+
+type iface interface {
+ f()
+}
+
var dynamic = func() {}
func deadcode() {
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.golden b/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.golden
index b5e6547..9159cd6 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.golden
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/calls/main.golden
@@ -96,6 +96,10 @@
this static function call dispatches to:
(main.method).f
+-------- @callees callees-implicit-selection-method-call --------
+this dynamic method call dispatches to:
+ (main.method).f
+
-------- @callers callers-not-a-wrapper --------
(main.myint).f is called from these 1 sites:
dynamic method call from main.main
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.go b/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.go
index 69e0a75..f48d6d0 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.go
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.go
@@ -6,6 +6,15 @@
// TODO(adonovan): more coverage of the (extensive) logic.
+import (
+ "nosuchpkg" // @describe badimport1 "nosuchpkg"
+ nosuchpkg2 "nosuchpkg" // @describe badimport2 "nosuchpkg2"
+ _ "unsafe" // @describe unsafe "unsafe"
+)
+
+var _ nosuchpkg.T
+var _ nosuchpkg2.T
+
type cake float64 // @describe type-ref-builtin "float64"
const c = iota // @describe const-ref-iota "iota"
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.golden b/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.golden
index 33d751a..bc6d9f7 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.golden
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/describe/main.golden
@@ -10,8 +10,21 @@
type cake float64
var global *string
func main func()
- const pi untyped float = 3141/1000
- const pie cake = 1768225803696341/562949953421312
+ const pi untyped float = 3.141
+ const pie cake = 3.141
+
+-------- @describe badimport1 --------
+
+Error: can't import package "nosuchpkg"
+-------- @describe badimport2 --------
+
+Error: can't import package "nosuchpkg"
+-------- @describe unsafe --------
+import of package "unsafe"
+ builtin Alignof
+ builtin Offsetof
+ type Pointer unsafe.Pointer
+ builtin Sizeof
-------- @describe type-ref-builtin --------
reference to built-in type float64
@@ -26,7 +39,7 @@
definition of const pie cake
-------- @describe const-ref-pi --------
-reference to const pi untyped float of constant value 3141/1000
+reference to const pi untyped float of constant value 3.141
defined here
-------- @describe func-def-main --------
@@ -114,7 +127,7 @@
definition of const localpie cake
-------- @describe const-ref-localpi --------
-reference to const localpi untyped float of constant value 3141/1000
+reference to const localpi untyped float of constant value 3.141
defined here
-------- @describe type-def-T --------
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods-json/main.golden b/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods-json/main.golden
index fa117df..831900a 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods-json/main.golden
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods-json/main.golden
@@ -274,6 +274,18 @@
"name": "lib.Type",
"pos": "testdata/src/lib/lib.go:3:6",
"kind": "basic"
+ },
+ {
+ "name": "main.I",
+ "pos": "testdata/src/implements-methods-json/main.go:35:6",
+ "kind": "interface"
+ }
+ ],
+ "from": [
+ {
+ "name": "main.I",
+ "pos": "testdata/src/implements-methods-json/main.go:35:6",
+ "kind": "interface"
}
],
"method": {
@@ -284,6 +296,16 @@
{
"name": "method (lib.Type) Method(x *int) *int",
"pos": "testdata/src/lib/lib.go:5:13"
+ },
+ {
+ "name": "method (main.I) Method(*int) *int",
+ "pos": "testdata/src/implements-methods-json/main.go:36:2"
+ }
+ ],
+ "from_method": [
+ {
+ "name": "method (main.I) Method(*int) *int",
+ "pos": "testdata/src/implements-methods-json/main.go:36:2"
}
]
}
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods/main.golden b/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods/main.golden
index bd591e8..227d305 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods/main.golden
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/implements-methods/main.golden
@@ -34,4 +34,6 @@
-------- @implements I.Method --------
abstract method func (I).Method(*int) *int
is implemented by method (lib.Type).Method
+ is implemented by method (main.I).Method
+ implements method (main.I).Method
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/implements/main.golden b/go/src/golang.org/x/tools/oracle/testdata/src/implements/main.golden
index ee00f3d..cb2f2ac 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/implements/main.golden
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/implements/main.golden
@@ -41,4 +41,6 @@
-------- @implements I --------
interface type I
is implemented by basic type lib.Type
+ is implemented by interface type main.I
+ implements main.I
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.go b/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.go
index 36cdb7a..fef571a 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.go
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.go
@@ -24,3 +24,11 @@
var s2 s
s2.f = 1
}
+
+// Test //line directives:
+
+type U int // @referrers ref-type-U "U"
+
+//line nosuchfile.y:123
+var u1 U
+var u2 U
diff --git a/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.golden b/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.golden
index 1f04be5..d98d510 100644
--- a/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.golden
+++ b/go/src/golang.org/x/tools/oracle/testdata/src/referrers/main.golden
@@ -36,3 +36,7 @@
_ = s{}.f // @referrers ref-field "f"
s2.f = 1
+-------- @referrers ref-type-U --------
+2 references to type U int
+open testdata/src/referrers/nosuchfile.y: no such file or directory (+ 1 more refs in this file)
+
diff --git a/go/src/golang.org/x/tools/oracle/whicherrs.go b/go/src/golang.org/x/tools/oracle/whicherrs.go
index aaa6068..be43c39 100644
--- a/go/src/golang.org/x/tools/oracle/whicherrs.go
+++ b/go/src/golang.org/x/tools/oracle/whicherrs.go
@@ -2,19 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package oracle
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"sort"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -91,7 +93,7 @@
}
// Defer SSA construction till after errors are reported.
- prog.BuildAll()
+ prog.Build()
globals := findVisibleErrs(prog, qpos)
constants := findVisibleConsts(prog, qpos)
diff --git a/go/src/golang.org/x/tools/oracle/whicherrs14.go b/go/src/golang.org/x/tools/oracle/whicherrs14.go
new file mode 100644
index 0000000..25449f3
--- /dev/null
+++ b/go/src/golang.org/x/tools/oracle/whicherrs14.go
@@ -0,0 +1,328 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package oracle
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "sort"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/oracle/serial"
+)
+
+var builtinErrorType = types.Universe.Lookup("error").Type()
+
+// whicherrs takes an position to an error and tries to find all types, constants
+// and global value which a given error can point to and which can be checked from the
+// scope where the error lives.
+// In short, it returns a list of things that can be checked against in order to handle
+// an error properly.
+//
+// TODO(dmorsing): figure out if fields in errors like *os.PathError.Err
+// can be queried recursively somehow.
+func whicherrs(q *Query) error {
+ lconf := loader.Config{Build: q.Build}
+
+ if err := setPTAScope(&lconf, q.Scope); err != nil {
+ return err
+ }
+
+ // Load/parse/type-check the program.
+ lprog, err := lconf.Load()
+ if err != nil {
+ return err
+ }
+ q.Fset = lprog.Fset
+
+ qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
+ if err != nil {
+ return err
+ }
+
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
+
+ ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
+ if err != nil {
+ return err
+ }
+
+ path, action := findInterestingNode(qpos.info, qpos.path)
+ if action != actionExpr {
+ return fmt.Errorf("whicherrs wants an expression; got %s",
+ astutil.NodeDescription(qpos.path[0]))
+ }
+ var expr ast.Expr
+ var obj types.Object
+ switch n := path[0].(type) {
+ case *ast.ValueSpec:
+ // ambiguous ValueSpec containing multiple names
+ return fmt.Errorf("multiple value specification")
+ case *ast.Ident:
+ obj = qpos.info.ObjectOf(n)
+ expr = n
+ case ast.Expr:
+ expr = n
+ default:
+ return fmt.Errorf("unexpected AST for expr: %T", n)
+ }
+
+ typ := qpos.info.TypeOf(expr)
+ if !types.Identical(typ, builtinErrorType) {
+ return fmt.Errorf("selection is not an expression of type 'error'")
+ }
+ // Determine the ssa.Value for the expression.
+ var value ssa.Value
+ if obj != nil {
+ // def/ref of func/var object
+ value, _, err = ssaValueForIdent(prog, qpos.info, obj, path)
+ } else {
+ value, _, err = ssaValueForExpr(prog, qpos.info, path)
+ }
+ if err != nil {
+ return err // e.g. trivially dead code
+ }
+
+ // Defer SSA construction till after errors are reported.
+ prog.Build()
+
+ globals := findVisibleErrs(prog, qpos)
+ constants := findVisibleConsts(prog, qpos)
+
+ res := &whicherrsResult{
+ qpos: qpos,
+ errpos: expr.Pos(),
+ }
+
+ // TODO(adonovan): the following code is heavily duplicated
+ // w.r.t. "pointsto". Refactor?
+
+ // Find the instruction which initialized the
+ // global error. If more than one instruction has stored to the global
+ // remove the global from the set of values that we want to query.
+ allFuncs := ssautil.AllFunctions(prog)
+ for fn := range allFuncs {
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ store, ok := instr.(*ssa.Store)
+ if !ok {
+ continue
+ }
+ gval, ok := store.Addr.(*ssa.Global)
+ if !ok {
+ continue
+ }
+ gbl, ok := globals[gval]
+ if !ok {
+ continue
+ }
+ // we already found a store to this global
+ // The normal error define is just one store in the init
+ // so we just remove this global from the set we want to query
+ if gbl != nil {
+ delete(globals, gval)
+ }
+ globals[gval] = store.Val
+ }
+ }
+ }
+
+ ptaConfig.AddQuery(value)
+ for _, v := range globals {
+ ptaConfig.AddQuery(v)
+ }
+
+ ptares := ptrAnalysis(ptaConfig)
+ valueptr := ptares.Queries[value]
+ for g, v := range globals {
+ ptr, ok := ptares.Queries[v]
+ if !ok {
+ continue
+ }
+ if !ptr.MayAlias(valueptr) {
+ continue
+ }
+ res.globals = append(res.globals, g)
+ }
+ pts := valueptr.PointsTo()
+ dedup := make(map[*ssa.NamedConst]bool)
+ for _, label := range pts.Labels() {
+ // These values are either MakeInterfaces or reflect
+ // generated interfaces. For the purposes of this
+ // analysis, we don't care about reflect generated ones
+ makeiface, ok := label.Value().(*ssa.MakeInterface)
+ if !ok {
+ continue
+ }
+ constval, ok := makeiface.X.(*ssa.Const)
+ if !ok {
+ continue
+ }
+ c := constants[*constval]
+ if c != nil && !dedup[c] {
+ dedup[c] = true
+ res.consts = append(res.consts, c)
+ }
+ }
+ concs := pts.DynamicTypes()
+ concs.Iterate(func(conc types.Type, _ interface{}) {
+ // go/types is a bit annoying here.
+ // We want to find all the types that we can
+ // typeswitch or assert to. This means finding out
+ // if the type pointed to can be seen by us.
+ //
+ // For the purposes of this analysis, the type is always
+ // either a Named type or a pointer to one.
+ // There are cases where error can be implemented
+ // by unnamed types, but in that case, we can't assert to
+ // it, so we don't care about it for this analysis.
+ var name *types.TypeName
+ switch t := conc.(type) {
+ case *types.Pointer:
+ named, ok := t.Elem().(*types.Named)
+ if !ok {
+ return
+ }
+ name = named.Obj()
+ case *types.Named:
+ name = t.Obj()
+ default:
+ return
+ }
+ if !isAccessibleFrom(name, qpos.info.Pkg) {
+ return
+ }
+ res.types = append(res.types, &errorType{conc, name})
+ })
+ sort.Sort(membersByPosAndString(res.globals))
+ sort.Sort(membersByPosAndString(res.consts))
+ sort.Sort(sorterrorType(res.types))
+
+ q.result = res
+ return nil
+}
+
+// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil.
+func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value {
+ globals := make(map[*ssa.Global]ssa.Value)
+ for _, pkg := range prog.AllPackages() {
+ for _, mem := range pkg.Members {
+ gbl, ok := mem.(*ssa.Global)
+ if !ok {
+ continue
+ }
+ gbltype := gbl.Type()
+ // globals are always pointers
+ if !types.Identical(deref(gbltype), builtinErrorType) {
+ continue
+ }
+ if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) {
+ continue
+ }
+ globals[gbl] = nil
+ }
+ }
+ return globals
+}
+
+// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
+func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst {
+ constants := make(map[ssa.Const]*ssa.NamedConst)
+ for _, pkg := range prog.AllPackages() {
+ for _, mem := range pkg.Members {
+ obj, ok := mem.(*ssa.NamedConst)
+ if !ok {
+ continue
+ }
+ consttype := obj.Type()
+ if !types.AssignableTo(consttype, builtinErrorType) {
+ continue
+ }
+ if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
+ continue
+ }
+ constants[*obj.Value] = obj
+ }
+ }
+
+ return constants
+}
+
+type membersByPosAndString []ssa.Member
+
+func (a membersByPosAndString) Len() int { return len(a) }
+func (a membersByPosAndString) Less(i, j int) bool {
+ cmp := a[i].Pos() - a[j].Pos()
+ return cmp < 0 || cmp == 0 && a[i].String() < a[j].String()
+}
+func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type sorterrorType []*errorType
+
+func (a sorterrorType) Len() int { return len(a) }
+func (a sorterrorType) Less(i, j int) bool {
+ cmp := a[i].obj.Pos() - a[j].obj.Pos()
+ return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String()
+}
+func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type errorType struct {
+ typ types.Type // concrete type N or *N that implements error
+ obj *types.TypeName // the named type N
+}
+
+type whicherrsResult struct {
+ qpos *queryPos
+ errpos token.Pos
+ globals []ssa.Member
+ consts []ssa.Member
+ types []*errorType
+}
+
+func (r *whicherrsResult) display(printf printfFunc) {
+ if len(r.globals) > 0 {
+ printf(r.qpos, "this error may point to these globals:")
+ for _, g := range r.globals {
+ printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg))
+ }
+ }
+ if len(r.consts) > 0 {
+ printf(r.qpos, "this error may contain these constants:")
+ for _, c := range r.consts {
+ printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg))
+ }
+ }
+ if len(r.types) > 0 {
+ printf(r.qpos, "this error may contain these dynamic types:")
+ for _, t := range r.types {
+ printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ))
+ }
+ }
+}
+
+func (r *whicherrsResult) toSerial(res *serial.Result, fset *token.FileSet) {
+ we := &serial.WhichErrs{}
+ we.ErrPos = fset.Position(r.errpos).String()
+ for _, g := range r.globals {
+ we.Globals = append(we.Globals, fset.Position(g.Pos()).String())
+ }
+ for _, c := range r.consts {
+ we.Constants = append(we.Constants, fset.Position(c.Pos()).String())
+ }
+ for _, t := range r.types {
+ var et serial.WhichErrsType
+ et.Type = r.qpos.typeString(t.typ)
+ et.Position = fset.Position(t.obj.Pos()).String()
+ we.Types = append(we.Types, et)
+ }
+ res.WhichErrs = we
+}
diff --git a/go/src/golang.org/x/tools/playground/appengine.go b/go/src/golang.org/x/tools/playground/appengine.go
index 073b419..94c5d52 100644
--- a/go/src/golang.org/x/tools/playground/appengine.go
+++ b/go/src/golang.org/x/tools/playground/appengine.go
@@ -13,6 +13,10 @@
"appengine/urlfetch"
)
+func init() {
+ onAppengine = !appengine.IsDevAppServer()
+}
+
func client(r *http.Request) *http.Client {
return urlfetch.Client(appengine.NewContext(r))
}
diff --git a/go/src/golang.org/x/tools/playground/appenginevm.go b/go/src/golang.org/x/tools/playground/appenginevm.go
new file mode 100644
index 0000000..aa3a212
--- /dev/null
+++ b/go/src/golang.org/x/tools/playground/appenginevm.go
@@ -0,0 +1,11 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appenginevm
+
+package playground
+
+func init() {
+ onAppengine = true
+}
diff --git a/go/src/golang.org/x/tools/playground/common.go b/go/src/golang.org/x/tools/playground/common.go
index 3ffce88..d8c2805 100644
--- a/go/src/golang.org/x/tools/playground/common.go
+++ b/go/src/golang.org/x/tools/playground/common.go
@@ -9,6 +9,7 @@
import (
"bytes"
+ "errors"
"fmt"
"io"
"net/http"
@@ -32,6 +33,9 @@
}
func passThru(w io.Writer, req *http.Request) error {
+ if req.URL.Path == "/share" && !allowShare(req) {
+ return errors.New("Forbidden")
+ }
defer req.Body.Close()
url := baseURL + req.URL.Path
r, err := client(req).Post(url, req.Header.Get("Content-type"), req.Body)
@@ -44,3 +48,16 @@
}
return nil
}
+
+var onAppengine = false // will be overriden by appengine.go and appenginevm.go
+
+func allowShare(r *http.Request) bool {
+ if !onAppengine {
+ return true
+ }
+ switch r.Header.Get("X-AppEngine-Country") {
+ case "", "ZZ", "CN":
+ return false
+ }
+ return true
+}
diff --git a/go/src/golang.org/x/tools/playground/socket/socket.go b/go/src/golang.org/x/tools/playground/socket/socket.go
index 76527ae..cec842e 100644
--- a/go/src/golang.org/x/tools/playground/socket/socket.go
+++ b/go/src/golang.org/x/tools/playground/socket/socket.go
@@ -31,7 +31,6 @@
"runtime"
"strconv"
"strings"
- "sync"
"time"
"unicode/utf8"
@@ -129,6 +128,7 @@
}
}
}()
+ defer close(out)
// Start and kill processes and handle errors.
proc := make(map[string]*process)
@@ -139,8 +139,7 @@
case "run":
log.Println("running snippet from:", c.Request().RemoteAddr)
proc[m.Id].Kill()
- lOut := limiter(in, out)
- proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options)
+ proc[m.Id] = startProcess(m.Id, m.Body, out, m.Options)
case "kill":
proc[m.Id].Kill()
}
@@ -160,7 +159,6 @@
// process represents a running process.
type process struct {
- id string
out chan<- *Message
done chan struct{} // closed when wait completes
run *exec.Cmd
@@ -169,12 +167,19 @@
// startProcess builds and runs the given program, sending its output
// and end event as Messages on the provided channel.
-func startProcess(id, body string, out chan<- *Message, opt *Options) *process {
- p := &process{
- id: id,
- out: out,
- done: make(chan struct{}),
- }
+func startProcess(id, body string, dest chan<- *Message, opt *Options) *process {
+ var (
+ done = make(chan struct{})
+ out = make(chan *Message)
+ p = &process{out: out, done: done}
+ )
+ go func() {
+ defer close(done)
+ for m := range buffer(limiter(out, p)) {
+ m.Id = id
+ dest <- m
+ }
+ }()
var err error
if path, args := shebang(body); path != "" {
if RunScripts {
@@ -189,13 +194,122 @@
p.end(err)
return nil
}
- go p.wait()
+ go func() {
+ p.end(p.run.Wait())
+ }()
return p
}
+// end sends an "end" message to the client, containing the process id and the
+// given error value. It also removes the binary, if present.
+func (p *process) end(err error) {
+ if p.bin != "" {
+ defer os.Remove(p.bin)
+ }
+ m := &Message{Kind: "end"}
+ if err != nil {
+ m.Body = err.Error()
+ }
+ p.out <- m
+ close(p.out)
+}
+
+// A killer provides a mechanism to terminate a process.
+// The Kill method returns only once the process has exited.
+type killer interface {
+ Kill()
+}
+
+// limiter returns a channel that wraps the given channel.
+// It receives Messages from the given channel and sends them to the returned
+// channel until it passes msgLimit messages, at which point it will kill the
+// process and pass only the "end" message.
+// When the given channel is closed, or when the "end" message is received,
+// it closes the returned channel.
+func limiter(in <-chan *Message, p killer) <-chan *Message {
+ out := make(chan *Message)
+ go func() {
+ defer close(out)
+ n := 0
+ for m := range in {
+ switch {
+ case n < msgLimit || m.Kind == "end":
+ out <- m
+ if m.Kind == "end" {
+ return
+ }
+ case n == msgLimit:
+ // Kill in a goroutine as Kill will not return
+ // until the process' output has been
+ // processed, and we're doing that in this loop.
+ go p.Kill()
+ default:
+ continue // don't increment
+ }
+ n++
+ }
+ }()
+ return out
+}
+
+// buffer returns a channel that wraps the given channel. It receives messages
+// from the given channel and sends them to the returned channel.
+// Message bodies are gathered over the period msgDelay and coalesced into a
+// single Message before they are passed on. Messages of the same kind are
+// coalesced; when a message of a different kind is received, any buffered
+// messages are flushed. When the given channel is closed, buffer flushes the
+// remaining buffered messages and closes the returned channel.
+func buffer(in <-chan *Message) <-chan *Message {
+ out := make(chan *Message)
+ go func() {
+ defer close(out)
+ var (
+ t = time.NewTimer(msgDelay)
+ tc <-chan time.Time
+ buf []byte
+ kind string
+ flush = func() {
+ if len(buf) == 0 {
+ return
+ }
+ out <- &Message{Kind: kind, Body: safeString(buf)}
+ buf = buf[:0] // recycle buffer
+ kind = ""
+ }
+ )
+ for {
+ select {
+ case m, ok := <-in:
+ if !ok {
+ flush()
+ return
+ }
+ if m.Kind == "end" {
+ flush()
+ out <- m
+ return
+ }
+ if kind != m.Kind {
+ flush()
+ kind = m.Kind
+ if tc == nil {
+ tc = t.C
+ t.Reset(msgDelay)
+ }
+ }
+ buf = append(buf, m.Body...)
+ case <-tc:
+ flush()
+ tc = nil
+ }
+ }
+ }()
+ return out
+}
+
// Kill stops the process if it is running and waits for it to exit.
func (p *process) Kill() {
- if p == nil {
+ if p == nil || p.run == nil {
return
}
p.run.Process.Kill()
@@ -224,8 +338,8 @@
Path: path,
Args: args,
Stdin: strings.NewReader(body),
- Stdout: &messageWriter{id: p.id, kind: "stdout", out: p.out},
- Stderr: &messageWriter{id: p.id, kind: "stderr", out: p.out},
+ Stdout: &messageWriter{kind: "stdout", out: p.out},
+ Stderr: &messageWriter{kind: "stderr", out: p.out},
}
if err := cmd.Start(); err != nil {
return err
@@ -261,7 +375,7 @@
args := []string{"go", "build", "-tags", "OMIT"}
if opt != nil && opt.Race {
p.out <- &Message{
- Id: p.id, Kind: "stderr",
+ Kind: "stderr",
Body: "Running with race detector.\n",
}
args = append(args, "-race")
@@ -298,35 +412,14 @@
return nil
}
-// wait waits for the running process to complete
-// and sends its error state to the client.
-func (p *process) wait() {
- p.end(p.run.Wait())
- close(p.done) // unblock waiting Kill calls
-}
-
-// end sends an "end" message to the client, containing the process id and the
-// given error value. It also removes the binary.
-func (p *process) end(err error) {
- if p.bin != "" {
- defer os.Remove(p.bin)
- }
- m := &Message{Id: p.id, Kind: "end"}
- if err != nil {
- m.Body = err.Error()
- }
- // Wait for any outstanding reads to finish (potential race here).
- time.AfterFunc(4*msgDelay, func() { p.out <- m })
-}
-
// cmd builds an *exec.Cmd that writes its standard output and error to the
// process' output channel.
func (p *process) cmd(dir string, args ...string) *exec.Cmd {
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = dir
cmd.Env = Environ()
- cmd.Stdout = &messageWriter{id: p.id, kind: "stdout", out: p.out}
- cmd.Stderr = &messageWriter{id: p.id, kind: "stderr", out: p.out}
+ cmd.Stdout = &messageWriter{kind: "stdout", out: p.out}
+ cmd.Stderr = &messageWriter{kind: "stderr", out: p.out}
return cmd
}
@@ -387,33 +480,15 @@
// messageWriter is an io.Writer that converts all writes to Message sends on
// the out channel with the specified id and kind.
type messageWriter struct {
- id, kind string
- out chan<- *Message
-
- mu sync.Mutex
- buf []byte
- send *time.Timer
+ kind string
+ out chan<- *Message
}
func (w *messageWriter) Write(b []byte) (n int, err error) {
- // Buffer writes that occur in a short period to send as one Message.
- w.mu.Lock()
- w.buf = append(w.buf, b...)
- if w.send == nil {
- w.send = time.AfterFunc(msgDelay, w.sendNow)
- }
- w.mu.Unlock()
+ w.out <- &Message{Kind: w.kind, Body: safeString(b)}
return len(b), nil
}
-func (w *messageWriter) sendNow() {
- w.mu.Lock()
- body := safeString(w.buf)
- w.buf, w.send = nil, nil
- w.mu.Unlock()
- w.out <- &Message{Id: w.id, Kind: w.kind, Body: body}
-}
-
// safeString returns b as a valid UTF-8 string.
func safeString(b []byte) string {
if utf8.Valid(b) {
@@ -428,30 +503,6 @@
return buf.String()
}
-// limiter returns a channel that wraps dest. Messages sent to the channel are
-// sent to dest. After msgLimit Messages have been passed on, a "kill" Message
-// is sent to the kill channel, and only "end" messages are passed.
-func limiter(kill chan<- *Message, dest chan<- *Message) chan<- *Message {
- ch := make(chan *Message)
- go func() {
- n := 0
- for m := range ch {
- switch {
- case n < msgLimit || m.Kind == "end":
- dest <- m
- if m.Kind == "end" {
- return
- }
- case n == msgLimit:
- // process produced too much output. Kill it.
- kill <- &Message{Id: m.Id, Kind: "kill"}
- }
- n++
- }
- }()
- return ch
-}
-
var tmpdir string
func init() {
diff --git a/go/src/golang.org/x/tools/playground/socket/socket_test.go b/go/src/golang.org/x/tools/playground/socket/socket_test.go
new file mode 100644
index 0000000..5dd2815
--- /dev/null
+++ b/go/src/golang.org/x/tools/playground/socket/socket_test.go
@@ -0,0 +1,73 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package socket
+
+import (
+ "testing"
+ "time"
+)
+
+func TestBuffer(t *testing.T) {
+ ch := make(chan *Message)
+ go func() {
+ ch <- &Message{Kind: "err", Body: "a"}
+ ch <- &Message{Kind: "err", Body: "b"}
+ ch <- &Message{Kind: "out", Body: "1"}
+ ch <- &Message{Kind: "out", Body: "2"}
+ time.Sleep(msgDelay * 2)
+ ch <- &Message{Kind: "out", Body: "3"}
+ ch <- &Message{Kind: "out", Body: "4"}
+ close(ch)
+ }()
+
+ var ms []*Message
+ for m := range buffer(ch) {
+ ms = append(ms, m)
+ }
+ if len(ms) != 3 {
+ t.Fatalf("got %v messages, want 2", len(ms))
+ }
+ if g, w := ms[0].Body, "ab"; g != w {
+ t.Errorf("message 0 body = %q, want %q", g, w)
+ }
+ if g, w := ms[1].Body, "12"; g != w {
+ t.Errorf("message 1 body = %q, want %q", g, w)
+ }
+ if g, w := ms[2].Body, "34"; g != w {
+ t.Errorf("message 2 body = %q, want %q", g, w)
+ }
+}
+
+type killRecorder chan struct{}
+
+func (k killRecorder) Kill() { close(k) }
+
+func TestLimiter(t *testing.T) {
+ ch := make(chan *Message)
+ go func() {
+ var m Message
+ for i := 0; i < msgLimit+10; i++ {
+ ch <- &m
+ }
+ ch <- &Message{Kind: "end"}
+ }()
+
+ kr := make(killRecorder)
+ n := 0
+ for m := range limiter(ch, kr) {
+ n++
+ if n > msgLimit && m.Kind != "end" {
+ t.Errorf("received non-end message after limit")
+ }
+ }
+ if n != msgLimit+1 {
+ t.Errorf("received %v messages, want %v", n, msgLimit+1)
+ }
+ select {
+ case <-kr:
+ case <-time.After(100 * time.Millisecond):
+ t.Errorf("process wasn't killed after reaching limit")
+ }
+}
diff --git a/go/src/golang.org/x/tools/present/background.go b/go/src/golang.org/x/tools/present/background.go
new file mode 100644
index 0000000..0a6216a
--- /dev/null
+++ b/go/src/golang.org/x/tools/present/background.go
@@ -0,0 +1,25 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "strings"
+)
+
+func init() {
+ Register("background", parseBackground)
+}
+
+type Background struct {
+ URL string
+}
+
+func (i Background) TemplateName() string { return "background" }
+
+func parseBackground(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ background := Background{URL: args[1]}
+ return background, nil
+}
diff --git a/go/src/golang.org/x/tools/present/doc.go b/go/src/golang.org/x/tools/present/doc.go
index 584e0c1..351c581 100644
--- a/go/src/golang.org/x/tools/present/doc.go
+++ b/go/src/golang.org/x/tools/present/doc.go
@@ -58,6 +58,7 @@
.code x.go /^func main/,/^}/
.play y.go
.image image.jpg
+ .background image.jpg
.iframe http://foo
.link http://foo label
.html file.html
@@ -177,6 +178,28 @@
.image images/janet.jpg _ 300
+video:
+
+The template uses the function "video" to inject video files.
+
+The syntax is simple: 2 or 4 space-separated arguments.
+The first argument is always the file name.
+The second argument is always the file content-type.
+If there are more arguments, they are the height and width;
+both must be present, or substituted with an underscore.
+Replacing a dimension argument with the underscore parameter
+preserves the aspect ratio of the video when scaling.
+
+ .video videos/evangeline.mp4 video/mp4 400 600
+
+ .video videos/mabel.ogg video/ogg 500 _
+
+background:
+
+The template uses the function "background" to set the background image for
+a slide. The only argument is the file name of the image.
+
+ .background images/susan.jpg
caption:
diff --git a/go/src/golang.org/x/tools/present/parse.go b/go/src/golang.org/x/tools/present/parse.go
index 449d5ed..034a83c 100644
--- a/go/src/golang.org/x/tools/present/parse.go
+++ b/go/src/golang.org/x/tools/present/parse.go
@@ -362,6 +362,7 @@
var l []string
for ok && strings.TrimSpace(text) != "" {
if text[0] == '.' { // Command breaks text block.
+ lines.back()
break
}
if strings.HasPrefix(text, `\.`) { // Backslash escapes initial period.
diff --git a/go/src/golang.org/x/tools/present/video.go b/go/src/golang.org/x/tools/present/video.go
new file mode 100644
index 0000000..913822e
--- /dev/null
+++ b/go/src/golang.org/x/tools/present/video.go
@@ -0,0 +1,51 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package present
+
+import (
+ "fmt"
+ "strings"
+)
+
+func init() {
+ Register("video", parseVideo)
+}
+
+type Video struct {
+ URL string
+ SourceType string
+ Width int
+ Height int
+}
+
+func (v Video) TemplateName() string { return "video" }
+
+func parseVideo(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ vid := Video{URL: args[1], SourceType: args[2]}
+ a, err := parseArgs(fileName, lineno, args[3:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ // If a parameter is empty (underscore) or invalid
+ // leave the field set to zero. The "video" action
+ // template will then omit that vid tag attribute and
+ // the browser will calculate the value to preserve
+ // the aspect ratio.
+ if v, ok := a[0].(int); ok {
+ vid.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ vid.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect video invocation: %q", text)
+ }
+ return vid, nil
+}
diff --git a/go/src/golang.org/x/tools/refactor/eg/eg.go b/go/src/golang.org/x/tools/refactor/eg/eg.go
index a609255..4d56824 100644
--- a/go/src/golang.org/x/tools/refactor/eg/eg.go
+++ b/go/src/golang.org/x/tools/refactor/eg/eg.go
@@ -1,3 +1,9 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
// Package eg implements the example-based refactoring tool whose
// command-line is defined in golang.org/x/tools/cmd/eg.
package eg // import "golang.org/x/tools/refactor/eg"
@@ -9,10 +15,8 @@
"go/format"
"go/printer"
"go/token"
+ "go/types"
"os"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
)
const Help = `
@@ -131,19 +135,13 @@
match f'; (4) use eg to rename f' to f in all calls; (5) delete f'.
`
-// TODO(adonovan): allow the tool to be invoked using relative package
-// directory names (./foo). Requires changes to go/loader.
-
// TODO(adonovan): expand upon the above documentation as an HTML page.
-// TODO(adonovan): eliminate dependency on loader.PackageInfo.
-// Move its TypeOf method into go/types.
-
// A Transformer represents a single example-based transformation.
type Transformer struct {
fset *token.FileSet
verbose bool
- info loader.PackageInfo // combined type info for template/input/output ASTs
+ info *types.Info // combined type info for template/input/output ASTs
seenInfos map[*types.Info]bool
wildcards map[*types.Var]bool // set of parameters in func before()
env map[string]ast.Expr // maps parameter name to wildcard binding
@@ -157,16 +155,17 @@
}
// NewTransformer returns a transformer based on the specified template,
-// a package containing "before" and "after" functions as described
-// in the package documentation.
+// a single-file package containing "before" and "after" functions as
+// described in the package documentation.
+// tmplInfo is the type information for tmplFile.
//
-func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose bool) (*Transformer, error) {
+func NewTransformer(fset *token.FileSet, tmplPkg *types.Package, tmplFile *ast.File, tmplInfo *types.Info, verbose bool) (*Transformer, error) {
// Check the template.
- beforeSig := funcSig(template.Pkg, "before")
+ beforeSig := funcSig(tmplPkg, "before")
if beforeSig == nil {
return nil, fmt.Errorf("no 'before' func found in template")
}
- afterSig := funcSig(template.Pkg, "after")
+ afterSig := funcSig(tmplPkg, "after")
if afterSig == nil {
return nil, fmt.Errorf("no 'after' func found in template")
}
@@ -177,18 +176,17 @@
beforeSig, afterSig)
}
- templateFile := template.Files[0]
- for _, imp := range templateFile.Imports {
+ for _, imp := range tmplFile.Imports {
if imp.Name != nil && imp.Name.Name == "." {
// Dot imports are currently forbidden. We
// make the simplifying assumption that all
// imports are regular, without local renames.
- //TODO document
+ // TODO(adonovan): document
return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
}
}
var beforeDecl, afterDecl *ast.FuncDecl
- for _, decl := range templateFile.Decls {
+ for _, decl := range tmplFile.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
switch decl.Name.Name {
case "before":
@@ -228,8 +226,8 @@
// of the replacement. (Consider the rule that array literal keys
// must be unique.) So we cannot hope to prove the safety of a
// transformation in general.
- Tb := template.TypeOf(before)
- Ta := template.TypeOf(after)
+ Tb := tmplInfo.TypeOf(before)
+ Ta := tmplInfo.TypeOf(after)
if types.AssignableTo(Tb, Ta) {
// safe: replacement is assignable to pattern.
} else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
@@ -253,19 +251,16 @@
// type info for the synthesized ASTs too. This saves us
// having to book-keep where each ast.Node originated as we
// construct the resulting hybrid AST.
- //
- // TODO(adonovan): move type utility methods of PackageInfo to
- // types.Info, or at least into go/types.typeutil.
- tr.info.Info = types.Info{
+ tr.info = &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
- mergeTypeInfo(&tr.info.Info, &template.Info)
+ mergeTypeInfo(tr.info, tmplInfo)
// Compute set of imported objects required by after().
- // TODO reject dot-imports in pattern
+ // TODO(adonovan): reject dot-imports in pattern
ast.Inspect(after, func(n ast.Node) bool {
if n, ok := n.(*ast.SelectorExpr); ok {
if _, ok := tr.info.Selections[n]; !ok {
diff --git a/go/src/golang.org/x/tools/refactor/eg/eg14.go b/go/src/golang.org/x/tools/refactor/eg/eg14.go
new file mode 100644
index 0000000..d6790fe
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/eg/eg14.go
@@ -0,0 +1,347 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package eg implements the example-based refactoring tool whose
+// command-line is defined in golang.org/x/tools/cmd/eg.
+package eg // import "golang.org/x/tools/refactor/eg"
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/printer"
+ "go/token"
+ "os"
+
+ "golang.org/x/tools/go/types"
+)
+
+const Help = `
+This tool implements example-based refactoring of expressions.
+
+The transformation is specified as a Go file defining two functions,
+'before' and 'after', of identical types. Each function body consists
+of a single statement: either a return statement with a single
+(possibly multi-valued) expression, or an expression statement. The
+'before' expression specifies a pattern and the 'after' expression its
+replacement.
+
+ package P
+ import ( "errors"; "fmt" )
+ func before(s string) error { return fmt.Errorf("%s", s) }
+ func after(s string) error { return errors.New(s) }
+
+The expression statement form is useful when the expression has no
+result, for example:
+
+ func before(msg string) { log.Fatalf("%s", msg) }
+ func after(msg string) { log.Fatal(msg) }
+
+The parameters of both functions are wildcards that may match any
+expression assignable to that type. If the pattern contains multiple
+occurrences of the same parameter, each must match the same expression
+in the input for the pattern to match. If the replacement contains
+multiple occurrences of the same parameter, the expression will be
+duplicated, possibly changing the side-effects.
+
+The tool analyses all Go code in the packages specified by the
+arguments, replacing all occurrences of the pattern with the
+substitution.
+
+So, the transform above would change this input:
+ err := fmt.Errorf("%s", "error: " + msg)
+to this output:
+ err := errors.New("error: " + msg)
+
+Identifiers, including qualified identifiers (p.X) are considered to
+match only if they denote the same object. This allows correct
+matching even in the presence of dot imports, named imports and
+locally shadowed package names in the input program.
+
+Matching of type syntax is semantic, not syntactic: type syntax in the
+pattern matches type syntax in the input if the types are identical.
+Thus, func(x int) matches func(y int).
+
+This tool was inspired by other example-based refactoring tools,
+'gofmt -r' for Go and Refaster for Java.
+
+
+LIMITATIONS
+===========
+
+EXPRESSIVENESS
+
+Only refactorings that replace one expression with another, regardless
+of the expression's context, may be expressed. Refactoring arbitrary
+statements (or sequences of statements) is a less well-defined problem
+and is less amenable to this approach.
+
+A pattern that contains a function literal (and hence statements)
+never matches.
+
+There is no way to generalize over related types, e.g. to express that
+a wildcard may have any integer type, for example.
+
+It is not possible to replace an expression by one of a different
+type, even in contexts where this is legal, such as x in fmt.Print(x).
+
+The struct literals T{x} and T{K: x} cannot both be matched by a single
+template.
+
+
+SAFETY
+
+Verifying that a transformation does not introduce type errors is very
+complex in the general case. An innocuous-looking replacement of one
+constant by another (e.g. 1 to 2) may cause type errors relating to
+array types and indices, for example. The tool performs only very
+superficial checks of type preservation.
+
+
+IMPORTS
+
+Although the matching algorithm is fully aware of scoping rules, the
+replacement algorithm is not, so the replacement code may contain
+incorrect identifier syntax for imported objects if there are dot
+imports, named imports or locally shadowed package names in the input
+program.
+
+Imports are added as needed, but they are not removed as needed.
+Run 'goimports' on the modified file for now.
+
+Dot imports are forbidden in the template.
+
+
+TIPS
+====
+
+Sometimes a little creativity is required to implement the desired
+migration. This section lists a few tips and tricks.
+
+To remove the final parameter from a function, temporarily change the
+function signature so that the final parameter is variadic, as this
+allows legal calls both with and without the argument. Then use eg to
+remove the final argument from all callers, and remove the variadic
+parameter by hand. The reverse process can be used to add a final
+parameter.
+
+To add or remove parameters other than the final one, you must do it in
+stages: (1) declare a variant function f' with a different name and the
+desired parameters; (2) use eg to transform calls to f into calls to f',
+changing the arguments as needed; (3) change the declaration of f to
+match f'; (4) use eg to rename f' to f in all calls; (5) delete f'.
+`
+
+// TODO(adonovan): expand upon the above documentation as an HTML page.
+
+// A Transformer represents a single example-based transformation.
+type Transformer struct {
+ fset *token.FileSet
+ verbose bool
+ info *types.Info // combined type info for template/input/output ASTs
+ seenInfos map[*types.Info]bool
+ wildcards map[*types.Var]bool // set of parameters in func before()
+ env map[string]ast.Expr // maps parameter name to wildcard binding
+ importedObjs map[types.Object]*ast.SelectorExpr // objects imported by after().
+ before, after ast.Expr
+ allowWildcards bool
+
+ // Working state of Transform():
+ nsubsts int // number of substitutions made
+ currentPkg *types.Package // package of current call
+}
+
+// NewTransformer returns a transformer based on the specified template,
+// a single-file package containing "before" and "after" functions as
+// described in the package documentation.
+// tmplInfo is the type information for tmplFile.
+//
+func NewTransformer(fset *token.FileSet, tmplPkg *types.Package, tmplFile *ast.File, tmplInfo *types.Info, verbose bool) (*Transformer, error) {
+ // Check the template.
+ beforeSig := funcSig(tmplPkg, "before")
+ if beforeSig == nil {
+ return nil, fmt.Errorf("no 'before' func found in template")
+ }
+ afterSig := funcSig(tmplPkg, "after")
+ if afterSig == nil {
+ return nil, fmt.Errorf("no 'after' func found in template")
+ }
+
+ // TODO(adonovan): should we also check the names of the params match?
+ if !types.Identical(afterSig, beforeSig) {
+ return nil, fmt.Errorf("before %s and after %s functions have different signatures",
+ beforeSig, afterSig)
+ }
+
+ for _, imp := range tmplFile.Imports {
+ if imp.Name != nil && imp.Name.Name == "." {
+ // Dot imports are currently forbidden. We
+ // make the simplifying assumption that all
+ // imports are regular, without local renames.
+ // TODO(adonovan): document
+ return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
+ }
+ }
+ var beforeDecl, afterDecl *ast.FuncDecl
+ for _, decl := range tmplFile.Decls {
+ if decl, ok := decl.(*ast.FuncDecl); ok {
+ switch decl.Name.Name {
+ case "before":
+ beforeDecl = decl
+ case "after":
+ afterDecl = decl
+ }
+ }
+ }
+
+ before, err := soleExpr(beforeDecl)
+ if err != nil {
+ return nil, fmt.Errorf("before: %s", err)
+ }
+ after, err := soleExpr(afterDecl)
+ if err != nil {
+ return nil, fmt.Errorf("after: %s", err)
+ }
+
+ wildcards := make(map[*types.Var]bool)
+ for i := 0; i < beforeSig.Params().Len(); i++ {
+ wildcards[beforeSig.Params().At(i)] = true
+ }
+
+ // checkExprTypes returns an error if Tb (type of before()) is not
+ // safe to replace with Ta (type of after()).
+ //
+ // Only superficial checks are performed, and they may result in both
+ // false positives and negatives.
+ //
+ // Ideally, we would only require that the replacement be assignable
+ // to the context of a specific pattern occurrence, but the type
+ // checker doesn't record that information and it's complex to deduce.
+ // A Go type cannot capture all the constraints of a given expression
+ // context, which may include the size, constness, signedness,
+ // namedness or constructor of its type, and even the specific value
+ // of the replacement. (Consider the rule that array literal keys
+ // must be unique.) So we cannot hope to prove the safety of a
+ // transformation in general.
+ Tb := tmplInfo.TypeOf(before)
+ Ta := tmplInfo.TypeOf(after)
+ if types.AssignableTo(Tb, Ta) {
+ // safe: replacement is assignable to pattern.
+ } else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
+ // safe: pattern has void type (must appear in an ExprStmt).
+ } else {
+ return nil, fmt.Errorf("%s is not a safe replacement for %s", Ta, Tb)
+ }
+
+ tr := &Transformer{
+ fset: fset,
+ verbose: verbose,
+ wildcards: wildcards,
+ allowWildcards: true,
+ seenInfos: make(map[*types.Info]bool),
+ importedObjs: make(map[types.Object]*ast.SelectorExpr),
+ before: before,
+ after: after,
+ }
+
+ // Combine type info from the template and input packages, and
+ // type info for the synthesized ASTs too. This saves us
+ // having to book-keep where each ast.Node originated as we
+ // construct the resulting hybrid AST.
+ tr.info = &types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ Selections: make(map[*ast.SelectorExpr]*types.Selection),
+ }
+ mergeTypeInfo(tr.info, tmplInfo)
+
+ // Compute set of imported objects required by after().
+ // TODO(adonovan): reject dot-imports in pattern
+ ast.Inspect(after, func(n ast.Node) bool {
+ if n, ok := n.(*ast.SelectorExpr); ok {
+ if _, ok := tr.info.Selections[n]; !ok {
+ // qualified ident
+ obj := tr.info.Uses[n.Sel]
+ tr.importedObjs[obj] = n
+ return false // prune
+ }
+ }
+ return true // recur
+ })
+
+ return tr, nil
+}
+
+// WriteAST is a convenience function that writes AST f to the specified file.
+func WriteAST(fset *token.FileSet, filename string, f *ast.File) (err error) {
+ fh, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err2 := fh.Close(); err != nil {
+ err = err2 // prefer earlier error
+ }
+ }()
+ return format.Node(fh, fset, f)
+}
+
+// -- utilities --------------------------------------------------------
+
+// funcSig returns the signature of the specified package-level function.
+func funcSig(pkg *types.Package, name string) *types.Signature {
+ if f, ok := pkg.Scope().Lookup(name).(*types.Func); ok {
+ return f.Type().(*types.Signature)
+ }
+ return nil
+}
+
+// soleExpr returns the sole expression in the before/after template function.
+func soleExpr(fn *ast.FuncDecl) (ast.Expr, error) {
+ if fn.Body == nil {
+ return nil, fmt.Errorf("no body")
+ }
+ if len(fn.Body.List) != 1 {
+ return nil, fmt.Errorf("must contain a single statement")
+ }
+ switch stmt := fn.Body.List[0].(type) {
+ case *ast.ReturnStmt:
+ if len(stmt.Results) != 1 {
+ return nil, fmt.Errorf("return statement must have a single operand")
+ }
+ return stmt.Results[0], nil
+
+ case *ast.ExprStmt:
+ return stmt.X, nil
+ }
+
+ return nil, fmt.Errorf("must contain a single return or expression statement")
+}
+
+// mergeTypeInfo adds type info from src to dst.
+func mergeTypeInfo(dst, src *types.Info) {
+ for k, v := range src.Types {
+ dst.Types[k] = v
+ }
+ for k, v := range src.Defs {
+ dst.Defs[k] = v
+ }
+ for k, v := range src.Uses {
+ dst.Uses[k] = v
+ }
+ for k, v := range src.Selections {
+ dst.Selections[k] = v
+ }
+}
+
+// (debugging only)
+func astString(fset *token.FileSet, n ast.Node) string {
+ var buf bytes.Buffer
+ printer.Fprint(&buf, fset, n)
+ return buf.String()
+}
diff --git a/go/src/golang.org/x/tools/refactor/eg/eg14_test.go b/go/src/golang.org/x/tools/refactor/eg/eg14_test.go
new file mode 100644
index 0000000..814383e
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/eg/eg14_test.go
@@ -0,0 +1,162 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// No testdata on Android.
+
+// +build !android
+
+package eg_test
+
+import (
+ "bytes"
+ "flag"
+ "go/parser"
+ "go/token"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/refactor/eg"
+)
+
+// TODO(adonovan): more tests:
+// - of command-line tool
+// - of all parts of syntax
+// - of applying a template to a package it imports:
+// the replacement syntax should use unqualified names for its objects.
+
+var (
+ updateFlag = flag.Bool("update", false, "update the golden files")
+ verboseFlag = flag.Bool("verbose", false, "show matcher information")
+)
+
+func Test(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows":
+ t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
+ }
+
+ conf := loader.Config{
+ Fset: token.NewFileSet(),
+ ParserMode: parser.ParseComments,
+ }
+
+ // Each entry is a single-file package.
+ // (Multi-file packages aren't interesting for this test.)
+ // Order matters: each non-template package is processed using
+ // the preceding template package.
+ for _, filename := range []string{
+ "testdata/A.template",
+ "testdata/A1.go",
+ "testdata/A2.go",
+
+ "testdata/B.template",
+ "testdata/B1.go",
+
+ "testdata/C.template",
+ "testdata/C1.go",
+
+ "testdata/D.template",
+ "testdata/D1.go",
+
+ "testdata/E.template",
+ "testdata/E1.go",
+
+ "testdata/F.template",
+ "testdata/F1.go",
+
+ "testdata/G.template",
+ "testdata/G1.go",
+
+ "testdata/H.template",
+ "testdata/H1.go",
+
+ "testdata/bad_type.template",
+ "testdata/no_before.template",
+ "testdata/no_after_return.template",
+ "testdata/type_mismatch.template",
+ "testdata/expr_type_mismatch.template",
+ } {
+ pkgname := strings.TrimSuffix(filepath.Base(filename), ".go")
+ conf.CreateFromFilenames(pkgname, filename)
+ }
+ iprog, err := conf.Load()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var xform *eg.Transformer
+ for _, info := range iprog.Created {
+ file := info.Files[0]
+ filename := iprog.Fset.File(file.Pos()).Name() // foo.go
+
+ if strings.HasSuffix(filename, "template") {
+ // a new template
+ shouldFail, _ := info.Pkg.Scope().Lookup("shouldFail").(*types.Const)
+ xform, err = eg.NewTransformer(iprog.Fset, info.Pkg, file, &info.Info, *verboseFlag)
+ if err != nil {
+ if shouldFail == nil {
+ t.Errorf("NewTransformer(%s): %s", filename, err)
+ } else if want := exact.StringVal(shouldFail.Val()); !strings.Contains(err.Error(), want) {
+ t.Errorf("NewTransformer(%s): got error %q, want error %q", filename, err, want)
+ }
+ } else if shouldFail != nil {
+ t.Errorf("NewTransformer(%s) succeeded unexpectedly; want error %q",
+ filename, shouldFail.Val())
+ }
+ continue
+ }
+
+ if xform == nil {
+ t.Errorf("%s: no previous template", filename)
+ continue
+ }
+
+ // apply previous template to this package
+ n := xform.Transform(&info.Info, info.Pkg, file)
+ if n == 0 {
+ t.Errorf("%s: no matches", filename)
+ continue
+ }
+
+ got := filename + "t" // foo.got
+ golden := filename + "lden" // foo.golden
+
+ // Write actual output to foo.got.
+ if err := eg.WriteAST(iprog.Fset, got, file); err != nil {
+ t.Error(err)
+ }
+ defer os.Remove(got)
+
+ // Compare foo.got with foo.golden.
+ var cmd *exec.Cmd
+ switch runtime.GOOS {
+ case "plan9":
+ cmd = exec.Command("/bin/diff", "-c", golden, got)
+ default:
+ cmd = exec.Command("/usr/bin/diff", "-u", golden, got)
+ }
+ buf := new(bytes.Buffer)
+ cmd.Stdout = buf
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ t.Errorf("eg tests for %s failed: %s.\n%s\n", filename, err, buf)
+
+ if *updateFlag {
+ t.Logf("Updating %s...", golden)
+ if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
+ t.Errorf("Update failed: %s", err)
+ }
+ }
+ }
+ }
+}
diff --git a/go/src/golang.org/x/tools/refactor/eg/eg_test.go b/go/src/golang.org/x/tools/refactor/eg/eg_test.go
index 295e842..c259920 100644
--- a/go/src/golang.org/x/tools/refactor/eg/eg_test.go
+++ b/go/src/golang.org/x/tools/refactor/eg/eg_test.go
@@ -1,7 +1,9 @@
-// Copyright 2015 The Go Authors. All rights reserved.
+// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// No testdata on Android.
// +build !android
@@ -11,8 +13,10 @@
import (
"bytes"
"flag"
+ exact "go/constant"
"go/parser"
"go/token"
+ "go/types"
"os"
"os/exec"
"path/filepath"
@@ -20,9 +24,7 @@
"strings"
"testing"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/refactor/eg"
)
@@ -100,7 +102,7 @@
if strings.HasSuffix(filename, "template") {
// a new template
shouldFail, _ := info.Pkg.Scope().Lookup("shouldFail").(*types.Const)
- xform, err = eg.NewTransformer(iprog.Fset, info, *verboseFlag)
+ xform, err = eg.NewTransformer(iprog.Fset, info.Pkg, file, &info.Info, *verboseFlag)
if err != nil {
if shouldFail == nil {
t.Errorf("NewTransformer(%s): %s", filename, err)
diff --git a/go/src/golang.org/x/tools/refactor/eg/match.go b/go/src/golang.org/x/tools/refactor/eg/match.go
index d8590d4..8d989bc 100644
--- a/go/src/golang.org/x/tools/refactor/eg/match.go
+++ b/go/src/golang.org/x/tools/refactor/eg/match.go
@@ -1,17 +1,22 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
package eg
import (
"fmt"
"go/ast"
"go/token"
+ "go/types"
"log"
"os"
"reflect"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
)
// matchExpr reports whether pattern x matches y.
@@ -42,8 +47,8 @@
// Object identifiers (including pkg-qualified ones)
// are handled semantically, not syntactically.
- xobj := isRef(x, &tr.info)
- yobj := isRef(y, &tr.info)
+ xobj := isRef(x, tr.info)
+ yobj := isRef(y, tr.info)
if xobj != nil {
return xobj == yobj
}
@@ -231,7 +236,7 @@
// isRef returns the object referred to by this (possibly qualified)
// identifier, or nil if the node is not a referring identifier.
-func isRef(n ast.Node, info *loader.PackageInfo) types.Object {
+func isRef(n ast.Node, info *types.Info) types.Object {
switch n := n.(type) {
case *ast.Ident:
return info.Uses[n]
diff --git a/go/src/golang.org/x/tools/refactor/eg/match14.go b/go/src/golang.org/x/tools/refactor/eg/match14.go
new file mode 100644
index 0000000..10b84ab
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/eg/match14.go
@@ -0,0 +1,251 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package eg
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "log"
+ "os"
+ "reflect"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+)
+
+// matchExpr reports whether pattern x matches y.
+//
+// If tr.allowWildcards, Idents in x that refer to parameters are
+// treated as wildcards, and match any y that is assignable to the
+// parameter type; matchExpr records this correspondence in tr.env.
+// Otherwise, matchExpr simply reports whether the two trees are
+// equivalent.
+//
+// A wildcard appearing more than once in the pattern must
+// consistently match the same tree.
+//
+func (tr *Transformer) matchExpr(x, y ast.Expr) bool {
+ if x == nil && y == nil {
+ return true
+ }
+ if x == nil || y == nil {
+ return false
+ }
+ x = unparen(x)
+ y = unparen(y)
+
+ // Is x a wildcard? (a reference to a 'before' parameter)
+ if xobj, ok := tr.wildcardObj(x); ok {
+ return tr.matchWildcard(xobj, y)
+ }
+
+ // Object identifiers (including pkg-qualified ones)
+ // are handled semantically, not syntactically.
+ xobj := isRef(x, tr.info)
+ yobj := isRef(y, tr.info)
+ if xobj != nil {
+ return xobj == yobj
+ }
+ if yobj != nil {
+ return false
+ }
+
+ // TODO(adonovan): audit: we cannot assume these ast.Exprs
+ // contain non-nil pointers. e.g. ImportSpec.Name may be a
+ // nil *ast.Ident.
+
+ if reflect.TypeOf(x) != reflect.TypeOf(y) {
+ return false
+ }
+ switch x := x.(type) {
+ case *ast.Ident:
+ log.Fatalf("unexpected Ident: %s", astString(tr.fset, x))
+
+ case *ast.BasicLit:
+ y := y.(*ast.BasicLit)
+ xval := exact.MakeFromLiteral(x.Value, x.Kind)
+ yval := exact.MakeFromLiteral(y.Value, y.Kind)
+ return exact.Compare(xval, token.EQL, yval)
+
+ case *ast.FuncLit:
+ // func literals (and thus statement syntax) never match.
+ return false
+
+ case *ast.CompositeLit:
+ y := y.(*ast.CompositeLit)
+ return (x.Type == nil) == (y.Type == nil) &&
+ (x.Type == nil || tr.matchType(x.Type, y.Type)) &&
+ tr.matchExprs(x.Elts, y.Elts)
+
+ case *ast.SelectorExpr:
+ y := y.(*ast.SelectorExpr)
+ return tr.matchSelectorExpr(x, y) &&
+ tr.info.Selections[x].Obj() == tr.info.Selections[y].Obj()
+
+ case *ast.IndexExpr:
+ y := y.(*ast.IndexExpr)
+ return tr.matchExpr(x.X, y.X) &&
+ tr.matchExpr(x.Index, y.Index)
+
+ case *ast.SliceExpr:
+ y := y.(*ast.SliceExpr)
+ return tr.matchExpr(x.X, y.X) &&
+ tr.matchExpr(x.Low, y.Low) &&
+ tr.matchExpr(x.High, y.High) &&
+ tr.matchExpr(x.Max, y.Max) &&
+ x.Slice3 == y.Slice3
+
+ case *ast.TypeAssertExpr:
+ y := y.(*ast.TypeAssertExpr)
+ return tr.matchExpr(x.X, y.X) &&
+ tr.matchType(x.Type, y.Type)
+
+ case *ast.CallExpr:
+ y := y.(*ast.CallExpr)
+ match := tr.matchExpr // function call
+ if tr.info.Types[x.Fun].IsType() {
+ match = tr.matchType // type conversion
+ }
+ return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() &&
+ match(x.Fun, y.Fun) &&
+ tr.matchExprs(x.Args, y.Args)
+
+ case *ast.StarExpr:
+ y := y.(*ast.StarExpr)
+ return tr.matchExpr(x.X, y.X)
+
+ case *ast.UnaryExpr:
+ y := y.(*ast.UnaryExpr)
+ return x.Op == y.Op &&
+ tr.matchExpr(x.X, y.X)
+
+ case *ast.BinaryExpr:
+ y := y.(*ast.BinaryExpr)
+ return x.Op == y.Op &&
+ tr.matchExpr(x.X, y.X) &&
+ tr.matchExpr(x.Y, y.Y)
+
+ case *ast.KeyValueExpr:
+ y := y.(*ast.KeyValueExpr)
+ return tr.matchExpr(x.Key, y.Key) &&
+ tr.matchExpr(x.Value, y.Value)
+ }
+
+ panic(fmt.Sprintf("unhandled AST node type: %T", x))
+}
+
+func (tr *Transformer) matchExprs(xx, yy []ast.Expr) bool {
+ if len(xx) != len(yy) {
+ return false
+ }
+ for i := range xx {
+ if !tr.matchExpr(xx[i], yy[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// matchType reports whether the two type ASTs denote identical types.
+func (tr *Transformer) matchType(x, y ast.Expr) bool {
+ tx := tr.info.Types[x].Type
+ ty := tr.info.Types[y].Type
+ return types.Identical(tx, ty)
+}
+
+func (tr *Transformer) wildcardObj(x ast.Expr) (*types.Var, bool) {
+ if x, ok := x.(*ast.Ident); ok && x != nil && tr.allowWildcards {
+ if xobj, ok := tr.info.Uses[x].(*types.Var); ok && tr.wildcards[xobj] {
+ return xobj, true
+ }
+ }
+ return nil, false
+}
+
+func (tr *Transformer) matchSelectorExpr(x, y *ast.SelectorExpr) bool {
+ if xobj, ok := tr.wildcardObj(x.X); ok {
+ field := x.Sel.Name
+ yt := tr.info.TypeOf(y.X)
+ o, _, _ := types.LookupFieldOrMethod(yt, true, tr.currentPkg, field)
+ if o != nil {
+ tr.env[xobj.Name()] = y.X // record binding
+ return true
+ }
+ }
+ return tr.matchExpr(x.X, y.X)
+}
+
+func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool {
+ name := xobj.Name()
+
+ if tr.verbose {
+ fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ",
+ tr.fset.Position(y.Pos()), name, astString(tr.fset, y))
+ }
+
+ // Check that y is assignable to the declared type of the param.
+ yt := tr.info.TypeOf(y)
+ if yt == nil {
+ // y has no type.
+ // Perhaps it is an *ast.Ellipsis in [...]T{}, or
+ // an *ast.KeyValueExpr in T{k: v}.
+ // Clearly these pseudo-expressions cannot match a
+ // wildcard, but it would nice if we had a way to ignore
+ // the difference between T{v} and T{k:v} for structs.
+ return false
+ }
+ if !types.AssignableTo(yt, xobj.Type()) {
+ if tr.verbose {
+ fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type())
+ }
+ return false
+ }
+
+ // A wildcard matches any expression.
+ // If it appears multiple times in the pattern, it must match
+ // the same expression each time.
+ if old, ok := tr.env[name]; ok {
+ // found existing binding
+ tr.allowWildcards = false
+ r := tr.matchExpr(old, y)
+ if tr.verbose {
+ fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n",
+ r, astString(tr.fset, old))
+ }
+ tr.allowWildcards = true
+ return r
+ }
+
+ if tr.verbose {
+ fmt.Fprintf(os.Stderr, "primary match\n")
+ }
+
+ tr.env[name] = y // record binding
+ return true
+}
+
+// -- utilities --------------------------------------------------------
+
+func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
+
+// isRef returns the object referred to by this (possibly qualified)
+// identifier, or nil if the node is not a referring identifier.
+func isRef(n ast.Node, info *types.Info) types.Object {
+ switch n := n.(type) {
+ case *ast.Ident:
+ return info.Uses[n]
+
+ case *ast.SelectorExpr:
+ if _, ok := info.Selections[n]; !ok {
+ // qualified ident
+ return info.Uses[n.Sel]
+ }
+ }
+ return nil
+}
diff --git a/go/src/golang.org/x/tools/refactor/eg/rewrite.go b/go/src/golang.org/x/tools/refactor/eg/rewrite.go
index db9c693..d91a99c 100644
--- a/go/src/golang.org/x/tools/refactor/eg/rewrite.go
+++ b/go/src/golang.org/x/tools/refactor/eg/rewrite.go
@@ -1,3 +1,9 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.5
+
package eg
// This file defines the AST rewriting pass.
@@ -8,6 +14,7 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"os"
"reflect"
"sort"
@@ -15,7 +22,6 @@
"strings"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
)
// Transform applies the transformation to the specified parsed file,
@@ -31,7 +37,7 @@
func (tr *Transformer) Transform(info *types.Info, pkg *types.Package, file *ast.File) int {
if !tr.seenInfos[info] {
tr.seenInfos[info] = true
- mergeTypeInfo(&tr.info.Info, info)
+ mergeTypeInfo(tr.info, info)
}
tr.currentPkg = pkg
tr.nsubsts = 0
@@ -234,7 +240,7 @@
// denoted by unqualified identifiers.
//
if tr.importedObjs != nil && pattern.Type() == selectorExprType {
- obj := isRef(pattern.Interface().(*ast.SelectorExpr), &tr.info)
+ obj := isRef(pattern.Interface().(*ast.SelectorExpr), tr.info)
if obj != nil {
if sel, ok := tr.importedObjs[obj]; ok {
var id ast.Expr
@@ -288,7 +294,7 @@
// All ast.Node implementations are *structs,
// so this case catches them all.
if e := rvToExpr(v); e != nil {
- updateTypeInfo(&tr.info.Info, e, p.Interface().(ast.Expr))
+ updateTypeInfo(tr.info, e, p.Interface().(ast.Expr))
}
return v
diff --git a/go/src/golang.org/x/tools/refactor/eg/rewrite14.go b/go/src/golang.org/x/tools/refactor/eg/rewrite14.go
new file mode 100644
index 0000000..01b4fe2
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/eg/rewrite14.go
@@ -0,0 +1,346 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package eg
+
+// This file defines the AST rewriting pass.
+// Most of it was plundered directly from
+// $GOROOT/src/cmd/gofmt/rewrite.go (after convergent evolution).
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "os"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/types"
+)
+
+// Transform applies the transformation to the specified parsed file,
+// whose type information is supplied in info, and returns the number
+// of replacements that were made.
+//
+// It mutates the AST in place (the identity of the root node is
+// unchanged), and may add nodes for which no type information is
+// available in info.
+//
+// Derived from rewriteFile in $GOROOT/src/cmd/gofmt/rewrite.go.
+//
+func (tr *Transformer) Transform(info *types.Info, pkg *types.Package, file *ast.File) int {
+ if !tr.seenInfos[info] {
+ tr.seenInfos[info] = true
+ mergeTypeInfo(tr.info, info)
+ }
+ tr.currentPkg = pkg
+ tr.nsubsts = 0
+
+ if tr.verbose {
+ fmt.Fprintf(os.Stderr, "before: %s\n", astString(tr.fset, tr.before))
+ fmt.Fprintf(os.Stderr, "after: %s\n", astString(tr.fset, tr.after))
+ }
+
+ var f func(rv reflect.Value) reflect.Value
+ f = func(rv reflect.Value) reflect.Value {
+ // don't bother if val is invalid to start with
+ if !rv.IsValid() {
+ return reflect.Value{}
+ }
+
+ rv = apply(f, rv)
+
+ e := rvToExpr(rv)
+ if e != nil {
+ savedEnv := tr.env
+ tr.env = make(map[string]ast.Expr) // inefficient! Use a slice of k/v pairs
+
+ if tr.matchExpr(tr.before, e) {
+ if tr.verbose {
+ fmt.Fprintf(os.Stderr, "%s matches %s",
+ astString(tr.fset, tr.before), astString(tr.fset, e))
+ if len(tr.env) > 0 {
+ fmt.Fprintf(os.Stderr, " with:")
+ for name, ast := range tr.env {
+ fmt.Fprintf(os.Stderr, " %s->%s",
+ name, astString(tr.fset, ast))
+ }
+ }
+ fmt.Fprintf(os.Stderr, "\n")
+ }
+ tr.nsubsts++
+
+ // Clone the replacement tree, performing parameter substitution.
+ // We update all positions to n.Pos() to aid comment placement.
+ rv = tr.subst(tr.env, reflect.ValueOf(tr.after),
+ reflect.ValueOf(e.Pos()))
+ }
+ tr.env = savedEnv
+ }
+
+ return rv
+ }
+ file2 := apply(f, reflect.ValueOf(file)).Interface().(*ast.File)
+
+ // By construction, the root node is unchanged.
+ if file != file2 {
+ panic("BUG")
+ }
+
+ // Add any necessary imports.
+ // TODO(adonovan): remove no-longer needed imports too.
+ if tr.nsubsts > 0 {
+ pkgs := make(map[string]*types.Package)
+ for obj := range tr.importedObjs {
+ pkgs[obj.Pkg().Path()] = obj.Pkg()
+ }
+
+ for _, imp := range file.Imports {
+ path, _ := strconv.Unquote(imp.Path.Value)
+ delete(pkgs, path)
+ }
+ delete(pkgs, pkg.Path()) // don't import self
+
+ // NB: AddImport may completely replace the AST!
+ // It thus renders info and tr.info no longer relevant to file.
+ var paths []string
+ for path := range pkgs {
+ paths = append(paths, path)
+ }
+ sort.Strings(paths)
+ for _, path := range paths {
+ astutil.AddImport(tr.fset, file, path)
+ }
+ }
+
+ tr.currentPkg = nil
+
+ return tr.nsubsts
+}
+
+// setValue is a wrapper for x.SetValue(y); it protects
+// the caller from panics if x cannot be changed to y.
+func setValue(x, y reflect.Value) {
+ // don't bother if y is invalid to start with
+ if !y.IsValid() {
+ return
+ }
+ defer func() {
+ if x := recover(); x != nil {
+ if s, ok := x.(string); ok &&
+ (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
+ // x cannot be set to y - ignore this rewrite
+ return
+ }
+ panic(x)
+ }
+ }()
+ x.Set(y)
+}
+
+// Values/types for special cases.
+var (
+ objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
+ scopePtrNil = reflect.ValueOf((*ast.Scope)(nil))
+
+ identType = reflect.TypeOf((*ast.Ident)(nil))
+ selectorExprType = reflect.TypeOf((*ast.SelectorExpr)(nil))
+ objectPtrType = reflect.TypeOf((*ast.Object)(nil))
+ positionType = reflect.TypeOf(token.NoPos)
+ callExprType = reflect.TypeOf((*ast.CallExpr)(nil))
+ scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
+)
+
+// apply replaces each AST field x in val with f(x), returning val.
+// To avoid extra conversions, f operates on the reflect.Value form.
+func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
+ if !val.IsValid() {
+ return reflect.Value{}
+ }
+
+ // *ast.Objects introduce cycles and are likely incorrect after
+ // rewrite; don't follow them but replace with nil instead
+ if val.Type() == objectPtrType {
+ return objectPtrNil
+ }
+
+ // similarly for scopes: they are likely incorrect after a rewrite;
+ // replace them with nil
+ if val.Type() == scopePtrType {
+ return scopePtrNil
+ }
+
+ switch v := reflect.Indirect(val); v.Kind() {
+ case reflect.Slice:
+ for i := 0; i < v.Len(); i++ {
+ e := v.Index(i)
+ setValue(e, f(e))
+ }
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ e := v.Field(i)
+ setValue(e, f(e))
+ }
+ case reflect.Interface:
+ e := v.Elem()
+ setValue(v, f(e))
+ }
+ return val
+}
+
+// subst returns a copy of (replacement) pattern with values from env
+// substituted in place of wildcards and pos used as the position of
+// tokens from the pattern. if env == nil, subst returns a copy of
+// pattern and doesn't change the line number information.
+func (tr *Transformer) subst(env map[string]ast.Expr, pattern, pos reflect.Value) reflect.Value {
+ if !pattern.IsValid() {
+ return reflect.Value{}
+ }
+
+ // *ast.Objects introduce cycles and are likely incorrect after
+ // rewrite; don't follow them but replace with nil instead
+ if pattern.Type() == objectPtrType {
+ return objectPtrNil
+ }
+
+ // similarly for scopes: they are likely incorrect after a rewrite;
+ // replace them with nil
+ if pattern.Type() == scopePtrType {
+ return scopePtrNil
+ }
+
+ // Wildcard gets replaced with map value.
+ if env != nil && pattern.Type() == identType {
+ id := pattern.Interface().(*ast.Ident)
+ if old, ok := env[id.Name]; ok {
+ return tr.subst(nil, reflect.ValueOf(old), reflect.Value{})
+ }
+ }
+
+ // Emit qualified identifiers in the pattern by appropriate
+ // (possibly qualified) identifier in the input.
+ //
+ // The template cannot contain dot imports, so all identifiers
+ // for imported objects are explicitly qualified.
+ //
+ // We assume (unsoundly) that there are no dot or named
+ // imports in the input code, nor are any imported package
+ // names shadowed, so the usual normal qualified identifier
+ // syntax may be used.
+ // TODO(adonovan): fix: avoid this assumption.
+ //
+ // A refactoring may be applied to a package referenced by the
+ // template. Objects belonging to the current package are
+ // denoted by unqualified identifiers.
+ //
+ if tr.importedObjs != nil && pattern.Type() == selectorExprType {
+ obj := isRef(pattern.Interface().(*ast.SelectorExpr), tr.info)
+ if obj != nil {
+ if sel, ok := tr.importedObjs[obj]; ok {
+ var id ast.Expr
+ if obj.Pkg() == tr.currentPkg {
+ id = sel.Sel // unqualified
+ } else {
+ id = sel // pkg-qualified
+ }
+
+ // Return a clone of id.
+ saved := tr.importedObjs
+ tr.importedObjs = nil // break cycle
+ r := tr.subst(nil, reflect.ValueOf(id), pos)
+ tr.importedObjs = saved
+ return r
+ }
+ }
+ }
+
+ if pos.IsValid() && pattern.Type() == positionType {
+ // use new position only if old position was valid in the first place
+ if old := pattern.Interface().(token.Pos); !old.IsValid() {
+ return pattern
+ }
+ return pos
+ }
+
+ // Otherwise copy.
+ switch p := pattern; p.Kind() {
+ case reflect.Slice:
+ v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
+ for i := 0; i < p.Len(); i++ {
+ v.Index(i).Set(tr.subst(env, p.Index(i), pos))
+ }
+ return v
+
+ case reflect.Struct:
+ v := reflect.New(p.Type()).Elem()
+ for i := 0; i < p.NumField(); i++ {
+ v.Field(i).Set(tr.subst(env, p.Field(i), pos))
+ }
+ return v
+
+ case reflect.Ptr:
+ v := reflect.New(p.Type()).Elem()
+ if elem := p.Elem(); elem.IsValid() {
+ v.Set(tr.subst(env, elem, pos).Addr())
+ }
+
+ // Duplicate type information for duplicated ast.Expr.
+ // All ast.Node implementations are *structs,
+ // so this case catches them all.
+ if e := rvToExpr(v); e != nil {
+ updateTypeInfo(tr.info, e, p.Interface().(ast.Expr))
+ }
+ return v
+
+ case reflect.Interface:
+ v := reflect.New(p.Type()).Elem()
+ if elem := p.Elem(); elem.IsValid() {
+ v.Set(tr.subst(env, elem, pos))
+ }
+ return v
+ }
+
+ return pattern
+}
+
+// -- utilities -------------------------------------------------------
+
+func rvToExpr(rv reflect.Value) ast.Expr {
+ if rv.CanInterface() {
+ if e, ok := rv.Interface().(ast.Expr); ok {
+ return e
+ }
+ }
+ return nil
+}
+
+// updateTypeInfo duplicates type information for the existing AST old
+// so that it also applies to duplicated AST new.
+func updateTypeInfo(info *types.Info, new, old ast.Expr) {
+ switch new := new.(type) {
+ case *ast.Ident:
+ orig := old.(*ast.Ident)
+ if obj, ok := info.Defs[orig]; ok {
+ info.Defs[new] = obj
+ }
+ if obj, ok := info.Uses[orig]; ok {
+ info.Uses[new] = obj
+ }
+
+ case *ast.SelectorExpr:
+ orig := old.(*ast.SelectorExpr)
+ if sel, ok := info.Selections[orig]; ok {
+ info.Selections[new] = sel
+ }
+ }
+
+ if tv, ok := info.Types[old]; ok {
+ info.Types[new] = tv
+ }
+}
diff --git a/go/src/golang.org/x/tools/refactor/importgraph/graph.go b/go/src/golang.org/x/tools/refactor/importgraph/graph.go
index 8ad8014..1fc0781 100644
--- a/go/src/golang.org/x/tools/refactor/importgraph/graph.go
+++ b/go/src/golang.org/x/tools/refactor/importgraph/graph.go
@@ -53,9 +53,10 @@
// Build scans the specified Go workspace and builds the forward and
// reverse import dependency graphs for all its packages.
-// It also returns a mapping from import paths to errors for packages
+// It also returns a mapping from canonical import paths to errors for packages
// whose loading was not entirely successful.
// A package may appear in the graph and in the errors mapping.
+// All package paths are canonical and may contain "/vendor/".
func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
type importEdge struct {
from, to string
@@ -67,39 +68,75 @@
ch := make(chan interface{})
- var wg sync.WaitGroup
- buildutil.ForEachPackage(ctxt, func(path string, err error) {
- wg.Add(1)
- go func() {
- defer wg.Done()
+ go func() {
+ sema := make(chan int, 20) // I/O concurrency limiting semaphore
+ var wg sync.WaitGroup
+ buildutil.ForEachPackage(ctxt, func(path string, err error) {
if err != nil {
ch <- pathError{path, err}
return
}
- bp, err := ctxt.Import(path, "", 0)
- if err != nil {
- if _, ok := err.(*build.NoGoError); ok {
- // empty directory is not an error
- } else {
- ch <- pathError{path, err}
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ sema <- 1
+ bp, err := ctxt.Import(path, "", 0)
+ <-sema
+
+ if err != nil {
+ if _, ok := err.(*build.NoGoError); ok {
+ // empty directory is not an error
+ } else {
+ ch <- pathError{path, err}
+ }
+ // Even in error cases, Import usually returns a package.
}
- // Even in error cases, Import usually returns a package.
- }
- if bp != nil {
- for _, imp := range bp.Imports {
- ch <- importEdge{path, imp}
+
+ // absolutize resolves an import path relative
+ // to the current package bp.
+ // The absolute form may contain "vendor".
+ //
+ // The vendoring feature slows down Build by 3×.
+ // Here are timings from a 1400 package workspace:
+ // 1100ms: current code (with vendor check)
+ // 880ms: with a nonblocking cache around ctxt.IsDir
+ // 840ms: nonblocking cache with duplicate suppression
+ // 340ms: original code (no vendor check)
+ // TODO(adonovan): optimize, somehow.
+ memo := make(map[string]string)
+ absolutize := func(path string) string {
+ canon, ok := memo[path]
+ if !ok {
+ sema <- 1
+ bp2, _ := ctxt.Import(path, bp.Dir, build.FindOnly)
+ <-sema
+
+ if bp2 != nil {
+ canon = bp2.ImportPath
+ } else {
+ canon = path
+ }
+ memo[path] = canon
+ }
+ return canon
}
- for _, imp := range bp.TestImports {
- ch <- importEdge{path, imp}
+
+ if bp != nil {
+ for _, imp := range bp.Imports {
+ ch <- importEdge{path, absolutize(imp)}
+ }
+ for _, imp := range bp.TestImports {
+ ch <- importEdge{path, absolutize(imp)}
+ }
+ for _, imp := range bp.XTestImports {
+ ch <- importEdge{path, absolutize(imp)}
+ }
}
- for _, imp := range bp.XTestImports {
- ch <- importEdge{path, imp}
- }
- }
- }()
- })
- go func() {
+
+ }()
+ })
wg.Wait()
close(ch)
}()
diff --git a/go/src/golang.org/x/tools/refactor/importgraph/graph_test.go b/go/src/golang.org/x/tools/refactor/importgraph/graph_test.go
index a486c26..52dd769 100644
--- a/go/src/golang.org/x/tools/refactor/importgraph/graph_test.go
+++ b/go/src/golang.org/x/tools/refactor/importgraph/graph_test.go
@@ -10,7 +10,6 @@
import (
"go/build"
- "runtime"
"sort"
"testing"
@@ -22,15 +21,12 @@
const this = "golang.org/x/tools/refactor/importgraph"
func TestBuild(t *testing.T) {
- saved := runtime.GOMAXPROCS(8) // Build is highly parallel
- defer runtime.GOMAXPROCS(saved)
-
forward, reverse, errors := importgraph.Build(&build.Default)
// Test direct edges.
// We throw in crypto/hmac to prove that external test files
// (such as this one) are inspected.
- for _, p := range []string{"go/build", "runtime", "testing", "crypto/hmac"} {
+ for _, p := range []string{"go/build", "testing", "crypto/hmac"} {
if !forward[this][p] {
t.Errorf("forward[importgraph][%s] not found", p)
}
@@ -40,7 +36,7 @@
}
// Test non-existent direct edges
- for _, p := range []string{"fmt", "errors", "reflect"} {
+ for _, p := range []string{"errors", "reflect"} {
if forward[this][p] {
t.Errorf("unexpected: forward[importgraph][%s] found", p)
}
diff --git a/go/src/golang.org/x/tools/refactor/lexical/lexical.go b/go/src/golang.org/x/tools/refactor/lexical/lexical.go
deleted file mode 100644
index c6567e4..0000000
--- a/go/src/golang.org/x/tools/refactor/lexical/lexical.go
+++ /dev/null
@@ -1,763 +0,0 @@
-// Package lexical computes the structure of the lexical environment,
-// including the definition of and references to all universal,
-// package-level, file-level and function-local entities. It does not
-// record qualified identifiers, labels, struct fields, or methods.
-//
-// It is intended for renaming and refactoring tools, which need a more
-// precise understanding of identifier resolution than is available from
-// the output of the type-checker alone.
-//
-// THIS INTERFACE IS EXPERIMENTAL AND MAY CHANGE OR BE REMOVED IN FUTURE.
-//
-package lexical // import "golang.org/x/tools/refactor/lexical"
-
-// OVERVIEW
-//
-// As we traverse the AST, we build a "spaghetti stack" of Blocks,
-// i.e. a tree with parent edges pointing to the root. Each time we
-// visit an identifier that's a reference into the lexical environment,
-// we create and save an Environment, which captures the current mapping
-// state of the Block; these are saved for the client.
-//
-// We don't bother recording non-lexical references.
-
-// TODO(adonovan):
-// - make it robust against syntax errors. Audit all type assertions, etc.
-// - better still, after the Go 1.4 thaw, move this into go/types.
-// I don't think it need be a big change since the visitor is already there;
-// we just need to records Environments. lexical.Block is analogous
-// to types.Scope.
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "os"
- "strconv"
-
- "golang.org/x/tools/go/types"
-)
-
-const trace = false
-
-var logf = func(format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, format, args...)
-}
-
-// A Block is a level of the lexical environment, a tree of blocks.
-// It maps names to objects.
-//
-type Block struct {
- kind string // one of universe package file func block if switch typeswitch case for range
- syntax ast.Node // syntax declaring the block (nil for universe and package) [needed?]
-
- parent Environment
- bindings []types.Object // bindings in lexical order
- index map[string]int // maps a name to the index of its binding, for fast lookup
-}
-
-// An Environment is a snapshot of a Block taken at a certain lexical
-// position. It may contain bindings for fewer names than the
-// (completed) block, or different bindings for names that are
-// re-defined later in the block.
-//
-// For example, the lexical Block for the function f below contains a
-// binding for the local var x, but the Environments captured by at the
-// two print(x) calls differ: the first contains this binding, the
-// second does not. The first Environment contains a different binding
-// for x: the string var defined in the package block, an ancestor.
-//
-// var x string
-// func f() {
-// print(x)
-// x := 1
-// print(x)
-// }
-//
-type Environment struct {
- block *Block
- nbindings int // length of prefix of block.bindings that's visible
-}
-
-// Depth returns the depth of this block in the block tree.
-// The universal block has depth 1, a package block 2, a file block 3, etc.
-func (b *Block) Depth() int {
- if b == nil {
- return 0
- }
- return 1 + b.parent.block.Depth()
-}
-
-// env returns an Environment that is a snapshot of b's current state.
-func (b *Block) env() Environment {
- return Environment{b, len(b.bindings)}
-}
-
-// Lookup returns the definition of name in the environment specified by
-// env, and the Block that defines it, which may be an ancestor.
-func (env Environment) Lookup(name string) (types.Object, *Block) {
- if env.block == nil {
- return nil, nil
- }
- return lookup(env.block, name, env.nbindings)
-}
-
-// nbindings specifies what prefix of b.bindings should be considered visible.
-func lookup(b *Block, name string, nbindings int) (types.Object, *Block) {
- if b == nil {
- return nil, nil
- }
- if i, ok := b.index[name]; ok && i < nbindings {
- return b.bindings[i], b
- }
-
- parent := b.parent
- if parent.block == nil {
- return nil, nil
- }
- return lookup(parent.block, name, parent.nbindings)
-}
-
-// Lookup returns the definition of name in the environment specified by
-// b, and the Block that defines it, which may be an ancestor.
-func (b *Block) Lookup(name string) (types.Object, *Block) {
- return b.env().Lookup(name)
-}
-
-// Block returns the block of which this environment is a partial view.
-func (env Environment) Block() *Block {
- return env.block
-}
-
-func (env Environment) String() string {
- return fmt.Sprintf("%s:%d", env.block, env.nbindings)
-}
-
-func (b *Block) String() string {
- var s string
- if b.parent.block != nil {
- s = b.parent.block.String()
- s += "."
- }
- return s + b.kind
-}
-
-var universe = &Block{kind: "universe", index: make(map[string]int)}
-
-func init() {
- for i, name := range types.Universe.Names() {
- obj := types.Universe.Lookup(name)
- universe.bindings = append(universe.bindings, obj)
- universe.index[name] = i
- }
-}
-
-// -- resolver ---------------------------------------------------------
-
-// A Reference provides the lexical environment for a given reference to
-// an object in lexical scope.
-type Reference struct {
- Id *ast.Ident
- Env Environment
-}
-
-// resolver holds the state of the identifier resolution visitation:
-// the package information, the result, and the current block.
-type resolver struct {
- fset *token.FileSet
- imports map[string]*types.Package
- pkg *types.Package
- info *types.Info
-
- // visitor state
- block *Block
-
- result *Info
-}
-
-func (r *resolver) setBlock(kind string, syntax ast.Node) *Block {
- b := &Block{
- kind: kind,
- syntax: syntax,
- parent: r.block.env(),
- index: make(map[string]int),
- }
- if syntax != nil {
- r.result.Blocks[syntax] = b
- }
- r.block = b
- return b
-}
-
-func (r *resolver) qualifier(pkg *types.Package) string {
- if pkg == r.pkg {
- return "" // unqualified intra-package reference
- }
- return pkg.Path()
-}
-
-func (r *resolver) use(id *ast.Ident, env Environment) {
- if id.Name == "_" {
- return // an error
- }
- obj, _ := env.Lookup(id.Name)
- if obj == nil {
- logf("%s: lookup of %s failed\n", r.fset.Position(id.Pos()), id.Name)
- } else if want := r.info.Uses[id]; obj != want {
- // sanity check against go/types resolver
- logf("%s: internal error: lookup of %s yielded wrong object: got %v (%s), want %v\n",
- r.fset.Position(id.Pos()), id.Name, types.ObjectString(obj, r.qualifier),
- r.fset.Position(obj.Pos()),
- want)
- }
- if trace {
- logf("use %s = %v in %s\n", id.Name, types.ObjectString(obj, r.qualifier), env)
- }
-
- r.result.Refs[obj] = append(r.result.Refs[obj], Reference{id, env})
-}
-
-func (r *resolver) define(b *Block, id *ast.Ident) {
- obj := r.info.Defs[id]
- if obj == nil {
- logf("%s: internal error: not a defining ident: %s\n",
- r.fset.Position(id.Pos()), id.Name)
- panic(id)
- }
- r.defineObject(b, id.Name, obj)
-
- // Objects (other than PkgName) defined at file scope
- // are also defined in the enclosing package scope.
- if _, ok := b.syntax.(*ast.File); ok {
- switch obj.(type) {
- default:
- r.defineObject(b.parent.block, id.Name, obj)
- case nil, *types.PkgName:
- }
- }
-}
-
-// Used for implicit objects created by some ImportSpecs and CaseClauses.
-func (r *resolver) defineImplicit(b *Block, n ast.Node, name string) {
- obj := r.info.Implicits[n]
- if obj == nil {
- logf("%s: internal error: not an implicit definition: %T\n",
- r.fset.Position(n.Pos()), n)
- }
- r.defineObject(b, name, obj)
-}
-
-func (r *resolver) defineObject(b *Block, name string, obj types.Object) {
- if obj.Name() == "_" {
- return
- }
- i := len(b.bindings)
- b.bindings = append(b.bindings, obj)
- b.index[name] = i
- if trace {
- logf("def %s = %s in %s\n", name, types.ObjectString(obj, r.qualifier), b)
- }
- r.result.Defs[obj] = b
-}
-
-func (r *resolver) function(recv *ast.FieldList, typ *ast.FuncType, body *ast.BlockStmt, syntax ast.Node) {
- // Use all signature types in enclosing block.
- r.expr(typ)
- r.fieldList(recv, false)
-
- savedBlock := r.block // save
- r.setBlock("func", syntax)
-
- // Define all parameters/results, and visit the body, within the func block.
- r.fieldList(typ.Params, true)
- r.fieldList(typ.Results, true)
- r.fieldList(recv, true)
- if body != nil {
- r.stmtList(body.List)
- }
-
- r.block = savedBlock // restore
-}
-
-func (r *resolver) fieldList(list *ast.FieldList, def bool) {
- if list != nil {
- for _, f := range list.List {
- if def {
- for _, id := range f.Names {
- r.define(r.block, id)
- }
- } else {
- r.expr(f.Type)
- }
- }
- }
-}
-
-func (r *resolver) exprList(list []ast.Expr) {
- for _, x := range list {
- r.expr(x)
- }
-}
-
-func (r *resolver) expr(n ast.Expr) {
- switch n := n.(type) {
- case *ast.BadExpr:
- case *ast.BasicLit:
- // no-op
-
- case *ast.Ident:
- r.use(n, r.block.env())
-
- case *ast.Ellipsis:
- if n.Elt != nil {
- r.expr(n.Elt)
- }
-
- case *ast.FuncLit:
- r.function(nil, n.Type, n.Body, n)
-
- case *ast.CompositeLit:
- if n.Type != nil {
- r.expr(n.Type)
- }
- tv := r.info.Types[n]
- if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
- for _, elt := range n.Elts {
- if kv, ok := elt.(*ast.KeyValueExpr); ok {
- r.expr(kv.Value)
-
- // Also uses field kv.Key (non-lexical)
- // id := kv.Key.(*ast.Ident)
- // obj := r.info.Uses[id]
- // logf("use %s = %v (field)\n",
- // id.Name, types.ObjectString(obj, r.qualifier))
- // TODO make a fake FieldVal selection?
- } else {
- r.expr(elt)
- }
- }
- } else {
- r.exprList(n.Elts)
- }
-
- case *ast.ParenExpr:
- r.expr(n.X)
-
- case *ast.SelectorExpr:
- r.expr(n.X)
-
- // Non-lexical reference to field/method, or qualified identifier.
- // if sel, ok := r.info.Selections[n]; ok { // selection
- // switch sel.Kind() {
- // case types.FieldVal:
- // logf("use %s = %v (field)\n",
- // n.Sel.Name, types.ObjectString(sel.Obj(), r.qualifier))
- // case types.MethodExpr, types.MethodVal:
- // logf("use %s = %v (method)\n",
- // n.Sel.Name, types.ObjectString(sel.Obj(), r.qualifier))
- // }
- // } else { // qualified identifier
- // obj := r.info.Uses[n.Sel]
- // logf("use %s = %v (qualified)\n", n.Sel.Name, obj)
- // }
-
- case *ast.IndexExpr:
- r.expr(n.X)
- r.expr(n.Index)
-
- case *ast.SliceExpr:
- r.expr(n.X)
- if n.Low != nil {
- r.expr(n.Low)
- }
- if n.High != nil {
- r.expr(n.High)
- }
- if n.Max != nil {
- r.expr(n.Max)
- }
-
- case *ast.TypeAssertExpr:
- r.expr(n.X)
- if n.Type != nil {
- r.expr(n.Type)
- }
-
- case *ast.CallExpr:
- r.expr(n.Fun)
- r.exprList(n.Args)
-
- case *ast.StarExpr:
- r.expr(n.X)
-
- case *ast.UnaryExpr:
- r.expr(n.X)
-
- case *ast.BinaryExpr:
- r.expr(n.X)
- r.expr(n.Y)
-
- case *ast.KeyValueExpr:
- r.expr(n.Key)
- r.expr(n.Value)
-
- case *ast.ArrayType:
- if n.Len != nil {
- r.expr(n.Len)
- }
- r.expr(n.Elt)
-
- case *ast.StructType:
- // Use all the type names, but don't define any fields.
- r.fieldList(n.Fields, false)
-
- case *ast.FuncType:
- // Use all the type names, but don't define any vars.
- r.fieldList(n.Params, false)
- r.fieldList(n.Results, false)
-
- case *ast.InterfaceType:
- // Use all the type names, but don't define any methods.
- r.fieldList(n.Methods, false)
-
- case *ast.MapType:
- r.expr(n.Key)
- r.expr(n.Value)
-
- case *ast.ChanType:
- r.expr(n.Value)
-
- default:
- panic(n)
- }
-}
-
-func (r *resolver) stmtList(list []ast.Stmt) {
- for _, s := range list {
- r.stmt(s)
- }
-}
-
-func (r *resolver) stmt(n ast.Stmt) {
- switch n := n.(type) {
- case *ast.BadStmt:
- case *ast.EmptyStmt:
- // nothing to do
-
- case *ast.DeclStmt:
- decl := n.Decl.(*ast.GenDecl)
- for _, spec := range decl.Specs {
- switch spec := spec.(type) {
- case *ast.ValueSpec: // const or var
- if spec.Type != nil {
- r.expr(spec.Type)
- }
- r.exprList(spec.Values)
- for _, name := range spec.Names {
- r.define(r.block, name)
- }
-
- case *ast.TypeSpec:
- r.define(r.block, spec.Name)
- r.expr(spec.Type)
- }
- }
-
- case *ast.LabeledStmt:
- // Also defines label n.Label (non-lexical)
- r.stmt(n.Stmt)
-
- case *ast.ExprStmt:
- r.expr(n.X)
-
- case *ast.SendStmt:
- r.expr(n.Chan)
- r.expr(n.Value)
-
- case *ast.IncDecStmt:
- r.expr(n.X)
-
- case *ast.AssignStmt:
- if n.Tok == token.DEFINE {
- r.exprList(n.Rhs)
- for _, lhs := range n.Lhs {
- id := lhs.(*ast.Ident)
- if _, ok := r.info.Defs[id]; ok {
- r.define(r.block, id)
- } else {
- r.use(id, r.block.env())
- }
- }
- } else { // ASSIGN
- r.exprList(n.Lhs)
- r.exprList(n.Rhs)
- }
-
- case *ast.GoStmt:
- r.expr(n.Call)
-
- case *ast.DeferStmt:
- r.expr(n.Call)
-
- case *ast.ReturnStmt:
- r.exprList(n.Results)
-
- case *ast.BranchStmt:
- if n.Label != nil {
- // Also uses label n.Label (non-lexical)
- }
-
- case *ast.SelectStmt:
- r.stmtList(n.Body.List)
-
- case *ast.BlockStmt: // (explicit blocks only)
- savedBlock := r.block // save
- r.setBlock("block", n)
- r.stmtList(n.List)
- r.block = savedBlock // restore
-
- case *ast.IfStmt:
- savedBlock := r.block // save
- r.setBlock("if", n)
- if n.Init != nil {
- r.stmt(n.Init)
- }
- r.expr(n.Cond)
- r.stmt(n.Body) // new block
- if n.Else != nil {
- r.stmt(n.Else)
- }
- r.block = savedBlock // restore
-
- case *ast.CaseClause:
- savedBlock := r.block // save
- r.setBlock("case", n)
- if obj, ok := r.info.Implicits[n]; ok {
- // e.g.
- // switch y := x.(type) {
- // case T: // we declare an implicit 'var y T' in this block
- // }
- r.defineImplicit(r.block, n, obj.Name())
- }
- r.exprList(n.List)
- r.stmtList(n.Body)
- r.block = savedBlock // restore
-
- case *ast.SwitchStmt:
- savedBlock := r.block // save
- r.setBlock("switch", n)
- if n.Init != nil {
- r.stmt(n.Init)
- }
- if n.Tag != nil {
- r.expr(n.Tag)
- }
- r.stmtList(n.Body.List)
- r.block = savedBlock // restore
-
- case *ast.TypeSwitchStmt:
- savedBlock := r.block // save
- r.setBlock("typeswitch", n)
- if n.Init != nil {
- r.stmt(n.Init)
- }
- if assign, ok := n.Assign.(*ast.AssignStmt); ok { // y := x.(type)
- r.expr(assign.Rhs[0]) // skip y: not a defining ident
- } else {
- r.stmt(n.Assign)
- }
- r.stmtList(n.Body.List)
- r.block = savedBlock // restore
-
- case *ast.CommClause:
- savedBlock := r.block // save
- r.setBlock("case", n)
- if n.Comm != nil {
- r.stmt(n.Comm)
- }
- r.stmtList(n.Body)
- r.block = savedBlock // restore
-
- case *ast.ForStmt:
- savedBlock := r.block // save
- r.setBlock("for", n)
- if n.Init != nil {
- r.stmt(n.Init)
- }
- if n.Cond != nil {
- r.expr(n.Cond)
- }
- if n.Post != nil {
- r.stmt(n.Post)
- }
- r.stmt(n.Body)
- r.block = savedBlock // restore
-
- case *ast.RangeStmt:
- r.expr(n.X)
- savedBlock := r.block // save
- r.setBlock("range", n)
- if n.Tok == token.DEFINE {
- if n.Key != nil {
- r.define(r.block, n.Key.(*ast.Ident))
- }
- if n.Value != nil {
- r.define(r.block, n.Value.(*ast.Ident))
- }
- } else {
- if n.Key != nil {
- r.expr(n.Key)
- }
- if n.Value != nil {
- r.expr(n.Value)
- }
- }
- r.stmt(n.Body)
- r.block = savedBlock // restore
-
- default:
- panic(n)
- }
-}
-
-func (r *resolver) doImport(s *ast.ImportSpec, fileBlock *Block) {
- path, _ := strconv.Unquote(s.Path.Value)
- pkg := r.imports[path]
- if s.Name == nil { // normal
- r.defineImplicit(fileBlock, s, pkg.Name())
- } else if s.Name.Name == "." { // dot import
- for _, name := range pkg.Scope().Names() {
- if ast.IsExported(name) {
- obj := pkg.Scope().Lookup(name)
- r.defineObject(fileBlock, name, obj)
- }
- }
- } else { // renaming import
- r.define(fileBlock, s.Name)
- }
-}
-
-func (r *resolver) doPackage(pkg *types.Package, files []*ast.File) {
- r.block = universe
- r.result.Blocks[nil] = universe
-
- r.result.PackageBlock = r.setBlock("package", nil)
-
- var fileBlocks []*Block
-
- // 1. Insert all package-level objects into file and package blocks.
- // (PkgName objects are only inserted into file blocks.)
- for _, f := range files {
- r.block = r.result.PackageBlock
- fileBlock := r.setBlock("file", f) // package is not yet visible to file
- fileBlocks = append(fileBlocks, fileBlock)
-
- for _, d := range f.Decls {
- switch d := d.(type) {
- case *ast.GenDecl:
- for _, s := range d.Specs {
- switch s := s.(type) {
- case *ast.ImportSpec:
- r.doImport(s, fileBlock)
-
- case *ast.ValueSpec: // const or var
- for _, name := range s.Names {
- r.define(r.result.PackageBlock, name)
- }
-
- case *ast.TypeSpec:
- r.define(r.result.PackageBlock, s.Name)
- }
- }
-
- case *ast.FuncDecl:
- if d.Recv == nil { // function
- if d.Name.Name != "init" {
- r.define(r.result.PackageBlock, d.Name)
- }
- }
- }
- }
- }
-
- // 2. Now resolve bodies of GenDecls and FuncDecls.
- for i, f := range files {
- fileBlock := fileBlocks[i]
- fileBlock.parent = r.result.PackageBlock.env() // make entire package visible to this file
-
- for _, d := range f.Decls {
- r.block = fileBlock
-
- switch d := d.(type) {
- case *ast.GenDecl:
- for _, s := range d.Specs {
- switch s := s.(type) {
- case *ast.ValueSpec: // const or var
- if s.Type != nil {
- r.expr(s.Type)
- }
- r.exprList(s.Values)
-
- case *ast.TypeSpec:
- r.expr(s.Type)
- }
- }
-
- case *ast.FuncDecl:
- r.function(d.Recv, d.Type, d.Body, d)
- }
- }
- }
-
- r.block = nil
-}
-
-// An Info contains the lexical reference structure of a package.
-type Info struct {
- Defs map[types.Object]*Block // maps each object to its defining lexical block
- Refs map[types.Object][]Reference // maps each object to the set of references to it
- Blocks map[ast.Node]*Block // maps declaring syntax to block; nil => universe
- PackageBlock *Block // the package-level lexical block
-}
-
-// Structure computes the structure of the lexical environment of the
-// package specified by (pkg, info, files).
-//
-// The info.{Types,Defs,Uses,Implicits} maps must have been populated
-// by the type-checker
-//
-// fset is used for logging.
-//
-func Structure(fset *token.FileSet, pkg *types.Package, info *types.Info, files []*ast.File) *Info {
- r := resolver{
- fset: fset,
- imports: make(map[string]*types.Package),
- result: &Info{
- Defs: make(map[types.Object]*Block),
- Refs: make(map[types.Object][]Reference),
- Blocks: make(map[ast.Node]*Block),
- },
- pkg: pkg,
- info: info,
- }
-
- // Build import map for just this package.
- r.imports["unsafe"] = types.Unsafe
- for _, imp := range pkg.Imports() {
- r.imports[imp.Path()] = imp
- }
-
- r.doPackage(pkg, files)
-
- return r.result
-}
-
-// -- Plundered from golang.org/x/tools/go/ssa -----------------
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
- if p, ok := typ.Underlying().(*types.Pointer); ok {
- return p.Elem()
- }
- return typ
-}
diff --git a/go/src/golang.org/x/tools/refactor/lexical/lexical_test.go b/go/src/golang.org/x/tools/refactor/lexical/lexical_test.go
deleted file mode 100644
index 77287a4..0000000
--- a/go/src/golang.org/x/tools/refactor/lexical/lexical_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Incomplete std lib sources on Android.
-
-// +build !android
-
-package lexical
-
-import (
- "go/build"
- "testing"
-
- "golang.org/x/tools/go/buildutil"
- "golang.org/x/tools/go/loader"
-)
-
-func TestStdlib(t *testing.T) {
- defer func(saved func(format string, args ...interface{})) {
- logf = saved
- }(logf)
- logf = t.Errorf
-
- ctxt := build.Default // copy
-
- // Enumerate $GOROOT packages.
- saved := ctxt.GOPATH
- ctxt.GOPATH = "" // disable GOPATH during AllPackages
- pkgs := buildutil.AllPackages(&ctxt)
- ctxt.GOPATH = saved
-
- // Throw in a number of go.tools packages too.
- pkgs = append(pkgs,
- "golang.org/x/tools/cmd/godoc",
- "golang.org/x/tools/refactor/lexical")
-
- // Load, parse and type-check the program.
- conf := loader.Config{Build: &ctxt}
- for _, path := range pkgs {
- conf.ImportWithTests(path)
- }
- iprog, err := conf.Load()
- if err != nil {
- t.Fatalf("Load failed: %v", err)
- }
-
- // This test ensures that Structure doesn't panic and that
- // its internal sanity-checks against go/types don't fail.
- for pkg, info := range iprog.AllPackages {
- _ = Structure(iprog.Fset, pkg, &info.Info, info.Files)
- }
-}
diff --git a/go/src/golang.org/x/tools/refactor/rename/check.go b/go/src/golang.org/x/tools/refactor/rename/check.go
index 017a604..cf1999e 100644
--- a/go/src/golang.org/x/tools/refactor/rename/check.go
+++ b/go/src/golang.org/x/tools/refactor/rename/check.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package rename
// This file defines the safety checks for each kind of renaming.
@@ -10,10 +12,9 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/refactor/lexical"
"golang.org/x/tools/refactor/satisfy"
)
@@ -101,18 +102,20 @@
}
info := r.packages[from.Pkg()]
- lexinfo := lexical.Structure(r.iprog.Fset, from.Pkg(), &info.Info, info.Files)
// Check that in the package block, "init" is a function, and never referenced.
if r.to == "init" {
kind := objectKind(from)
if kind == "func" {
// Reject if intra-package references to it exist.
- if refs := lexinfo.Refs[from]; len(refs) > 0 {
- r.errorf(from.Pos(),
- "renaming this func %q to %q would make it a package initializer",
- from.Name(), r.to)
- r.errorf(refs[0].Id.Pos(), "\tbut references to it exist")
+ for id, obj := range info.Uses {
+ if obj == from {
+ r.errorf(from.Pos(),
+ "renaming this func %q to %q would make it a package initializer",
+ from.Name(), r.to)
+ r.errorf(id.Pos(), "\tbut references to it exist")
+ break
+ }
}
} else {
r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
@@ -122,7 +125,9 @@
// Check for conflicts between package block and all file blocks.
for _, f := range info.Files {
- if prev, b := lexinfo.Blocks[f].Lookup(r.to); b == lexinfo.Blocks[f] {
+ fileScope := info.Info.Scopes[f]
+ b, prev := fileScope.LookupParent(r.to, token.NoPos)
+ if b == fileScope {
r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
objectKind(from), from.Name(), r.to)
r.errorf(prev.Pos(), "\twith this %s",
@@ -205,11 +210,9 @@
// requires no checks.
//
func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) {
- lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files)
-
- b := lexinfo.Defs[from] // the block defining the 'from' object
+ b := from.Parent() // the block defining the 'from' object
if b != nil {
- to, toBlock := b.Lookup(r.to)
+ toBlock, to := b.LookupParent(r.to, from.Parent().End())
if toBlock == b {
// same-block conflict
r.errorf(from.Pos(), "renaming this %s %q to %q",
@@ -221,42 +224,45 @@
// Check for super-block conflict.
// The name r.to is defined in a superblock.
// Is that name referenced from within this block?
- for _, ref := range lexinfo.Refs[to] {
- if obj, _ := ref.Env.Lookup(from.Name()); obj == from {
+ forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool {
+ _, obj := lexicalLookup(block, from.Name(), id.Pos())
+ if obj == from {
// super-block conflict
r.errorf(from.Pos(), "renaming this %s %q to %q",
objectKind(from), from.Name(), r.to)
- r.errorf(ref.Id.Pos(), "\twould shadow this reference")
+ r.errorf(id.Pos(), "\twould shadow this reference")
r.errorf(to.Pos(), "\tto the %s declared here",
objectKind(to))
- return
+ return false // stop
}
- }
+ return true
+ })
}
}
// Check for sub-block conflict.
// Is there an intervening definition of r.to between
// the block defining 'from' and some reference to it?
- for _, ref := range lexinfo.Refs[from] {
- // TODO(adonovan): think about dot imports.
- // (Is b == fromBlock an invariant?)
- _, fromBlock := ref.Env.Lookup(from.Name())
- fromDepth := fromBlock.Depth()
+ forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool {
+ // Find the block that defines the found reference.
+ // It may be an ancestor.
+ fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos())
- to, toBlock := ref.Env.Lookup(r.to)
+ // See what r.to would resolve to in the same scope.
+ toBlock, to := lexicalLookup(block, r.to, id.Pos())
if to != nil {
// sub-block conflict
- if toBlock.Depth() > fromDepth {
+ if deeper(toBlock, fromBlock) {
r.errorf(from.Pos(), "renaming this %s %q to %q",
objectKind(from), from.Name(), r.to)
- r.errorf(ref.Id.Pos(), "\twould cause this reference to become shadowed")
+ r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
r.errorf(to.Pos(), "\tby this intervening %s definition",
objectKind(to))
- return
+ return false // stop
}
}
- }
+ return true
+ })
// Renaming a type that is used as an embedded field
// requires renaming the field too. e.g.
@@ -274,6 +280,123 @@
}
}
+// lexicalLookup is like (*types.Scope).LookupParent but respects the
+// environment visible at pos. It assumes the relative position
+// information is correct with each file.
+func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) {
+ for b := block; b != nil; b = b.Parent() {
+ obj := b.Lookup(name)
+ // The scope of a package-level object is the entire package,
+ // so ignore pos in that case.
+ // No analogous clause is needed for file-level objects
+ // since no reference can appear before an import decl.
+ if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) {
+ return b, obj
+ }
+ }
+ return nil, nil
+}
+
+// deeper reports whether block x is lexically deeper than y.
+func deeper(x, y *types.Scope) bool {
+ if x == y || x == nil {
+ return false
+ } else if y == nil {
+ return true
+ } else {
+ return deeper(x.Parent(), y.Parent())
+ }
+}
+
+// forEachLexicalRef calls fn(id, block) for each identifier id in package
+// info that is a reference to obj in lexical scope. block is the
+// lexical block enclosing the reference. If fn returns false the
+// iteration is terminated and findLexicalRefs returns false.
+func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
+ ok := true
+ var stack []ast.Node
+
+ var visit func(n ast.Node) bool
+ visit = func(n ast.Node) bool {
+ if n == nil {
+ stack = stack[:len(stack)-1] // pop
+ return false
+ }
+ if !ok {
+ return false // bail out
+ }
+
+ stack = append(stack, n) // push
+ switch n := n.(type) {
+ case *ast.Ident:
+ if info.Uses[n] == obj {
+ block := enclosingBlock(&info.Info, stack)
+ if !fn(n, block) {
+ ok = false
+ }
+ }
+ return visit(nil) // pop stack
+
+ case *ast.SelectorExpr:
+ // don't visit n.Sel
+ ast.Inspect(n.X, visit)
+ return visit(nil) // pop stack, don't descend
+
+ case *ast.CompositeLit:
+ // Handle recursion ourselves for struct literals
+ // so we don't visit field identifiers.
+ tv := info.Types[n]
+ if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
+ if n.Type != nil {
+ ast.Inspect(n.Type, visit)
+ }
+ for _, elt := range n.Elts {
+ if kv, ok := elt.(*ast.KeyValueExpr); ok {
+ ast.Inspect(kv.Value, visit)
+ } else {
+ ast.Inspect(elt, visit)
+ }
+ }
+ return visit(nil) // pop stack, don't descend
+ }
+ }
+ return true
+ }
+
+ for _, f := range info.Files {
+ ast.Inspect(f, visit)
+ if len(stack) != 0 {
+ panic(stack)
+ }
+ if !ok {
+ break
+ }
+ }
+ return ok
+}
+
+// enclosingBlock returns the innermost block enclosing the specified
+// AST node, specified in the form of a path from the root of the file,
+// [file...n].
+func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope {
+ for i := range stack {
+ n := stack[len(stack)-1-i]
+ // For some reason, go/types always associates a
+ // function's scope with its FuncType.
+ // TODO(adonovan): feature or a bug?
+ switch f := n.(type) {
+ case *ast.FuncDecl:
+ n = f.Type
+ case *ast.FuncLit:
+ n = f.Type
+ }
+ if b := info.Scopes[n]; b != nil {
+ return b
+ }
+ }
+ panic("no Scope for *ast.File")
+}
+
func (r *renamer) checkLabel(label *types.Label) {
// Check there are no identical labels in the function's label block.
// (Label blocks don't nest, so this is easy.)
diff --git a/go/src/golang.org/x/tools/refactor/rename/check14.go b/go/src/golang.org/x/tools/refactor/rename/check14.go
new file mode 100644
index 0000000..3f1f7ab
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/rename/check14.go
@@ -0,0 +1,860 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package rename
+
+// This file defines the safety checks for each kind of renaming.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/refactor/satisfy"
+)
+
+// errorf reports an error (e.g. conflict) and prevents file modification.
+func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) {
+ r.hadConflicts = true
+ reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(format, args...))
+}
+
+// check performs safety checks of the renaming of the 'from' object to r.to.
+func (r *renamer) check(from types.Object) {
+ if r.objsToUpdate[from] {
+ return
+ }
+ r.objsToUpdate[from] = true
+
+ // NB: order of conditions is important.
+ if from_, ok := from.(*types.PkgName); ok {
+ r.checkInFileBlock(from_)
+ } else if from_, ok := from.(*types.Label); ok {
+ r.checkLabel(from_)
+ } else if isPackageLevel(from) {
+ r.checkInPackageBlock(from)
+ } else if v, ok := from.(*types.Var); ok && v.IsField() {
+ r.checkStructField(v)
+ } else if f, ok := from.(*types.Func); ok && recv(f) != nil {
+ r.checkMethod(f)
+ } else if isLocal(from) {
+ r.checkInLocalScope(from)
+ } else {
+ r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
+ objectKind(from), from)
+ }
+}
+
+// checkInFileBlock performs safety checks for renames of objects in the file block,
+// i.e. imported package names.
+func (r *renamer) checkInFileBlock(from *types.PkgName) {
+ // Check import name is not "init".
+ if r.to == "init" {
+ r.errorf(from.Pos(), "%q is not a valid imported package name", r.to)
+ }
+
+ // Check for conflicts between file and package block.
+ if prev := from.Pkg().Scope().Lookup(r.to); prev != nil {
+ r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
+ objectKind(from), from.Name(), r.to)
+ r.errorf(prev.Pos(), "\twith this package member %s",
+ objectKind(prev))
+ return // since checkInPackageBlock would report redundant errors
+ }
+
+ // Check for conflicts in lexical scope.
+ r.checkInLexicalScope(from, r.packages[from.Pkg()])
+
+ // Finally, modify ImportSpec syntax to add or remove the Name as needed.
+ info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
+ if from.Imported().Name() == r.to {
+ // ImportSpec.Name not needed
+ path[1].(*ast.ImportSpec).Name = nil
+ } else {
+ // ImportSpec.Name needed
+ if spec := path[1].(*ast.ImportSpec); spec.Name == nil {
+ spec.Name = &ast.Ident{NamePos: spec.Path.Pos(), Name: r.to}
+ info.Defs[spec.Name] = from
+ }
+ }
+}
+
+// checkInPackageBlock performs safety checks for renames of
+// func/var/const/type objects in the package block.
+func (r *renamer) checkInPackageBlock(from types.Object) {
+ // Check that there are no references to the name from another
+ // package if the renaming would make it unexported.
+ if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
+ for pkg, info := range r.packages {
+ if pkg == from.Pkg() {
+ continue
+ }
+ if id := someUse(info, from); id != nil &&
+ !r.checkExport(id, pkg, from) {
+ break
+ }
+ }
+ }
+
+ info := r.packages[from.Pkg()]
+
+ // Check that in the package block, "init" is a function, and never referenced.
+ if r.to == "init" {
+ kind := objectKind(from)
+ if kind == "func" {
+ // Reject if intra-package references to it exist.
+ for id, obj := range info.Uses {
+ if obj == from {
+ r.errorf(from.Pos(),
+ "renaming this func %q to %q would make it a package initializer",
+ from.Name(), r.to)
+ r.errorf(id.Pos(), "\tbut references to it exist")
+ break
+ }
+ }
+ } else {
+ r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
+ kind, r.to)
+ }
+ }
+
+ // Check for conflicts between package block and all file blocks.
+ for _, f := range info.Files {
+ fileScope := info.Info.Scopes[f]
+ b, prev := fileScope.LookupParent(r.to, token.NoPos)
+ if b == fileScope {
+ r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
+ objectKind(from), from.Name(), r.to)
+ r.errorf(prev.Pos(), "\twith this %s",
+ objectKind(prev))
+ return // since checkInPackageBlock would report redundant errors
+ }
+ }
+
+ // Check for conflicts in lexical scope.
+ if from.Exported() {
+ for _, info := range r.packages {
+ r.checkInLexicalScope(from, info)
+ }
+ } else {
+ r.checkInLexicalScope(from, info)
+ }
+}
+
+func (r *renamer) checkInLocalScope(from types.Object) {
+ info := r.packages[from.Pkg()]
+
+ // Is this object an implicit local var for a type switch?
+ // Each case has its own var, whose position is the decl of y,
+ // but Ident in that decl does not appear in the Uses map.
+ //
+ // switch y := x.(type) { // Defs[Ident(y)] is undefined
+ // case int: print(y) // Implicits[CaseClause(int)] = Var(y_int)
+ // case string: print(y) // Implicits[CaseClause(string)] = Var(y_string)
+ // }
+ //
+ var isCaseVar bool
+ for syntax, obj := range info.Implicits {
+ if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() {
+ isCaseVar = true
+ r.check(obj)
+ }
+ }
+
+ r.checkInLexicalScope(from, info)
+
+ // Finally, if this was a type switch, change the variable y.
+ if isCaseVar {
+ _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
+ path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...]
+ }
+}
+
+// checkInLexicalScope performs safety checks that a renaming does not
+// change the lexical reference structure of the specified package.
+//
+// For objects in lexical scope, there are three kinds of conflicts:
+// same-, sub-, and super-block conflicts. We will illustrate all three
+// using this example:
+//
+// var x int
+// var z int
+//
+// func f(y int) {
+// print(x)
+// print(y)
+// }
+//
+// Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
+// with the new name already exists, defined in the same lexical block
+// as the old object.
+//
+// Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
+// a reference to x from within (what would become) a hole in its scope.
+// The definition of y in an (inner) sub-block would cast a shadow in
+// the scope of the renamed variable.
+//
+// Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the
+// converse situation: there is an existing definition of the new name
+// (x) in an (enclosing) super-block, and the renaming would create a
+// hole in its scope, within which there exist references to it. The
+// new name casts a shadow in scope of the existing definition of x in
+// the super-block.
+//
+// Removing the old name (and all references to it) is always safe, and
+// requires no checks.
+//
+func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) {
+ b := from.Parent() // the block defining the 'from' object
+ if b != nil {
+ toBlock, to := b.LookupParent(r.to, from.Parent().End())
+ if toBlock == b {
+ // same-block conflict
+ r.errorf(from.Pos(), "renaming this %s %q to %q",
+ objectKind(from), from.Name(), r.to)
+ r.errorf(to.Pos(), "\tconflicts with %s in same block",
+ objectKind(to))
+ return
+ } else if toBlock != nil {
+ // Check for super-block conflict.
+ // The name r.to is defined in a superblock.
+ // Is that name referenced from within this block?
+ forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool {
+ _, obj := lexicalLookup(block, from.Name(), id.Pos())
+ if obj == from {
+ // super-block conflict
+ r.errorf(from.Pos(), "renaming this %s %q to %q",
+ objectKind(from), from.Name(), r.to)
+ r.errorf(id.Pos(), "\twould shadow this reference")
+ r.errorf(to.Pos(), "\tto the %s declared here",
+ objectKind(to))
+ return false // stop
+ }
+ return true
+ })
+ }
+ }
+
+ // Check for sub-block conflict.
+ // Is there an intervening definition of r.to between
+ // the block defining 'from' and some reference to it?
+ forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool {
+ // Find the block that defines the found reference.
+ // It may be an ancestor.
+ fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos())
+
+ // See what r.to would resolve to in the same scope.
+ toBlock, to := lexicalLookup(block, r.to, id.Pos())
+ if to != nil {
+ // sub-block conflict
+ if deeper(toBlock, fromBlock) {
+ r.errorf(from.Pos(), "renaming this %s %q to %q",
+ objectKind(from), from.Name(), r.to)
+ r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
+ r.errorf(to.Pos(), "\tby this intervening %s definition",
+ objectKind(to))
+ return false // stop
+ }
+ }
+ return true
+ })
+
+ // Renaming a type that is used as an embedded field
+ // requires renaming the field too. e.g.
+ // type T int // if we rename this to U..
+ // var s struct {T}
+ // print(s.T) // ...this must change too
+ if _, ok := from.(*types.TypeName); ok {
+ for id, obj := range info.Uses {
+ if obj == from {
+ if field := info.Defs[id]; field != nil {
+ r.check(field)
+ }
+ }
+ }
+ }
+}
+
+// lexicalLookup is like (*types.Scope).LookupParent but respects the
+// environment visible at pos. It assumes the relative position
+// information is correct with each file.
+func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) {
+ for b := block; b != nil; b = b.Parent() {
+ obj := b.Lookup(name)
+ // The scope of a package-level object is the entire package,
+ // so ignore pos in that case.
+ // No analogous clause is needed for file-level objects
+ // since no reference can appear before an import decl.
+ if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) {
+ return b, obj
+ }
+ }
+ return nil, nil
+}
+
+// deeper reports whether block x is lexically deeper than y.
+func deeper(x, y *types.Scope) bool {
+ if x == y || x == nil {
+ return false
+ } else if y == nil {
+ return true
+ } else {
+ return deeper(x.Parent(), y.Parent())
+ }
+}
+
+// forEachLexicalRef calls fn(id, block) for each identifier id in package
+// info that is a reference to obj in lexical scope. block is the
+// lexical block enclosing the reference. If fn returns false the
+// iteration is terminated and findLexicalRefs returns false.
+func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
+ ok := true
+ var stack []ast.Node
+
+ var visit func(n ast.Node) bool
+ visit = func(n ast.Node) bool {
+ if n == nil {
+ stack = stack[:len(stack)-1] // pop
+ return false
+ }
+ if !ok {
+ return false // bail out
+ }
+
+ stack = append(stack, n) // push
+ switch n := n.(type) {
+ case *ast.Ident:
+ if info.Uses[n] == obj {
+ block := enclosingBlock(&info.Info, stack)
+ if !fn(n, block) {
+ ok = false
+ }
+ }
+ return visit(nil) // pop stack
+
+ case *ast.SelectorExpr:
+ // don't visit n.Sel
+ ast.Inspect(n.X, visit)
+ return visit(nil) // pop stack, don't descend
+
+ case *ast.CompositeLit:
+ // Handle recursion ourselves for struct literals
+ // so we don't visit field identifiers.
+ tv := info.Types[n]
+ if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
+ if n.Type != nil {
+ ast.Inspect(n.Type, visit)
+ }
+ for _, elt := range n.Elts {
+ if kv, ok := elt.(*ast.KeyValueExpr); ok {
+ ast.Inspect(kv.Value, visit)
+ } else {
+ ast.Inspect(elt, visit)
+ }
+ }
+ return visit(nil) // pop stack, don't descend
+ }
+ }
+ return true
+ }
+
+ for _, f := range info.Files {
+ ast.Inspect(f, visit)
+ if len(stack) != 0 {
+ panic(stack)
+ }
+ if !ok {
+ break
+ }
+ }
+ return ok
+}
+
+// enclosingBlock returns the innermost block enclosing the specified
+// AST node, specified in the form of a path from the root of the file,
+// [file...n].
+func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope {
+ for i := range stack {
+ n := stack[len(stack)-1-i]
+ // For some reason, go/types always associates a
+ // function's scope with its FuncType.
+ // TODO(adonovan): feature or a bug?
+ switch f := n.(type) {
+ case *ast.FuncDecl:
+ n = f.Type
+ case *ast.FuncLit:
+ n = f.Type
+ }
+ if b := info.Scopes[n]; b != nil {
+ return b
+ }
+ }
+ panic("no Scope for *ast.File")
+}
+
+func (r *renamer) checkLabel(label *types.Label) {
+ // Check there are no identical labels in the function's label block.
+ // (Label blocks don't nest, so this is easy.)
+ if prev := label.Parent().Lookup(r.to); prev != nil {
+ r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name())
+ r.errorf(prev.Pos(), "\twould conflict with this one")
+ }
+}
+
+// checkStructField checks that the field renaming will not cause
+// conflicts at its declaration, or ambiguity or changes to any selection.
+func (r *renamer) checkStructField(from *types.Var) {
+ // Check that the struct declaration is free of field conflicts,
+ // and field/method conflicts.
+
+ // go/types offers no easy way to get from a field (or interface
+ // method) to its declaring struct (or interface), so we must
+ // ascend the AST.
+ info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
+ // path matches this pattern:
+ // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]
+
+ // Ascend to FieldList.
+ var i int
+ for {
+ if _, ok := path[i].(*ast.FieldList); ok {
+ break
+ }
+ i++
+ }
+ i++
+ tStruct := path[i].(*ast.StructType)
+ i++
+ // Ascend past parens (unlikely).
+ for {
+ _, ok := path[i].(*ast.ParenExpr)
+ if !ok {
+ break
+ }
+ i++
+ }
+ if spec, ok := path[i].(*ast.TypeSpec); ok {
+ // This struct is also a named type.
+ // We must check for direct (non-promoted) field/field
+ // and method/field conflicts.
+ named := info.Defs[spec.Name].Type()
+ prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to)
+ if len(indices) == 1 {
+ r.errorf(from.Pos(), "renaming this field %q to %q",
+ from.Name(), r.to)
+ r.errorf(prev.Pos(), "\twould conflict with this %s",
+ objectKind(prev))
+ return // skip checkSelections to avoid redundant errors
+ }
+ } else {
+ // This struct is not a named type.
+ // We need only check for direct (non-promoted) field/field conflicts.
+ T := info.Types[tStruct].Type.Underlying().(*types.Struct)
+ for i := 0; i < T.NumFields(); i++ {
+ if prev := T.Field(i); prev.Name() == r.to {
+ r.errorf(from.Pos(), "renaming this field %q to %q",
+ from.Name(), r.to)
+ r.errorf(prev.Pos(), "\twould conflict with this field")
+ return // skip checkSelections to avoid redundant errors
+ }
+ }
+ }
+
+ // Renaming an anonymous field requires renaming the type too. e.g.
+ // print(s.T) // if we rename T to U,
+ // type T int // this and
+ // var s struct {T} // this must change too.
+ if from.Anonymous() {
+ if named, ok := from.Type().(*types.Named); ok {
+ r.check(named.Obj())
+ } else if named, ok := deref(from.Type()).(*types.Named); ok {
+ r.check(named.Obj())
+ }
+ }
+
+ // Check integrity of existing (field and method) selections.
+ r.checkSelections(from)
+}
+
+// checkSelection checks that all uses and selections that resolve to
+// the specified object would continue to do so after the renaming.
+func (r *renamer) checkSelections(from types.Object) {
+ for pkg, info := range r.packages {
+ if id := someUse(info, from); id != nil {
+ if !r.checkExport(id, pkg, from) {
+ return
+ }
+ }
+
+ for syntax, sel := range info.Selections {
+ // There may be extant selections of only the old
+ // name or only the new name, so we must check both.
+ // (If neither, the renaming is sound.)
+ //
+ // In both cases, we wish to compare the lengths
+ // of the implicit field path (Selection.Index)
+ // to see if the renaming would change it.
+ //
+ // If a selection that resolves to 'from', when renamed,
+ // would yield a path of the same or shorter length,
+ // this indicates ambiguity or a changed referent,
+ // analogous to same- or sub-block lexical conflict.
+ //
+ // If a selection using the name 'to' would
+ // yield a path of the same or shorter length,
+ // this indicates ambiguity or shadowing,
+ // analogous to same- or super-block lexical conflict.
+
+ // TODO(adonovan): fix: derive from Types[syntax.X].Mode
+ // TODO(adonovan): test with pointer, value, addressable value.
+ isAddressable := true
+
+ if sel.Obj() == from {
+ if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil {
+ // Renaming this existing selection of
+ // 'from' may block access to an existing
+ // type member named 'to'.
+ delta := len(indices) - len(sel.Index())
+ if delta > 0 {
+ continue // no ambiguity
+ }
+ r.selectionConflict(from, delta, syntax, obj)
+ return
+ }
+
+ } else if sel.Obj().Name() == r.to {
+ if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from {
+ // Renaming 'from' may cause this existing
+ // selection of the name 'to' to change
+ // its meaning.
+ delta := len(indices) - len(sel.Index())
+ if delta > 0 {
+ continue // no ambiguity
+ }
+ r.selectionConflict(from, -delta, syntax, sel.Obj())
+ return
+ }
+ }
+ }
+ }
+}
+
+func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) {
+ r.errorf(from.Pos(), "renaming this %s %q to %q",
+ objectKind(from), from.Name(), r.to)
+
+ switch {
+ case delta < 0:
+ // analogous to sub-block conflict
+ r.errorf(syntax.Sel.Pos(),
+ "\twould change the referent of this selection")
+ r.errorf(obj.Pos(), "\tof this %s", objectKind(obj))
+ case delta == 0:
+ // analogous to same-block conflict
+ r.errorf(syntax.Sel.Pos(),
+ "\twould make this reference ambiguous")
+ r.errorf(obj.Pos(), "\twith this %s", objectKind(obj))
+ case delta > 0:
+ // analogous to super-block conflict
+ r.errorf(syntax.Sel.Pos(),
+ "\twould shadow this selection")
+ r.errorf(obj.Pos(), "\tof the %s declared here",
+ objectKind(obj))
+ }
+}
+
+// checkMethod performs safety checks for renaming a method.
+// There are three hazards:
+// - declaration conflicts
+// - selection ambiguity/changes
+// - entailed renamings of assignable concrete/interface types.
+// We reject renamings initiated at concrete methods if it would
+// change the assignability relation. For renamings of abstract
+// methods, we rename all methods transitively coupled to it via
+// assignability.
+func (r *renamer) checkMethod(from *types.Func) {
+ // e.g. error.Error
+ if from.Pkg() == nil {
+ r.errorf(from.Pos(), "you cannot rename built-in method %s", from)
+ return
+ }
+
+ // ASSIGNABILITY: We reject renamings of concrete methods that
+ // would break a 'satisfy' constraint; but renamings of abstract
+ // methods are allowed to proceed, and we rename affected
+ // concrete and abstract methods as necessary. It is the
+ // initial method that determines the policy.
+
+ // Check for conflict at point of declaration.
+ // Check to ensure preservation of assignability requirements.
+ R := recv(from).Type()
+ if isInterface(R) {
+ // Abstract method
+
+ // declaration
+ prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to)
+ if prev != nil {
+ r.errorf(from.Pos(), "renaming this interface method %q to %q",
+ from.Name(), r.to)
+ r.errorf(prev.Pos(), "\twould conflict with this method")
+ return
+ }
+
+ // Check all interfaces that embed this one for
+ // declaration conflicts too.
+ for _, info := range r.packages {
+ // Start with named interface types (better errors)
+ for _, obj := range info.Defs {
+ if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
+ f, _, _ := types.LookupFieldOrMethod(
+ obj.Type(), false, from.Pkg(), from.Name())
+ if f == nil {
+ continue
+ }
+ t, _, _ := types.LookupFieldOrMethod(
+ obj.Type(), false, from.Pkg(), r.to)
+ if t == nil {
+ continue
+ }
+ r.errorf(from.Pos(), "renaming this interface method %q to %q",
+ from.Name(), r.to)
+ r.errorf(t.Pos(), "\twould conflict with this method")
+ r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name())
+ }
+ }
+
+ // Now look at all literal interface types (includes named ones again).
+ for e, tv := range info.Types {
+ if e, ok := e.(*ast.InterfaceType); ok {
+ _ = e
+ _ = tv.Type.(*types.Interface)
+ // TODO(adonovan): implement same check as above.
+ }
+ }
+ }
+
+ // assignability
+ //
+ // Find the set of concrete or abstract methods directly
+ // coupled to abstract method 'from' by some
+ // satisfy.Constraint, and rename them too.
+ for key := range r.satisfy() {
+ // key = (lhs, rhs) where lhs is always an interface.
+
+ lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
+ if lsel == nil {
+ continue
+ }
+ rmethods := r.msets.MethodSet(key.RHS)
+ rsel := rmethods.Lookup(from.Pkg(), from.Name())
+ if rsel == nil {
+ continue
+ }
+
+ // If both sides have a method of this name,
+ // and one of them is m, the other must be coupled.
+ var coupled *types.Func
+ switch from {
+ case lsel.Obj():
+ coupled = rsel.Obj().(*types.Func)
+ case rsel.Obj():
+ coupled = lsel.Obj().(*types.Func)
+ default:
+ continue
+ }
+
+ // We must treat concrete-to-interface
+ // constraints like an implicit selection C.f of
+ // each interface method I.f, and check that the
+ // renaming leaves the selection unchanged and
+ // unambiguous.
+ //
+ // Fun fact: the implicit selection of C.f
+ // type I interface{f()}
+ // type C struct{I}
+ // func (C) g()
+ // var _ I = C{} // here
+ // yields abstract method I.f. This can make error
+ // messages less than obvious.
+ //
+ if !isInterface(key.RHS) {
+ // The logic below was derived from checkSelections.
+
+ rtosel := rmethods.Lookup(from.Pkg(), r.to)
+ if rtosel != nil {
+ rto := rtosel.Obj().(*types.Func)
+ delta := len(rsel.Index()) - len(rtosel.Index())
+ if delta < 0 {
+ continue // no ambiguity
+ }
+
+ // TODO(adonovan): record the constraint's position.
+ keyPos := token.NoPos
+
+ r.errorf(from.Pos(), "renaming this method %q to %q",
+ from.Name(), r.to)
+ if delta == 0 {
+ // analogous to same-block conflict
+ r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous",
+ r.to, key.RHS, key.LHS)
+ r.errorf(rto.Pos(), "\twith (%s).%s",
+ recv(rto).Type(), r.to)
+ } else {
+ // analogous to super-block conflict
+ r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s",
+ r.to, key.RHS, key.LHS)
+ r.errorf(coupled.Pos(), "\tfrom (%s).%s",
+ recv(coupled).Type(), r.to)
+ r.errorf(rto.Pos(), "\tto (%s).%s",
+ recv(rto).Type(), r.to)
+ }
+ return // one error is enough
+ }
+ }
+
+ if !r.changeMethods {
+ // This should be unreachable.
+ r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from)
+ r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled)
+ r.errorf(from.Pos(), "\tPlease file a bug report")
+ return
+ }
+
+ // Rename the coupled method to preserve assignability.
+ r.check(coupled)
+ }
+ } else {
+ // Concrete method
+
+ // declaration
+ prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to)
+ if prev != nil && len(indices) == 1 {
+ r.errorf(from.Pos(), "renaming this method %q to %q",
+ from.Name(), r.to)
+ r.errorf(prev.Pos(), "\twould conflict with this %s",
+ objectKind(prev))
+ return
+ }
+
+ // assignability
+ //
+ // Find the set of abstract methods coupled to concrete
+ // method 'from' by some satisfy.Constraint, and rename
+ // them too.
+ //
+ // Coupling may be indirect, e.g. I.f <-> C.f via type D.
+ //
+ // type I interface {f()}
+ // type C int
+ // type (C) f()
+ // type D struct{C}
+ // var _ I = D{}
+ //
+ for key := range r.satisfy() {
+ // key = (lhs, rhs) where lhs is always an interface.
+ if isInterface(key.RHS) {
+ continue
+ }
+ rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
+ if rsel == nil || rsel.Obj() != from {
+ continue // rhs does not have the method
+ }
+ lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
+ if lsel == nil {
+ continue
+ }
+ imeth := lsel.Obj().(*types.Func)
+
+ // imeth is the abstract method (e.g. I.f)
+ // and key.RHS is the concrete coupling type (e.g. D).
+ if !r.changeMethods {
+ r.errorf(from.Pos(), "renaming this method %q to %q",
+ from.Name(), r.to)
+ var pos token.Pos
+ var iface string
+
+ I := recv(imeth).Type()
+ if named, ok := I.(*types.Named); ok {
+ pos = named.Obj().Pos()
+ iface = "interface " + named.Obj().Name()
+ } else {
+ pos = from.Pos()
+ iface = I.String()
+ }
+ r.errorf(pos, "\twould make %s no longer assignable to %s",
+ key.RHS, iface)
+ r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)",
+ I, from.Name())
+ return // one error is enough
+ }
+
+ // Rename the coupled interface method to preserve assignability.
+ r.check(imeth)
+ }
+ }
+
+ // Check integrity of existing (field and method) selections.
+ // We skip this if there were errors above, to avoid redundant errors.
+ r.checkSelections(from)
+}
+
+func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool {
+ // Reject cross-package references if r.to is unexported.
+ // (Such references may be qualified identifiers or field/method
+ // selections.)
+ if !ast.IsExported(r.to) && pkg != from.Pkg() {
+ r.errorf(from.Pos(),
+ "renaming this %s %q to %q would make it unexported",
+ objectKind(from), from.Name(), r.to)
+ r.errorf(id.Pos(), "\tbreaking references from packages such as %q",
+ pkg.Path())
+ return false
+ }
+ return true
+}
+
+// satisfy returns the set of interface satisfaction constraints.
+func (r *renamer) satisfy() map[satisfy.Constraint]bool {
+ if r.satisfyConstraints == nil {
+ // Compute on demand: it's expensive.
+ var f satisfy.Finder
+ for _, info := range r.packages {
+ f.Find(&info.Info, info.Files)
+ }
+ r.satisfyConstraints = f.Result
+ }
+ return r.satisfyConstraints
+}
+
+// -- helpers ----------------------------------------------------------
+
+// recv returns the method's receiver.
+func recv(meth *types.Func) *types.Var {
+ return meth.Type().(*types.Signature).Recv()
+}
+
+// someUse returns an arbitrary use of obj within info.
+func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident {
+ for id, o := range info.Uses {
+ if o == obj {
+ return id
+ }
+ }
+ return nil
+}
+
+// -- Plundered from golang.org/x/tools/go/ssa -----------------
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }
+
+func deref(typ types.Type) types.Type {
+ if p, _ := typ.(*types.Pointer); p != nil {
+ return p.Elem()
+ }
+ return typ
+}
diff --git a/go/src/golang.org/x/tools/refactor/rename/rename.el b/go/src/golang.org/x/tools/refactor/rename/go-rename.el
similarity index 83%
rename from go/src/golang.org/x/tools/refactor/rename/rename.el
rename to go/src/golang.org/x/tools/refactor/rename/go-rename.el
index 139bb47..8faecc6 100644
--- a/go/src/golang.org/x/tools/refactor/rename/rename.el
+++ b/go/src/golang.org/x/tools/refactor/rename/go-rename.el
@@ -1,16 +1,25 @@
-;;; Copyright 2014 The Go Authors. All rights reserved.
-;;; Use of this source code is governed by a BSD-style
-;;; license that can be found in the LICENSE file.
-;;;
-;;; Integration of the 'gorename' tool into Emacs.
-;;;
-;;; To install:
-;;; % go get golang.org/x/tools/cmd/gorename
-;;; % go build golang.org/x/tools/cmd/gorename
-;;; % mv gorename $HOME/bin/ # or elsewhere on $PATH
-;;;
-;;; The go-rename-command variable can be customized to specify an
-;;; alternative location for the installed command.
+;;; go-rename.el --- Integration of the 'gorename' tool into Emacs.
+
+;; Copyright 2014 The Go Authors. All rights reserved.
+;; Use of this source code is governed by a BSD-style
+;; license that can be found in the LICENSE file.
+
+;; Version: 0.1
+;; Package-Requires: ((go-mode "1.3.1"))
+;; Keywords: tools
+
+;;; Commentary:
+
+;; To install:
+
+;; % go get golang.org/x/tools/cmd/gorename
+;; % go build golang.org/x/tools/cmd/gorename
+;; % mv gorename $HOME/bin/ # or elsewhere on $PATH
+
+;; The go-rename-command variable can be customized to specify an
+;; alternative location for the installed command.
+
+;;; Code:
(require 'compile)
(require 'go-mode)
@@ -25,6 +34,7 @@
:type 'string
:group 'go-rename)
+;;;###autoload
(defun go-rename (new-name &optional force)
"Rename the entity denoted by the identifier at point, using
the `gorename' tool. With FORCE, call `gorename' with the
@@ -94,3 +104,5 @@
(buffer-substring (point-min) (point-max))))
(provide 'go-rename)
+
+;;; go-rename.el ends here
diff --git a/go/src/golang.org/x/tools/refactor/rename/mvpkg.go b/go/src/golang.org/x/tools/refactor/rename/mvpkg.go
index bb0d9b0..927195c 100644
--- a/go/src/golang.org/x/tools/refactor/rename/mvpkg.go
+++ b/go/src/golang.org/x/tools/refactor/rename/mvpkg.go
@@ -17,6 +17,8 @@
"fmt"
"go/ast"
"go/build"
+ "go/format"
+ "go/token"
"log"
"os"
"os/exec"
@@ -238,6 +240,22 @@
}
newName := filepath.Base(m.to)
for _, f := range pkg.Files {
+ // Update all import comments.
+ for _, cg := range f.Comments {
+ c := cg.List[0]
+ if c.Slash >= f.Name.End() &&
+ sameLine(m.iprog.Fset, c.Slash, f.Name.End()) &&
+ (f.Decls == nil || c.Slash < f.Decls[0].Pos()) {
+ if strings.HasPrefix(c.Text, `// import "`) {
+ c.Text = `// import "` + m.to + `"`
+ break
+ }
+ if strings.HasPrefix(c.Text, `/* import "`) {
+ c.Text = `/* import "` + m.to + `" */`
+ break
+ }
+ }
+ }
f.Name.Name = newName // change package decl
filesToUpdate[f] = true
}
@@ -304,8 +322,13 @@
}
for f := range filesToUpdate {
+ var buf bytes.Buffer
+ if err := format.Node(&buf, m.iprog.Fset, f); err != nil {
+ log.Printf("failed to pretty-print syntax tree: %v", err)
+ continue
+ }
tokenFile := m.iprog.Fset.File(f.Pos())
- rewriteFile(m.iprog.Fset, f, tokenFile.Name())
+ writeFile(tokenFile.Name(), buf.Bytes())
}
// Move the directories.
@@ -338,6 +361,11 @@
return moveDirectory(m.fromDir, m.toDir)
}
+// sameLine reports whether two positions in the same file are on the same line.
+func sameLine(fset *token.FileSet, x, y token.Pos) bool {
+ return fset.Position(x).Line == fset.Position(y).Line
+}
+
var moveDirectory = func(from, to string) error {
return os.Rename(from, to)
}
diff --git a/go/src/golang.org/x/tools/refactor/rename/mvpkg_test.go b/go/src/golang.org/x/tools/refactor/rename/mvpkg_test.go
index 61fa354..e07b6b4 100644
--- a/go/src/golang.org/x/tools/refactor/rename/mvpkg_test.go
+++ b/go/src/golang.org/x/tools/refactor/rename/mvpkg_test.go
@@ -5,12 +5,8 @@
package rename
import (
- "bytes"
"fmt"
- "go/ast"
"go/build"
- "go/format"
- "go/token"
"io/ioutil"
"path/filepath"
"regexp"
@@ -79,12 +75,8 @@
ctxt := test.ctxt
got := make(map[string]string)
- rewriteFile = func(fset *token.FileSet, f *ast.File, orig string) error {
- var out bytes.Buffer
- if err := format.Node(&out, fset, f); err != nil {
- return err
- }
- got[orig] = out.String()
+ writeFile = func(filename string, content []byte) error {
+ got[filename] = string(content)
return nil
}
moveDirectory = func(from, to string) error {
@@ -238,6 +230,43 @@
`,
},
},
+ // package import comments
+ {
+ ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}),
+ from: "foo", to: "bar",
+ want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar"
+`},
+ },
+ {
+ ctxt: fakeContext(map[string][]string{"foo": {`package foo /* import "baz" */`}}),
+ from: "foo", to: "bar",
+ want: map[string]string{"/go/src/bar/0.go": `package bar /* import "bar" */
+`},
+ },
+ {
+ ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}),
+ from: "foo", to: "bar",
+ want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar"
+`},
+ },
+ {
+ ctxt: fakeContext(map[string][]string{"foo": {`package foo
+// import " this is not an import comment`}}),
+ from: "foo", to: "bar",
+ want: map[string]string{"/go/src/bar/0.go": `package bar
+
+// import " this is not an import comment
+`},
+ },
+ {
+ ctxt: fakeContext(map[string][]string{"foo": {`package foo
+/* import " this is not an import comment */`}}),
+ from: "foo", to: "bar",
+ want: map[string]string{"/go/src/bar/0.go": `package bar
+
+/* import " this is not an import comment */
+`},
+ },
}
for _, test := range tests {
@@ -267,12 +296,8 @@
}
got[path] = string(bytes)
})
- rewriteFile = func(fset *token.FileSet, f *ast.File, orig string) error {
- var out bytes.Buffer
- if err := format.Node(&out, fset, f); err != nil {
- return err
- }
- got[orig] = out.String()
+ writeFile = func(filename string, content []byte) error {
+ got[filename] = string(content)
return nil
}
moveDirectory = func(from, to string) error {
diff --git a/go/src/golang.org/x/tools/refactor/rename/rename.go b/go/src/golang.org/x/tools/refactor/rename/rename.go
index a028c21..be47701 100644
--- a/go/src/golang.org/x/tools/refactor/rename/rename.go
+++ b/go/src/golang.org/x/tools/refactor/rename/rename.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package rename contains the implementation of the 'gorename' command
// whose main function is in golang.org/x/tools/cmd/gorename.
// See the Usage constant for the command documentation.
@@ -16,15 +18,18 @@
"go/format"
"go/parser"
"go/token"
+ "go/types"
+ "io"
"io/ioutil"
+ "log"
"os"
+ "os/exec"
"path"
"sort"
"strconv"
"strings"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/refactor/importgraph"
"golang.org/x/tools/refactor/satisfy"
@@ -76,7 +81,7 @@
(In due course this bug will be fixed by moving certain
analyses into the type-checker.)
--dryrun causes the tool to report conflicts but not update any files.
+-d display diffs instead of rewriting files
-v enables verbose logging.
@@ -96,11 +101,11 @@
Examples:
-% gorename -offset file.go:#123 -to foo
+$ gorename -offset file.go:#123 -to foo
Rename the object whose identifier is at byte offset 123 within file file.go.
-% gorename -from \"bytes\".Buffer.Len' -to Size
+$ gorename -from '"bytes".Buffer.Len' -to Size
Rename the "Len" method of the *bytes.Buffer type to "Size".
@@ -137,8 +142,12 @@
// It may even cause gorename to crash. TODO(adonovan): fix that.
Force bool
- // DryRun causes the tool to report conflicts but not update any files.
- DryRun bool
+ // Diff causes the tool to display diffs instead of rewriting files.
+ Diff bool
+
+ // DiffCmd specifies the diff command used by the -d feature.
+ // (The command must accept a -u flag and two filename arguments.)
+ DiffCmd = "diff"
// ConflictError is returned by Main when it aborts the renaming due to conflicts.
// (It is distinguished because the interesting errors are the conflicts themselves.)
@@ -148,6 +157,8 @@
Verbose bool
)
+var stdout io.Writer = os.Stdout
+
type renamer struct {
iprog *loader.Program
objsToUpdate map[types.Object]bool
@@ -212,6 +223,11 @@
return fmt.Errorf("-to %q: not a valid identifier", to)
}
+ if Diff {
+ defer func(saved func(string, []byte) error) { writeFile = saved }(writeFile)
+ writeFile = diff
+ }
+
var spec *spec
var err error
if fromFlag != "" {
@@ -248,7 +264,7 @@
// package defining the object, plus their tests.
if Verbose {
- fmt.Fprintln(os.Stderr, "Potentially global renaming; scanning workspace...")
+ log.Print("Potentially global renaming; scanning workspace...")
}
// Scan the workspace and build the import graph.
@@ -329,10 +345,6 @@
if r.hadConflicts && !Force {
return ConflictError
}
- if DryRun {
- // TODO(adonovan): print the delta?
- return nil
- }
return r.update()
}
@@ -361,7 +373,7 @@
}
sort.Strings(list)
for _, pkg := range list {
- fmt.Fprintf(os.Stderr, "Loading package: %s\n", pkg)
+ log.Printf("Loading package: %s", pkg)
}
}
@@ -433,21 +445,30 @@
npkgs++
first = false
if Verbose {
- fmt.Fprintf(os.Stderr, "Updating package %s\n",
- info.Pkg.Path())
+ log.Printf("Updating package %s", info.Pkg.Path())
}
}
- if err := rewriteFile(r.iprog.Fset, f, tokenFile.Name()); err != nil {
- fmt.Fprintf(os.Stderr, "gorename: %s\n", err)
+
+ filename := tokenFile.Name()
+ var buf bytes.Buffer
+ if err := format.Node(&buf, r.iprog.Fset, f); err != nil {
+ log.Printf("failed to pretty-print syntax tree: %v", err)
+ nerrs++
+ continue
+ }
+ if err := writeFile(filename, buf.Bytes()); err != nil {
+ log.Print(err)
nerrs++
}
}
}
}
- fmt.Fprintf(os.Stderr, "Renamed %d occurrence%s in %d file%s in %d package%s.\n",
- nidents, plural(nidents),
- len(filesToUpdate), plural(len(filesToUpdate)),
- npkgs, plural(npkgs))
+ if !Diff {
+ fmt.Printf("Renamed %d occurrence%s in %d file%s in %d package%s.\n",
+ nidents, plural(nidents),
+ len(filesToUpdate), plural(len(filesToUpdate)),
+ npkgs, plural(npkgs))
+ }
if nerrs > 0 {
return fmt.Errorf("failed to rewrite %d file%s", nerrs, plural(nerrs))
}
@@ -461,15 +482,29 @@
return ""
}
-var rewriteFile = func(fset *token.FileSet, f *ast.File, filename string) (err error) {
- // TODO(adonovan): print packages and filenames in a form useful
- // to editors (so they can reload files).
- if Verbose {
- fmt.Fprintf(os.Stderr, "\t%s\n", filename)
+// writeFile is a seam for testing and for the -d flag.
+var writeFile = reallyWriteFile
+
+func reallyWriteFile(filename string, content []byte) error {
+ return ioutil.WriteFile(filename, content, 0644)
+}
+
+func diff(filename string, content []byte) error {
+ renamed := fmt.Sprintf("%s.%d.renamed", filename, os.Getpid())
+ if err := ioutil.WriteFile(renamed, content, 0644); err != nil {
+ return err
}
- var buf bytes.Buffer
- if err := format.Node(&buf, fset, f); err != nil {
- return fmt.Errorf("failed to pretty-print syntax tree: %v", err)
+ defer os.Remove(renamed)
+
+ diff, err := exec.Command(DiffCmd, "-u", filename, renamed).CombinedOutput()
+ if len(diff) > 0 {
+ // diff exits with a non-zero status when the files don't match.
+ // Ignore that failure as long as we get output.
+ stdout.Write(diff)
+ return nil
}
- return ioutil.WriteFile(filename, buf.Bytes(), 0644)
+ if err != nil {
+ return fmt.Errorf("computing diff: %v", err)
+ }
+ return nil
}
diff --git a/go/src/golang.org/x/tools/refactor/rename/rename14.go b/go/src/golang.org/x/tools/refactor/rename/rename14.go
new file mode 100644
index 0000000..e4ccc06
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/rename/rename14.go
@@ -0,0 +1,510 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package rename contains the implementation of the 'gorename' command
+// whose main function is in golang.org/x/tools/cmd/gorename.
+// See the Usage constant for the command documentation.
+package rename // import "golang.org/x/tools/refactor/rename"
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "sort"
+ "strconv"
+ "strings"
+
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/refactor/importgraph"
+ "golang.org/x/tools/refactor/satisfy"
+)
+
+const Usage = `gorename: precise type-safe renaming of identifiers in Go source code.
+
+Usage:
+
+ gorename (-from <spec> | -offset <file>:#<byte-offset>) -to <name> [-force]
+
+You must specify the object (named entity) to rename using the -offset
+or -from flag. Exactly one must be specified.
+
+Flags:
+
+-offset specifies the filename and byte offset of an identifier to rename.
+ This form is intended for use by text editors.
+
+-from specifies the object to rename using a query notation;
+ This form is intended for interactive use at the command line.
+ A legal -from query has one of the following forms:
+
+ "encoding/json".Decoder.Decode method of package-level named type
+ (*"encoding/json".Decoder).Decode ditto, alternative syntax
+ "encoding/json".Decoder.buf field of package-level named struct type
+ "encoding/json".HTMLEscape package member (const, func, var, type)
+ "encoding/json".Decoder.Decode::x local object x within a method
+ "encoding/json".HTMLEscape::x local object x within a function
+ "encoding/json"::x object x anywhere within a package
+ json.go::x object x within file json.go
+
+ Double-quotes must be escaped when writing a shell command.
+ Quotes may be omitted for single-segment import paths such as "fmt".
+
+ For methods, the parens and '*' on the receiver type are both
+ optional.
+
+ It is an error if one of the ::x queries matches multiple
+ objects.
+
+-to the new name.
+
+-force causes the renaming to proceed even if conflicts were reported.
+ The resulting program may be ill-formed, or experience a change
+ in behaviour.
+
+ WARNING: this flag may even cause the renaming tool to crash.
+ (In due course this bug will be fixed by moving certain
+ analyses into the type-checker.)
+
+-d display diffs instead of rewriting files
+
+-v enables verbose logging.
+
+gorename automatically computes the set of packages that might be
+affected. For a local renaming, this is just the package specified by
+-from or -offset, but for a potentially exported name, gorename scans
+the workspace ($GOROOT and $GOPATH).
+
+gorename rejects renamings of concrete methods that would change the
+assignability relation between types and interfaces. If the interface
+change was intentional, initiate the renaming at the interface method.
+
+gorename rejects any renaming that would create a conflict at the point
+of declaration, or a reference conflict (ambiguity or shadowing), or
+anything else that could cause the resulting program not to compile.
+
+
+Examples:
+
+$ gorename -offset file.go:#123 -to foo
+
+ Rename the object whose identifier is at byte offset 123 within file file.go.
+
+$ gorename -from '"bytes".Buffer.Len' -to Size
+
+ Rename the "Len" method of the *bytes.Buffer type to "Size".
+
+---- TODO ----
+
+Correctness:
+- handle dot imports correctly
+- document limitations (reflection, 'implements' algorithm).
+- sketch a proof of exhaustiveness.
+
+Features:
+- support running on packages specified as *.go files on the command line
+- support running on programs containing errors (loader.Config.AllowErrors)
+- allow users to specify a scope other than "global" (to avoid being
+ stuck by neglected packages in $GOPATH that don't build).
+- support renaming the package clause (no object)
+- support renaming an import path (no ident or object)
+ (requires filesystem + SCM updates).
+- detect and reject edits to autogenerated files (cgo, protobufs)
+ and optionally $GOROOT packages.
+- report all conflicts, or at least all qualitatively distinct ones.
+ Sometimes we stop to avoid redundancy, but
+ it may give a disproportionate sense of safety in -force mode.
+- support renaming all instances of a pattern, e.g.
+ all receiver vars of a given type,
+ all local variables of a given type,
+ all PkgNames for a given package.
+- emit JSON output for other editors and tools.
+`
+
+var (
+ // Force enables patching of the source files even if conflicts were reported.
+ // The resulting program may be ill-formed.
+ // It may even cause gorename to crash. TODO(adonovan): fix that.
+ Force bool
+
+ // Diff causes the tool to display diffs instead of rewriting files.
+ Diff bool
+
+ // DiffCmd specifies the diff command used by the -d feature.
+ // (The command must accept a -u flag and two filename arguments.)
+ DiffCmd = "diff"
+
+ // ConflictError is returned by Main when it aborts the renaming due to conflicts.
+ // (It is distinguished because the interesting errors are the conflicts themselves.)
+ ConflictError = errors.New("renaming aborted due to conflicts")
+
+ // Verbose enables extra logging.
+ Verbose bool
+)
+
+var stdout io.Writer = os.Stdout
+
+type renamer struct {
+ iprog *loader.Program
+ objsToUpdate map[types.Object]bool
+ hadConflicts bool
+ to string
+ satisfyConstraints map[satisfy.Constraint]bool
+ packages map[*types.Package]*loader.PackageInfo // subset of iprog.AllPackages to inspect
+ msets typeutil.MethodSetCache
+ changeMethods bool
+}
+
+var reportError = func(posn token.Position, message string) {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", posn, message)
+}
+
+// importName renames imports of the package with the given path in
+// the given package. If fromName is not empty, only imports as
+// fromName will be renamed. If the renaming would lead to a conflict,
+// the file is left unchanged.
+func importName(iprog *loader.Program, info *loader.PackageInfo, fromPath, fromName, to string) error {
+ for _, f := range info.Files {
+ var from types.Object
+ for _, imp := range f.Imports {
+ importPath, _ := strconv.Unquote(imp.Path.Value)
+ importName := path.Base(importPath)
+ if imp.Name != nil {
+ importName = imp.Name.Name
+ }
+ if importPath == fromPath && (fromName == "" || importName == fromName) {
+ from = info.Implicits[imp]
+ break
+ }
+ }
+ if from == nil {
+ continue
+ }
+ r := renamer{
+ iprog: iprog,
+ objsToUpdate: make(map[types.Object]bool),
+ to: to,
+ packages: map[*types.Package]*loader.PackageInfo{info.Pkg: info},
+ }
+ r.check(from)
+ if r.hadConflicts {
+ continue // ignore errors; leave the existing name
+ }
+ if err := r.update(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func Main(ctxt *build.Context, offsetFlag, fromFlag, to string) error {
+ // -- Parse the -from or -offset specifier ----------------------------
+
+ if (offsetFlag == "") == (fromFlag == "") {
+ return fmt.Errorf("exactly one of the -from and -offset flags must be specified")
+ }
+
+ if !isValidIdentifier(to) {
+ return fmt.Errorf("-to %q: not a valid identifier", to)
+ }
+
+ if Diff {
+ defer func(saved func(string, []byte) error) { writeFile = saved }(writeFile)
+ writeFile = diff
+ }
+
+ var spec *spec
+ var err error
+ if fromFlag != "" {
+ spec, err = parseFromFlag(ctxt, fromFlag)
+ } else {
+ spec, err = parseOffsetFlag(ctxt, offsetFlag)
+ }
+ if err != nil {
+ return err
+ }
+
+ if spec.fromName == to {
+ return fmt.Errorf("the old and new names are the same: %s", to)
+ }
+
+ // -- Load the program consisting of the initial package -------------
+
+ iprog, err := loadProgram(ctxt, map[string]bool{spec.pkg: true})
+ if err != nil {
+ return err
+ }
+
+ fromObjects, err := findFromObjects(iprog, spec)
+ if err != nil {
+ return err
+ }
+
+ // -- Load a larger program, for global renamings ---------------------
+
+ if requiresGlobalRename(fromObjects, to) {
+ // For a local refactoring, we needn't load more
+ // packages, but if the renaming affects the package's
+ // API, we we must load all packages that depend on the
+ // package defining the object, plus their tests.
+
+ if Verbose {
+ log.Print("Potentially global renaming; scanning workspace...")
+ }
+
+ // Scan the workspace and build the import graph.
+ _, rev, errors := importgraph.Build(ctxt)
+ if len(errors) > 0 {
+ // With a large GOPATH tree, errors are inevitable.
+ // Report them but proceed.
+ fmt.Fprintf(os.Stderr, "While scanning Go workspace:\n")
+ for path, err := range errors {
+ fmt.Fprintf(os.Stderr, "Package %q: %s.\n", path, err)
+ }
+ }
+
+ // Enumerate the set of potentially affected packages.
+ affectedPackages := make(map[string]bool)
+ for _, obj := range fromObjects {
+ // External test packages are never imported,
+ // so they will never appear in the graph.
+ for path := range rev.Search(obj.Pkg().Path()) {
+ affectedPackages[path] = true
+ }
+ }
+
+ // TODO(adonovan): allow the user to specify the scope,
+ // or -ignore patterns? Computing the scope when we
+ // don't (yet) support inputs containing errors can make
+ // the tool rather brittle.
+
+ // Re-load the larger program.
+ iprog, err = loadProgram(ctxt, affectedPackages)
+ if err != nil {
+ return err
+ }
+
+ fromObjects, err = findFromObjects(iprog, spec)
+ if err != nil {
+ return err
+ }
+ }
+
+ // -- Do the renaming -------------------------------------------------
+
+ r := renamer{
+ iprog: iprog,
+ objsToUpdate: make(map[types.Object]bool),
+ to: to,
+ packages: make(map[*types.Package]*loader.PackageInfo),
+ }
+
+ // A renaming initiated at an interface method indicates the
+ // intention to rename abstract and concrete methods as needed
+ // to preserve assignability.
+ for _, obj := range fromObjects {
+ if obj, ok := obj.(*types.Func); ok {
+ recv := obj.Type().(*types.Signature).Recv()
+ if recv != nil && isInterface(recv.Type().Underlying()) {
+ r.changeMethods = true
+ break
+ }
+ }
+ }
+
+ // Only the initially imported packages (iprog.Imported) and
+ // their external tests (iprog.Created) should be inspected or
+ // modified, as only they have type-checked functions bodies.
+ // The rest are just dependencies, needed only for package-level
+ // type information.
+ for _, info := range iprog.Imported {
+ r.packages[info.Pkg] = info
+ }
+ for _, info := range iprog.Created { // (tests)
+ r.packages[info.Pkg] = info
+ }
+
+ for _, from := range fromObjects {
+ r.check(from)
+ }
+ if r.hadConflicts && !Force {
+ return ConflictError
+ }
+ return r.update()
+}
+
+// loadProgram loads the specified set of packages (plus their tests)
+// and all their dependencies, from source, through the specified build
+// context. Only packages in pkgs will have their functions bodies typechecked.
+func loadProgram(ctxt *build.Context, pkgs map[string]bool) (*loader.Program, error) {
+ conf := loader.Config{
+ Build: ctxt,
+ ParserMode: parser.ParseComments,
+
+ // TODO(adonovan): enable this. Requires making a lot of code more robust!
+ AllowErrors: false,
+ }
+
+ // Optimization: don't type-check the bodies of functions in our
+ // dependencies, since we only need exported package members.
+ conf.TypeCheckFuncBodies = func(p string) bool {
+ return pkgs[p] || pkgs[strings.TrimSuffix(p, "_test")]
+ }
+
+ if Verbose {
+ var list []string
+ for pkg := range pkgs {
+ list = append(list, pkg)
+ }
+ sort.Strings(list)
+ for _, pkg := range list {
+ log.Printf("Loading package: %s", pkg)
+ }
+ }
+
+ for pkg := range pkgs {
+ conf.ImportWithTests(pkg)
+ }
+ return conf.Load()
+}
+
+// requiresGlobalRename reports whether this renaming could potentially
+// affect other packages in the Go workspace.
+func requiresGlobalRename(fromObjects []types.Object, to string) bool {
+ var tfm bool
+ for _, from := range fromObjects {
+ if from.Exported() {
+ return true
+ }
+ switch objectKind(from) {
+ case "type", "field", "method":
+ tfm = true
+ }
+ }
+ if ast.IsExported(to) && tfm {
+ // A global renaming may be necessary even if we're
+ // exporting a previous unexported name, since if it's
+ // the name of a type, field or method, this could
+ // change selections in other packages.
+ // (We include "type" in this list because a type
+ // used as an embedded struct field entails a field
+ // renaming.)
+ return true
+ }
+ return false
+}
+
+// update updates the input files.
+func (r *renamer) update() error {
+ // We use token.File, not filename, since a file may appear to
+ // belong to multiple packages and be parsed more than once.
+ // token.File captures this distinction; filename does not.
+ var nidents int
+ var filesToUpdate = make(map[*token.File]bool)
+ for _, info := range r.packages {
+ // Mutate the ASTs and note the filenames.
+ for id, obj := range info.Defs {
+ if r.objsToUpdate[obj] {
+ nidents++
+ id.Name = r.to
+ filesToUpdate[r.iprog.Fset.File(id.Pos())] = true
+ }
+ }
+ for id, obj := range info.Uses {
+ if r.objsToUpdate[obj] {
+ nidents++
+ id.Name = r.to
+ filesToUpdate[r.iprog.Fset.File(id.Pos())] = true
+ }
+ }
+ }
+
+ // TODO(adonovan): don't rewrite cgo + generated files.
+ var nerrs, npkgs int
+ for _, info := range r.packages {
+ first := true
+ for _, f := range info.Files {
+ tokenFile := r.iprog.Fset.File(f.Pos())
+ if filesToUpdate[tokenFile] {
+ if first {
+ npkgs++
+ first = false
+ if Verbose {
+ log.Printf("Updating package %s", info.Pkg.Path())
+ }
+ }
+
+ filename := tokenFile.Name()
+ var buf bytes.Buffer
+ if err := format.Node(&buf, r.iprog.Fset, f); err != nil {
+ log.Printf("failed to pretty-print syntax tree: %v", err)
+ nerrs++
+ continue
+ }
+ if err := writeFile(filename, buf.Bytes()); err != nil {
+ log.Print(err)
+ nerrs++
+ }
+ }
+ }
+ }
+ if !Diff {
+ fmt.Printf("Renamed %d occurrence%s in %d file%s in %d package%s.\n",
+ nidents, plural(nidents),
+ len(filesToUpdate), plural(len(filesToUpdate)),
+ npkgs, plural(npkgs))
+ }
+ if nerrs > 0 {
+ return fmt.Errorf("failed to rewrite %d file%s", nerrs, plural(nerrs))
+ }
+ return nil
+}
+
+func plural(n int) string {
+ if n != 1 {
+ return "s"
+ }
+ return ""
+}
+
+// writeFile is a seam for testing and for the -d flag.
+var writeFile = reallyWriteFile
+
+func reallyWriteFile(filename string, content []byte) error {
+ return ioutil.WriteFile(filename, content, 0644)
+}
+
+func diff(filename string, content []byte) error {
+ renamed := fmt.Sprintf("%s.%d.renamed", filename, os.Getpid())
+ if err := ioutil.WriteFile(renamed, content, 0644); err != nil {
+ return err
+ }
+ defer os.Remove(renamed)
+
+ diff, err := exec.Command(DiffCmd, "-u", filename, renamed).CombinedOutput()
+ if len(diff) > 0 {
+ // diff exits with a non-zero status when the files don't match.
+ // Ignore that failure as long as we get output.
+ stdout.Write(diff)
+ return nil
+ }
+ if err != nil {
+ return fmt.Errorf("computing diff: %v", err)
+ }
+ return nil
+}
diff --git a/go/src/golang.org/x/tools/refactor/rename/rename_test.go b/go/src/golang.org/x/tools/refactor/rename/rename_test.go
index 1d282a1..3670319 100644
--- a/go/src/golang.org/x/tools/refactor/rename/rename_test.go
+++ b/go/src/golang.org/x/tools/refactor/rename/rename_test.go
@@ -7,12 +7,12 @@
import (
"bytes"
"fmt"
- "go/ast"
"go/build"
- "go/format"
"go/token"
+ "os"
"path/filepath"
"regexp"
+ "runtime"
"strings"
"testing"
@@ -22,11 +22,11 @@
// TODO(adonovan): test reported source positions, somehow.
func TestConflicts(t *testing.T) {
- defer func(savedDryRun bool, savedReportError func(token.Position, string)) {
- DryRun = savedDryRun
+ defer func(savedWriteFile func(string, []byte) error, savedReportError func(token.Position, string)) {
+ writeFile = savedWriteFile
reportError = savedReportError
- }(DryRun, reportError)
- DryRun = true
+ }(writeFile, reportError)
+ writeFile = func(string, []byte) error { return nil }
var ctxt *build.Context
for _, test := range []struct {
@@ -417,9 +417,9 @@
}
func TestRewrites(t *testing.T) {
- defer func(savedRewriteFile func(*token.FileSet, *ast.File, string) error) {
- rewriteFile = savedRewriteFile
- }(rewriteFile)
+ defer func(savedWriteFile func(string, []byte) error) {
+ writeFile = savedWriteFile
+ }(writeFile)
var ctxt *build.Context
for _, test := range []struct {
@@ -721,6 +721,57 @@
},
},
+ // Renaming of embedded field that is a qualified reference with the '-from' flag.
+ // (Regression test for bug 12038.)
+ {
+ ctxt: fakeContext(map[string][]string{
+ "foo": {`package foo; type T int`},
+ "main": {`package main
+
+import "foo"
+
+type V struct{ *foo.T }
+`},
+ }),
+ from: "(main.V).T", to: "U", // the "T" in *foo.T
+ want: map[string]string{
+ "/go/src/foo/0.go": `package foo
+
+type U int
+`,
+ "/go/src/main/0.go": `package main
+
+import "foo"
+
+type V struct{ *foo.U }
+`,
+ },
+ },
+ {
+ ctxt: fakeContext(map[string][]string{
+ "foo": {`package foo; type T int`},
+ "main": {`package main
+
+import "foo"
+
+type V struct{ foo.T }
+`},
+ }),
+ from: "(main.V).T", to: "U", // the "T" in *foo.T
+ want: map[string]string{
+ "/go/src/foo/0.go": `package foo
+
+type U int
+`,
+ "/go/src/main/0.go": `package main
+
+import "foo"
+
+type V struct{ foo.U }
+`,
+ },
+ },
+
// Interface method renaming.
{
ctxt: fakeContext(map[string][]string{
@@ -977,12 +1028,8 @@
}
got := make(map[string]string)
- rewriteFile = func(fset *token.FileSet, f *ast.File, orig string) error {
- var out bytes.Buffer
- if err := format.Node(&out, fset, f); err != nil {
- return err
- }
- got[filepath.ToSlash(orig)] = out.String()
+ writeFile = func(filename string, content []byte) error {
+ got[filepath.ToSlash(filename)] = string(content)
return nil
}
@@ -1017,6 +1064,38 @@
}
}
+func TestDiff(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skipf("diff tool non-existent for windows on builders")
+ }
+
+ defer func() {
+ Diff = false
+ stdout = os.Stdout
+ }()
+ Diff = true
+ stdout = new(bytes.Buffer)
+
+ if err := Main(&build.Default, "", `"golang.org/x/tools/refactor/rename".justHereForTestingDiff`, "Foo"); err != nil {
+ t.Fatal(err)
+ }
+
+ // NB: there are tabs in the string literal!
+ if !strings.Contains(stdout.(fmt.Stringer).String(), `
+-func justHereForTestingDiff() {
+- justHereForTestingDiff()
++func Foo() {
++ Foo()
+ }
+`) {
+ t.Errorf("unexpected diff:\n<<%s>>", stdout)
+ }
+}
+
+func justHereForTestingDiff() {
+ justHereForTestingDiff()
+}
+
// ---------------------------------------------------------------------
// Simplifying wrapper around buildutil.FakeContext for packages whose
diff --git a/go/src/golang.org/x/tools/refactor/rename/spec.go b/go/src/golang.org/x/tools/refactor/rename/spec.go
index 3c73653..259404d 100644
--- a/go/src/golang.org/x/tools/refactor/rename/spec.go
+++ b/go/src/golang.org/x/tools/refactor/rename/spec.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package rename
// This file contains logic related to specifying a renaming: parsing of
@@ -15,6 +17,8 @@
"go/build"
"go/parser"
"go/token"
+ "go/types"
+ "log"
"os"
"path/filepath"
"strconv"
@@ -22,7 +26,6 @@
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
)
// A spec specifies an entity to rename.
@@ -113,9 +116,13 @@
spec.fromName = spec.searchFor
}
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+
// Sanitize the package.
- // TODO(adonovan): test with relative packages. May need loader changes.
- bp, err := ctxt.Import(spec.pkg, ".", build.FindOnly)
+ bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
if err != nil {
return nil, fmt.Errorf("can't find package %q", spec.pkg)
}
@@ -126,7 +133,7 @@
}
if Verbose {
- fmt.Fprintf(os.Stderr, "-from spec: %+v\n", spec)
+ log.Printf("-from spec: %+v", spec)
}
return &spec, nil
@@ -271,20 +278,23 @@
// for main packages, even though that's not an import path.
// Seems like a bug.
//
- // pkgObj := iprog.ImportMap[spec.pkg]
- // if pkgObj == nil {
+ // pkg := iprog.ImportMap[spec.pkg]
+ // if pkg == nil {
// return fmt.Errorf("cannot find package %s", spec.pkg) // can't happen?
// }
+ // info := iprog.AllPackages[pkg]
// Workaround: lookup by value.
- var pkgObj *types.Package
- for pkg := range iprog.AllPackages {
+ var info *loader.PackageInfo
+ var pkg *types.Package
+ for pkg, info = range iprog.AllPackages {
if pkg.Path() == spec.pkg {
- pkgObj = pkg
break
}
}
- info := iprog.AllPackages[pkgObj]
+ if info == nil {
+ return nil, fmt.Errorf("package %q was not loaded", spec.pkg)
+ }
objects, err := findObjects(info, spec)
if err != nil {
@@ -451,6 +461,15 @@
}
if spec.searchFor == "" {
+ // If it is an embedded field, return the type of the field.
+ if v, ok := obj.(*types.Var); ok && v.Anonymous() {
+ switch t := v.Type().(type) {
+ case *types.Pointer:
+ return []types.Object{t.Elem().(*types.Named).Obj()}, nil
+ case *types.Named:
+ return []types.Object{t.Obj()}, nil
+ }
+ }
return []types.Object{obj}, nil
}
diff --git a/go/src/golang.org/x/tools/refactor/rename/spec14.go b/go/src/golang.org/x/tools/refactor/rename/spec14.go
new file mode 100644
index 0000000..7634ae8
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/rename/spec14.go
@@ -0,0 +1,568 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package rename
+
+// This file contains logic related to specifying a renaming: parsing of
+// the flags as a form of query, and finding the object(s) it denotes.
+// See Usage for flag details.
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "log"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/types"
+)
+
+// A spec specifies an entity to rename.
+//
+// It is populated from an -offset flag or -from query;
+// see Usage for the allowed -from query forms.
+//
+type spec struct {
+ // pkg is the package containing the position
+ // specified by the -from or -offset flag.
+ // If filename == "", our search for the 'from' entity
+ // is restricted to this package.
+ pkg string
+
+ // The original name of the entity being renamed.
+ // If the query had a ::from component, this is that;
+ // otherwise it's the last segment, e.g.
+ // (encoding/json.Decoder).from
+ // encoding/json.from
+ fromName string
+
+ // -- The remaining fields are private to this file. All are optional. --
+
+ // The query's ::x suffix, if any.
+ searchFor string
+
+ // e.g. "Decoder" in "(encoding/json.Decoder).fieldOrMethod"
+ // or "encoding/json.Decoder
+ pkgMember string
+
+ // e.g. fieldOrMethod in "(encoding/json.Decoder).fieldOrMethod"
+ typeMember string
+
+ // Restricts the query to this file.
+ // Implied by -from="file.go::x" and -offset flags.
+ filename string
+
+ // Byte offset of the 'from' identifier within the file named 'filename'.
+ // -offset mode only.
+ offset int
+}
+
+// parseFromFlag interprets the "-from" flag value as a renaming specification.
+// See Usage in rename.go for valid formats.
+func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
+ var spec spec
+ var main string // sans "::x" suffix
+ switch parts := strings.Split(fromFlag, "::"); len(parts) {
+ case 1:
+ main = parts[0]
+ case 2:
+ main = parts[0]
+ spec.searchFor = parts[1]
+ if parts[1] == "" {
+ // error
+ }
+ default:
+ return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
+ }
+
+ if strings.HasSuffix(main, ".go") {
+ // main is "filename.go"
+ if spec.searchFor == "" {
+ return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
+ }
+ spec.filename = main
+ if !buildutil.FileExists(ctxt, spec.filename) {
+ return nil, fmt.Errorf("no such file: %s", spec.filename)
+ }
+
+ bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
+ if err != nil {
+ return nil, err
+ }
+ spec.pkg = bp.ImportPath
+
+ } else {
+ // main is one of:
+ // "importpath"
+ // "importpath".member
+ // (*"importpath".type).fieldormethod (parens and star optional)
+ if err := parseObjectSpec(&spec, main); err != nil {
+ return nil, err
+ }
+ }
+
+ if spec.searchFor != "" {
+ spec.fromName = spec.searchFor
+ }
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+
+ // Sanitize the package.
+ bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
+ if err != nil {
+ return nil, fmt.Errorf("can't find package %q", spec.pkg)
+ }
+ spec.pkg = bp.ImportPath
+
+ if !isValidIdentifier(spec.fromName) {
+ return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
+ }
+
+ if Verbose {
+ log.Printf("-from spec: %+v", spec)
+ }
+
+ return &spec, nil
+}
+
+// parseObjectSpec parses main as one of the non-filename forms of
+// object specification.
+func parseObjectSpec(spec *spec, main string) error {
+ // Parse main as a Go expression, albeit a strange one.
+ e, _ := parser.ParseExpr(main)
+
+ if pkg := parseImportPath(e); pkg != "" {
+ // e.g. bytes or "encoding/json": a package
+ spec.pkg = pkg
+ if spec.searchFor == "" {
+ return fmt.Errorf("-from %q: package import path %q must have a ::name suffix",
+ main, main)
+ }
+ return nil
+ }
+
+ if e, ok := e.(*ast.SelectorExpr); ok {
+ x := unparen(e.X)
+
+ // Strip off star constructor, if any.
+ if star, ok := x.(*ast.StarExpr); ok {
+ x = star.X
+ }
+
+ if pkg := parseImportPath(x); pkg != "" {
+ // package member e.g. "encoding/json".HTMLEscape
+ spec.pkg = pkg // e.g. "encoding/json"
+ spec.pkgMember = e.Sel.Name // e.g. "HTMLEscape"
+ spec.fromName = e.Sel.Name
+ return nil
+ }
+
+ if x, ok := x.(*ast.SelectorExpr); ok {
+ // field/method of type e.g. ("encoding/json".Decoder).Decode
+ y := unparen(x.X)
+ if pkg := parseImportPath(y); pkg != "" {
+ spec.pkg = pkg // e.g. "encoding/json"
+ spec.pkgMember = x.Sel.Name // e.g. "Decoder"
+ spec.typeMember = e.Sel.Name // e.g. "Decode"
+ spec.fromName = e.Sel.Name
+ return nil
+ }
+ }
+ }
+
+ return fmt.Errorf("-from %q: invalid expression", main)
+}
+
+// parseImportPath returns the import path of the package denoted by e.
+// Any import path may be represented as a string literal;
+// single-segment import paths (e.g. "bytes") may also be represented as
+// ast.Ident. parseImportPath returns "" for all other expressions.
+func parseImportPath(e ast.Expr) string {
+ switch e := e.(type) {
+ case *ast.Ident:
+ return e.Name // e.g. bytes
+
+ case *ast.BasicLit:
+ if e.Kind == token.STRING {
+ pkgname, _ := strconv.Unquote(e.Value)
+ return pkgname // e.g. "encoding/json"
+ }
+ }
+ return ""
+}
+
+// parseOffsetFlag interprets the "-offset" flag value as a renaming specification.
+func parseOffsetFlag(ctxt *build.Context, offsetFlag string) (*spec, error) {
+ var spec spec
+ // Validate -offset, e.g. file.go:#123
+ parts := strings.Split(offsetFlag, ":#")
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("-offset %q: invalid offset specification", offsetFlag)
+ }
+
+ spec.filename = parts[0]
+ if !buildutil.FileExists(ctxt, spec.filename) {
+ return nil, fmt.Errorf("no such file: %s", spec.filename)
+ }
+
+ bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
+ if err != nil {
+ return nil, err
+ }
+ spec.pkg = bp.ImportPath
+
+ for _, r := range parts[1] {
+ if !isDigit(r) {
+ return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
+ }
+ }
+ spec.offset, err = strconv.Atoi(parts[1])
+ if err != nil {
+ return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
+ }
+
+ // Parse the file and check there's an identifier at that offset.
+ fset := token.NewFileSet()
+ f, err := buildutil.ParseFile(fset, ctxt, nil, wd, spec.filename, parser.ParseComments)
+ if err != nil {
+ return nil, fmt.Errorf("-offset %q: cannot parse file: %s", offsetFlag, err)
+ }
+
+ id := identAtOffset(fset, f, spec.offset)
+ if id == nil {
+ return nil, fmt.Errorf("-offset %q: no identifier at this position", offsetFlag)
+ }
+
+ spec.fromName = id.Name
+
+ return &spec, nil
+}
+
+var wd = func() string {
+ wd, err := os.Getwd()
+ if err != nil {
+ panic("cannot get working directory: " + err.Error())
+ }
+ return wd
+}()
+
+// For source trees built with 'go build', the -from or -offset
+// spec identifies exactly one initial 'from' object to rename ,
+// but certain proprietary build systems allow a single file to
+// appear in multiple packages (e.g. the test package contains a
+// copy of its library), so there may be multiple objects for
+// the same source entity.
+
+func findFromObjects(iprog *loader.Program, spec *spec) ([]types.Object, error) {
+ if spec.filename != "" {
+ return findFromObjectsInFile(iprog, spec)
+ }
+
+ // Search for objects defined in specified package.
+
+ // TODO(adonovan): the iprog.ImportMap has an entry {"main": ...}
+ // for main packages, even though that's not an import path.
+ // Seems like a bug.
+ //
+ // pkg := iprog.ImportMap[spec.pkg]
+ // if pkg == nil {
+ // return fmt.Errorf("cannot find package %s", spec.pkg) // can't happen?
+ // }
+ // info := iprog.AllPackages[pkg]
+
+ // Workaround: lookup by value.
+ var info *loader.PackageInfo
+ var pkg *types.Package
+ for pkg, info = range iprog.AllPackages {
+ if pkg.Path() == spec.pkg {
+ break
+ }
+ }
+ if info == nil {
+ return nil, fmt.Errorf("package %q was not loaded", spec.pkg)
+ }
+
+ objects, err := findObjects(info, spec)
+ if err != nil {
+ return nil, err
+ }
+ if len(objects) > 1 {
+ // ambiguous "*" scope query
+ return nil, ambiguityError(iprog.Fset, objects)
+ }
+ return objects, nil
+}
+
+func findFromObjectsInFile(iprog *loader.Program, spec *spec) ([]types.Object, error) {
+ var fromObjects []types.Object
+ for _, info := range iprog.AllPackages {
+ // restrict to specified filename
+ // NB: under certain proprietary build systems, a given
+ // filename may appear in multiple packages.
+ for _, f := range info.Files {
+ thisFile := iprog.Fset.File(f.Pos())
+ if !sameFile(thisFile.Name(), spec.filename) {
+ continue
+ }
+ // This package contains the query file.
+
+ if spec.offset != 0 {
+ // Search for a specific ident by file/offset.
+ id := identAtOffset(iprog.Fset, f, spec.offset)
+ if id == nil {
+ // can't happen?
+ return nil, fmt.Errorf("identifier not found")
+ }
+ obj := info.Uses[id]
+ if obj == nil {
+ obj = info.Defs[id]
+ if obj == nil {
+ // Ident without Object.
+
+ // Package clause?
+ pos := thisFile.Pos(spec.offset)
+ _, path, _ := iprog.PathEnclosingInterval(pos, pos)
+ if len(path) == 2 { // [Ident File]
+ // TODO(adonovan): support this case.
+ return nil, fmt.Errorf("cannot rename %q: renaming package clauses is not yet supported",
+ path[1].(*ast.File).Name.Name)
+ }
+
+ // Implicit y in "switch y := x.(type) {"?
+ if obj := typeSwitchVar(&info.Info, path); obj != nil {
+ return []types.Object{obj}, nil
+ }
+
+ // Probably a type error.
+ return nil, fmt.Errorf("cannot find object for %q", id.Name)
+ }
+ }
+ if obj.Pkg() == nil {
+ return nil, fmt.Errorf("cannot rename predeclared identifiers (%s)", obj)
+
+ }
+
+ fromObjects = append(fromObjects, obj)
+ } else {
+ // do a package-wide query
+ objects, err := findObjects(info, spec)
+ if err != nil {
+ return nil, err
+ }
+
+ // filter results: only objects defined in thisFile
+ var filtered []types.Object
+ for _, obj := range objects {
+ if iprog.Fset.File(obj.Pos()) == thisFile {
+ filtered = append(filtered, obj)
+ }
+ }
+ if len(filtered) == 0 {
+ return nil, fmt.Errorf("no object %q declared in file %s",
+ spec.fromName, spec.filename)
+ } else if len(filtered) > 1 {
+ return nil, ambiguityError(iprog.Fset, filtered)
+ }
+ fromObjects = append(fromObjects, filtered[0])
+ }
+ break
+ }
+ }
+ if len(fromObjects) == 0 {
+ // can't happen?
+ return nil, fmt.Errorf("file %s was not part of the loaded program", spec.filename)
+ }
+ return fromObjects, nil
+}
+
+func typeSwitchVar(info *types.Info, path []ast.Node) types.Object {
+ if len(path) > 3 {
+ // [Ident AssignStmt TypeSwitchStmt...]
+ if sw, ok := path[2].(*ast.TypeSwitchStmt); ok {
+ // choose the first case.
+ if len(sw.Body.List) > 0 {
+ obj := info.Implicits[sw.Body.List[0].(*ast.CaseClause)]
+ if obj != nil {
+ return obj
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// On success, findObjects returns the list of objects named
+// spec.fromName matching the spec. On success, the result has exactly
+// one element unless spec.searchFor!="", in which case it has at least one
+// element.
+//
+func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) {
+ if spec.pkgMember == "" {
+ if spec.searchFor == "" {
+ panic(spec)
+ }
+ objects := searchDefs(&info.Info, spec.searchFor)
+ if objects == nil {
+ return nil, fmt.Errorf("no object %q declared in package %q",
+ spec.searchFor, info.Pkg.Path())
+ }
+ return objects, nil
+ }
+
+ pkgMember := info.Pkg.Scope().Lookup(spec.pkgMember)
+ if pkgMember == nil {
+ return nil, fmt.Errorf("package %q has no member %q",
+ info.Pkg.Path(), spec.pkgMember)
+ }
+
+ var searchFunc *types.Func
+ if spec.typeMember == "" {
+ // package member
+ if spec.searchFor == "" {
+ return []types.Object{pkgMember}, nil
+ }
+
+ // Search within pkgMember, which must be a function.
+ searchFunc, _ = pkgMember.(*types.Func)
+ if searchFunc == nil {
+ return nil, fmt.Errorf("cannot search for %q within %s %q",
+ spec.searchFor, objectKind(pkgMember), pkgMember)
+ }
+ } else {
+ // field/method of type
+ // e.g. (encoding/json.Decoder).Decode
+ // or ::x within it.
+
+ tName, _ := pkgMember.(*types.TypeName)
+ if tName == nil {
+ return nil, fmt.Errorf("%s.%s is a %s, not a type",
+ info.Pkg.Path(), pkgMember.Name(), objectKind(pkgMember))
+ }
+
+ // search within named type.
+ obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember)
+ if obj == nil {
+ return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s",
+ spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name())
+ }
+
+ if spec.searchFor == "" {
+ // If it is an embedded field, return the type of the field.
+ if v, ok := obj.(*types.Var); ok && v.Anonymous() {
+ switch t := v.Type().(type) {
+ case *types.Pointer:
+ return []types.Object{t.Elem().(*types.Named).Obj()}, nil
+ case *types.Named:
+ return []types.Object{t.Obj()}, nil
+ }
+ }
+ return []types.Object{obj}, nil
+ }
+
+ searchFunc, _ = obj.(*types.Func)
+ if searchFunc == nil {
+ return nil, fmt.Errorf("cannot search for local name %q within %s (%s.%s).%s; need a function",
+ spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(),
+ obj.Name())
+ }
+ if isInterface(tName.Type()) {
+ return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s",
+ spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name())
+ }
+ }
+
+ // -- search within function or method --
+
+ decl := funcDecl(info, searchFunc)
+ if decl == nil {
+ return nil, fmt.Errorf("cannot find syntax for %s", searchFunc) // can't happen?
+ }
+
+ var objects []types.Object
+ for _, obj := range searchDefs(&info.Info, spec.searchFor) {
+ // We use positions, not scopes, to determine whether
+ // the obj is within searchFunc. This is clumsy, but the
+ // alternative, using the types.Scope tree, doesn't
+ // account for non-lexical objects like fields and
+ // interface methods.
+ if decl.Pos() <= obj.Pos() && obj.Pos() < decl.End() && obj != searchFunc {
+ objects = append(objects, obj)
+ }
+ }
+ if objects == nil {
+ return nil, fmt.Errorf("no local definition of %q within %s",
+ spec.searchFor, searchFunc)
+ }
+ return objects, nil
+}
+
+func funcDecl(info *loader.PackageInfo, fn *types.Func) *ast.FuncDecl {
+ for _, f := range info.Files {
+ for _, d := range f.Decls {
+ if d, ok := d.(*ast.FuncDecl); ok && info.Defs[d.Name] == fn {
+ return d
+ }
+ }
+ }
+ return nil
+}
+
+func searchDefs(info *types.Info, name string) []types.Object {
+ var objects []types.Object
+ for id, obj := range info.Defs {
+ if obj == nil {
+ // e.g. blank ident.
+ // TODO(adonovan): but also implicit y in
+ // switch y := x.(type)
+ // Needs some thought.
+ continue
+ }
+ if id.Name == name {
+ objects = append(objects, obj)
+ }
+ }
+ return objects
+}
+
+func identAtOffset(fset *token.FileSet, f *ast.File, offset int) *ast.Ident {
+ var found *ast.Ident
+ ast.Inspect(f, func(n ast.Node) bool {
+ if id, ok := n.(*ast.Ident); ok {
+ idpos := fset.Position(id.Pos()).Offset
+ if idpos <= offset && offset < idpos+len(id.Name) {
+ found = id
+ }
+ }
+ return found == nil // keep traversing only until found
+ })
+ return found
+}
+
+// ambiguityError returns an error describing an ambiguous "*" scope query.
+func ambiguityError(fset *token.FileSet, objects []types.Object) error {
+ var buf bytes.Buffer
+ for i, obj := range objects {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ posn := fset.Position(obj.Pos())
+ fmt.Fprintf(&buf, "%s at %s:%d",
+ objectKind(obj), filepath.Base(posn.Filename), posn.Column)
+ }
+ return fmt.Errorf("ambiguous specifier %s matches %s",
+ objects[0].Name(), buf.String())
+}
diff --git a/go/src/golang.org/x/tools/refactor/rename/util.go b/go/src/golang.org/x/tools/refactor/rename/util.go
index def9399..f0f80f0 100644
--- a/go/src/golang.org/x/tools/refactor/rename/util.go
+++ b/go/src/golang.org/x/tools/refactor/rename/util.go
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package rename
import (
"go/ast"
+ "go/types"
"os"
"path/filepath"
"reflect"
@@ -14,7 +17,6 @@
"unicode"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
)
func objectKind(obj types.Object) string {
diff --git a/go/src/golang.org/x/tools/refactor/rename/util14.go b/go/src/golang.org/x/tools/refactor/rename/util14.go
new file mode 100644
index 0000000..126d3ee
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/rename/util14.go
@@ -0,0 +1,106 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+package rename
+
+import (
+ "go/ast"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "unicode"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/types"
+)
+
+func objectKind(obj types.Object) string {
+ switch obj := obj.(type) {
+ case *types.PkgName:
+ return "imported package name"
+ case *types.TypeName:
+ return "type"
+ case *types.Var:
+ if obj.IsField() {
+ return "field"
+ }
+ case *types.Func:
+ if obj.Type().(*types.Signature).Recv() != nil {
+ return "method"
+ }
+ }
+ // label, func, var, const
+ return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
+}
+
+func typeKind(T types.Type) string {
+ return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(T.Underlying()).String(), "*types."))
+}
+
+// NB: for renamings, blank is not considered valid.
+func isValidIdentifier(id string) bool {
+ if id == "" || id == "_" {
+ return false
+ }
+ for i, r := range id {
+ if !isLetter(r) && (i == 0 || !isDigit(r)) {
+ return false
+ }
+ }
+ return true
+}
+
+// isLocal reports whether obj is local to some function.
+// Precondition: not a struct field or interface method.
+func isLocal(obj types.Object) bool {
+ // [... 5=stmt 4=func 3=file 2=pkg 1=universe]
+ var depth int
+ for scope := obj.Parent(); scope != nil; scope = scope.Parent() {
+ depth++
+ }
+ return depth >= 4
+}
+
+func isPackageLevel(obj types.Object) bool {
+ return obj.Pkg().Scope().Lookup(obj.Name()) == obj
+}
+
+// -- Plundered from go/scanner: ---------------------------------------
+
+func isLetter(ch rune) bool {
+ return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
+}
+
+func isDigit(ch rune) bool {
+ return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
+}
+
+// -- Plundered from golang.org/x/tools/oracle -----------------
+
+// sameFile returns true if x and y have the same basename and denote
+// the same file.
+//
+func sameFile(x, y string) bool {
+ if runtime.GOOS == "windows" {
+ x = filepath.ToSlash(x)
+ y = filepath.ToSlash(y)
+ }
+ if x == y {
+ return true
+ }
+ if filepath.Base(x) == filepath.Base(y) { // (optimisation)
+ if xi, err := os.Stat(x); err == nil {
+ if yi, err := os.Stat(y); err == nil {
+ return os.SameFile(xi, yi)
+ }
+ }
+ }
+ return false
+}
+
+func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
diff --git a/go/src/golang.org/x/tools/refactor/satisfy/find.go b/go/src/golang.org/x/tools/refactor/satisfy/find.go
index acbcf62..a346c1a 100644
--- a/go/src/golang.org/x/tools/refactor/satisfy/find.go
+++ b/go/src/golang.org/x/tools/refactor/satisfy/find.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package satisfy inspects the type-checked ASTs of Go packages and
// reports the set of discovered type constraints of the form (lhs, rhs
// Type) where lhs is a non-trivial interface, rhs satisfies this
@@ -48,9 +50,9 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/src/golang.org/x/tools/refactor/satisfy/find14.go b/go/src/golang.org/x/tools/refactor/satisfy/find14.go
new file mode 100644
index 0000000..3b8d1e0
--- /dev/null
+++ b/go/src/golang.org/x/tools/refactor/satisfy/find14.go
@@ -0,0 +1,707 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.5
+
+// Package satisfy inspects the type-checked ASTs of Go packages and
+// reports the set of discovered type constraints of the form (lhs, rhs
+// Type) where lhs is a non-trivial interface, rhs satisfies this
+// interface, and this fact is necessary for the package to be
+// well-typed.
+//
+// THIS PACKAGE IS EXPERIMENTAL AND MAY CHANGE AT ANY TIME.
+//
+// It is provided only for the gorename tool. Ideally this
+// functionality will become part of the type-checker in due course,
+// since it is computing it anyway, and it is robust for ill-typed
+// inputs, which this package is not.
+//
+package satisfy // import "golang.org/x/tools/refactor/satisfy"
+
+// NOTES:
+//
+// We don't care about numeric conversions, so we don't descend into
+// types or constant expressions. This is unsound because
+// constant expressions can contain arbitrary statements, e.g.
+// const x = len([1]func(){func() {
+// ...
+// }})
+//
+// TODO(adonovan): make this robust against ill-typed input.
+// Or move it into the type-checker.
+//
+// Assignability conversions are possible in the following places:
+// - in assignments y = x, y := x, var y = x.
+// - from call argument types to formal parameter types
+// - in append and delete calls
+// - from return operands to result parameter types
+// - in composite literal T{k:v}, from k and v to T's field/element/key type
+// - in map[key] from key to the map's key type
+// - in comparisons x==y and switch x { case y: }.
+// - in explicit conversions T(x)
+// - in sends ch <- x, from x to the channel element type
+// - in type assertions x.(T) and switch x.(type) { case T: }
+//
+// The results of this pass provide information equivalent to the
+// ssa.MakeInterface and ssa.ChangeInterface instructions.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// A Constraint records the fact that the RHS type does and must
+// satisify the LHS type, which is an interface.
+// The names are suggestive of an assignment statement LHS = RHS.
+type Constraint struct {
+ LHS, RHS types.Type
+}
+
+// A Finder inspects the type-checked ASTs of Go packages and
+// accumulates the set of type constraints (x, y) such that x is
+// assignable to y, y is an interface, and both x and y have methods.
+//
+// In other words, it returns the subset of the "implements" relation
+// that is checked during compilation of a package. Refactoring tools
+// will need to preserve at least this part of the relation to ensure
+// continued compilation.
+//
+type Finder struct {
+ Result map[Constraint]bool
+ msetcache typeutil.MethodSetCache
+
+ // per-Find state
+ info *types.Info
+ sig *types.Signature
+}
+
+// Find inspects a single package, populating Result with its pairs of
+// constrained types.
+//
+// The result is non-canonical and thus may contain duplicates (but this
+// tends to preserves names of interface types better).
+//
+// The package must be free of type errors, and
+// info.{Defs,Uses,Selections,Types} must have been populated by the
+// type-checker.
+//
+func (f *Finder) Find(info *types.Info, files []*ast.File) {
+ if f.Result == nil {
+ f.Result = make(map[Constraint]bool)
+ }
+
+ f.info = info
+ for _, file := range files {
+ for _, d := range file.Decls {
+ switch d := d.(type) {
+ case *ast.GenDecl:
+ if d.Tok == token.VAR { // ignore consts
+ for _, spec := range d.Specs {
+ f.valueSpec(spec.(*ast.ValueSpec))
+ }
+ }
+
+ case *ast.FuncDecl:
+ if d.Body != nil {
+ f.sig = f.info.Defs[d.Name].Type().(*types.Signature)
+ f.stmt(d.Body)
+ f.sig = nil
+ }
+ }
+ }
+ }
+ f.info = nil
+}
+
+var (
+ tInvalid = types.Typ[types.Invalid]
+ tUntypedBool = types.Typ[types.UntypedBool]
+ tUntypedNil = types.Typ[types.UntypedNil]
+)
+
+// exprN visits an expression in a multi-value context.
+func (f *Finder) exprN(e ast.Expr) types.Type {
+ typ := f.info.Types[e].Type.(*types.Tuple)
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ return f.exprN(e.X)
+
+ case *ast.CallExpr:
+ // x, err := f(args)
+ sig := f.expr(e.Fun).Underlying().(*types.Signature)
+ f.call(sig, e.Args)
+
+ case *ast.IndexExpr:
+ // y, ok := x[i]
+ x := f.expr(e.X)
+ f.assign(f.expr(e.Index), x.Underlying().(*types.Map).Key())
+
+ case *ast.TypeAssertExpr:
+ // y, ok := x.(T)
+ f.typeAssert(f.expr(e.X), typ.At(0).Type())
+
+ case *ast.UnaryExpr: // must be receive <-
+ // y, ok := <-x
+ f.expr(e.X)
+
+ default:
+ panic(e)
+ }
+ return typ
+}
+
+func (f *Finder) call(sig *types.Signature, args []ast.Expr) {
+ if len(args) == 0 {
+ return
+ }
+
+ // Ellipsis call? e.g. f(x, y, z...)
+ if _, ok := args[len(args)-1].(*ast.Ellipsis); ok {
+ for i, arg := range args {
+ // The final arg is a slice, and so is the final param.
+ f.assign(sig.Params().At(i).Type(), f.expr(arg))
+ }
+ return
+ }
+
+ var argtypes []types.Type
+
+ // Gather the effective actual parameter types.
+ if tuple, ok := f.info.Types[args[0]].Type.(*types.Tuple); ok {
+ // f(g()) call where g has multiple results?
+ f.expr(args[0])
+ // unpack the tuple
+ for i := 0; i < tuple.Len(); i++ {
+ argtypes = append(argtypes, tuple.At(i).Type())
+ }
+ } else {
+ for _, arg := range args {
+ argtypes = append(argtypes, f.expr(arg))
+ }
+ }
+
+ // Assign the actuals to the formals.
+ if !sig.Variadic() {
+ for i, argtype := range argtypes {
+ f.assign(sig.Params().At(i).Type(), argtype)
+ }
+ } else {
+ // The first n-1 parameters are assigned normally.
+ nnormals := sig.Params().Len() - 1
+ for i, argtype := range argtypes[:nnormals] {
+ f.assign(sig.Params().At(i).Type(), argtype)
+ }
+ // Remaining args are assigned to elements of varargs slice.
+ tElem := sig.Params().At(nnormals).Type().(*types.Slice).Elem()
+ for i := nnormals; i < len(argtypes); i++ {
+ f.assign(tElem, argtypes[i])
+ }
+ }
+}
+
+func (f *Finder) builtin(obj *types.Builtin, sig *types.Signature, args []ast.Expr, T types.Type) types.Type {
+ switch obj.Name() {
+ case "make", "new":
+ // skip the type operand
+ for _, arg := range args[1:] {
+ f.expr(arg)
+ }
+
+ case "append":
+ s := f.expr(args[0])
+ if _, ok := args[len(args)-1].(*ast.Ellipsis); ok && len(args) == 2 {
+ // append(x, y...) including append([]byte, "foo"...)
+ f.expr(args[1])
+ } else {
+ // append(x, y, z)
+ tElem := s.Underlying().(*types.Slice).Elem()
+ for _, arg := range args[1:] {
+ f.assign(tElem, f.expr(arg))
+ }
+ }
+
+ case "delete":
+ m := f.expr(args[0])
+ k := f.expr(args[1])
+ f.assign(m.Underlying().(*types.Map).Key(), k)
+
+ default:
+ // ordinary call
+ f.call(sig, args)
+ }
+
+ return T
+}
+
+func (f *Finder) extract(tuple types.Type, i int) types.Type {
+ if tuple, ok := tuple.(*types.Tuple); ok && i < tuple.Len() {
+ return tuple.At(i).Type()
+ }
+ return tInvalid
+}
+
+func (f *Finder) valueSpec(spec *ast.ValueSpec) {
+ var T types.Type
+ if spec.Type != nil {
+ T = f.info.Types[spec.Type].Type
+ }
+ switch len(spec.Values) {
+ case len(spec.Names): // e.g. var x, y = f(), g()
+ for _, value := range spec.Values {
+ v := f.expr(value)
+ if T != nil {
+ f.assign(T, v)
+ }
+ }
+
+ case 1: // e.g. var x, y = f()
+ tuple := f.exprN(spec.Values[0])
+ for i := range spec.Names {
+ if T != nil {
+ f.assign(T, f.extract(tuple, i))
+ }
+ }
+ }
+}
+
+// assign records pairs of distinct types that are related by
+// assignability, where the left-hand side is an interface and both
+// sides have methods.
+//
+// It should be called for all assignability checks, type assertions,
+// explicit conversions and comparisons between two types, unless the
+// types are uninteresting (e.g. lhs is a concrete type, or the empty
+// interface; rhs has no methods).
+//
+func (f *Finder) assign(lhs, rhs types.Type) {
+ if types.Identical(lhs, rhs) {
+ return
+ }
+ if !isInterface(lhs) {
+ return
+ }
+
+ if f.msetcache.MethodSet(lhs).Len() == 0 {
+ return
+ }
+ if f.msetcache.MethodSet(rhs).Len() == 0 {
+ return
+ }
+ // record the pair
+ f.Result[Constraint{lhs, rhs}] = true
+}
+
+// typeAssert must be called for each type assertion x.(T) where x has
+// interface type I.
+func (f *Finder) typeAssert(I, T types.Type) {
+ // Type assertions are slightly subtle, because they are allowed
+ // to be "impossible", e.g.
+ //
+ // var x interface{f()}
+ // _ = x.(interface{f()int}) // legal
+ //
+ // (In hindsight, the language spec should probably not have
+ // allowed this, but it's too late to fix now.)
+ //
+ // This means that a type assert from I to T isn't exactly a
+ // constraint that T is assignable to I, but for a refactoring
+ // tool it is a conditional constraint that, if T is assignable
+ // to I before a refactoring, it should remain so after.
+
+ if types.AssignableTo(T, I) {
+ f.assign(I, T)
+ }
+}
+
+// compare must be called for each comparison x==y.
+func (f *Finder) compare(x, y types.Type) {
+ if types.AssignableTo(x, y) {
+ f.assign(y, x)
+ } else if types.AssignableTo(y, x) {
+ f.assign(x, y)
+ }
+}
+
+// expr visits a true expression (not a type or defining ident)
+// and returns its type.
+func (f *Finder) expr(e ast.Expr) types.Type {
+ tv := f.info.Types[e]
+ if tv.Value != nil {
+ return tv.Type // prune the descent for constants
+ }
+
+ // tv.Type may be nil for an ast.Ident.
+
+ switch e := e.(type) {
+ case *ast.BadExpr, *ast.BasicLit:
+ // no-op
+
+ case *ast.Ident:
+ // (referring idents only)
+ if obj, ok := f.info.Uses[e]; ok {
+ return obj.Type()
+ }
+ if e.Name == "_" { // e.g. "for _ = range x"
+ return tInvalid
+ }
+ panic("undefined ident: " + e.Name)
+
+ case *ast.Ellipsis:
+ if e.Elt != nil {
+ f.expr(e.Elt)
+ }
+
+ case *ast.FuncLit:
+ saved := f.sig
+ f.sig = tv.Type.(*types.Signature)
+ f.stmt(e.Body)
+ f.sig = saved
+
+ case *ast.CompositeLit:
+ switch T := deref(tv.Type).Underlying().(type) {
+ case *types.Struct:
+ for i, elem := range e.Elts {
+ if kv, ok := elem.(*ast.KeyValueExpr); ok {
+ f.assign(f.info.Uses[kv.Key.(*ast.Ident)].Type(), f.expr(kv.Value))
+ } else {
+ f.assign(T.Field(i).Type(), f.expr(elem))
+ }
+ }
+
+ case *types.Map:
+ for _, elem := range e.Elts {
+ elem := elem.(*ast.KeyValueExpr)
+ f.assign(T.Key(), f.expr(elem.Key))
+ f.assign(T.Elem(), f.expr(elem.Value))
+ }
+
+ case *types.Array, *types.Slice:
+ tElem := T.(interface {
+ Elem() types.Type
+ }).Elem()
+ for _, elem := range e.Elts {
+ if kv, ok := elem.(*ast.KeyValueExpr); ok {
+ // ignore the key
+ f.assign(tElem, f.expr(kv.Value))
+ } else {
+ f.assign(tElem, f.expr(elem))
+ }
+ }
+
+ default:
+ panic("unexpected composite literal type: " + tv.Type.String())
+ }
+
+ case *ast.ParenExpr:
+ f.expr(e.X)
+
+ case *ast.SelectorExpr:
+ if _, ok := f.info.Selections[e]; ok {
+ f.expr(e.X) // selection
+ } else {
+ return f.info.Uses[e.Sel].Type() // qualified identifier
+ }
+
+ case *ast.IndexExpr:
+ x := f.expr(e.X)
+ i := f.expr(e.Index)
+ if ux, ok := x.Underlying().(*types.Map); ok {
+ f.assign(ux.Key(), i)
+ }
+
+ case *ast.SliceExpr:
+ f.expr(e.X)
+ if e.Low != nil {
+ f.expr(e.Low)
+ }
+ if e.High != nil {
+ f.expr(e.High)
+ }
+ if e.Max != nil {
+ f.expr(e.Max)
+ }
+
+ case *ast.TypeAssertExpr:
+ x := f.expr(e.X)
+ f.typeAssert(x, f.info.Types[e.Type].Type)
+
+ case *ast.CallExpr:
+ if tvFun := f.info.Types[e.Fun]; tvFun.IsType() {
+ // conversion
+ arg0 := f.expr(e.Args[0])
+ f.assign(tvFun.Type, arg0)
+ } else {
+ // function call
+ if id, ok := unparen(e.Fun).(*ast.Ident); ok {
+ if obj, ok := f.info.Uses[id].(*types.Builtin); ok {
+ sig := f.info.Types[id].Type.(*types.Signature)
+ return f.builtin(obj, sig, e.Args, tv.Type)
+ }
+ }
+ // ordinary call
+ f.call(f.expr(e.Fun).Underlying().(*types.Signature), e.Args)
+ }
+
+ case *ast.StarExpr:
+ f.expr(e.X)
+
+ case *ast.UnaryExpr:
+ f.expr(e.X)
+
+ case *ast.BinaryExpr:
+ x := f.expr(e.X)
+ y := f.expr(e.Y)
+ if e.Op == token.EQL || e.Op == token.NEQ {
+ f.compare(x, y)
+ }
+
+ case *ast.KeyValueExpr:
+ f.expr(e.Key)
+ f.expr(e.Value)
+
+ case *ast.ArrayType,
+ *ast.StructType,
+ *ast.FuncType,
+ *ast.InterfaceType,
+ *ast.MapType,
+ *ast.ChanType:
+ panic(e)
+ }
+
+ if tv.Type == nil {
+ panic(fmt.Sprintf("no type for %T", e))
+ }
+
+ return tv.Type
+}
+
+func (f *Finder) stmt(s ast.Stmt) {
+ switch s := s.(type) {
+ case *ast.BadStmt,
+ *ast.EmptyStmt,
+ *ast.BranchStmt:
+ // no-op
+
+ case *ast.DeclStmt:
+ d := s.Decl.(*ast.GenDecl)
+ if d.Tok == token.VAR { // ignore consts
+ for _, spec := range d.Specs {
+ f.valueSpec(spec.(*ast.ValueSpec))
+ }
+ }
+
+ case *ast.LabeledStmt:
+ f.stmt(s.Stmt)
+
+ case *ast.ExprStmt:
+ f.expr(s.X)
+
+ case *ast.SendStmt:
+ ch := f.expr(s.Chan)
+ val := f.expr(s.Value)
+ f.assign(ch.Underlying().(*types.Chan).Elem(), val)
+
+ case *ast.IncDecStmt:
+ f.expr(s.X)
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ // y := x or y = x
+ var rhsTuple types.Type
+ if len(s.Lhs) != len(s.Rhs) {
+ rhsTuple = f.exprN(s.Rhs[0])
+ }
+ for i := range s.Lhs {
+ var lhs, rhs types.Type
+ if rhsTuple == nil {
+ rhs = f.expr(s.Rhs[i]) // 1:1 assignment
+ } else {
+ rhs = f.extract(rhsTuple, i) // n:1 assignment
+ }
+
+ if id, ok := s.Lhs[i].(*ast.Ident); ok {
+ if id.Name != "_" {
+ if obj, ok := f.info.Defs[id]; ok {
+ lhs = obj.Type() // definition
+ }
+ }
+ }
+ if lhs == nil {
+ lhs = f.expr(s.Lhs[i]) // assignment
+ }
+ f.assign(lhs, rhs)
+ }
+
+ default:
+ // y op= x
+ f.expr(s.Lhs[0])
+ f.expr(s.Rhs[0])
+ }
+
+ case *ast.GoStmt:
+ f.expr(s.Call)
+
+ case *ast.DeferStmt:
+ f.expr(s.Call)
+
+ case *ast.ReturnStmt:
+ formals := f.sig.Results()
+ switch len(s.Results) {
+ case formals.Len(): // 1:1
+ for i, result := range s.Results {
+ f.assign(formals.At(i).Type(), f.expr(result))
+ }
+
+ case 1: // n:1
+ tuple := f.exprN(s.Results[0])
+ for i := 0; i < formals.Len(); i++ {
+ f.assign(formals.At(i).Type(), f.extract(tuple, i))
+ }
+ }
+
+ case *ast.SelectStmt:
+ f.stmt(s.Body)
+
+ case *ast.BlockStmt:
+ for _, s := range s.List {
+ f.stmt(s)
+ }
+
+ case *ast.IfStmt:
+ if s.Init != nil {
+ f.stmt(s.Init)
+ }
+ f.expr(s.Cond)
+ f.stmt(s.Body)
+ if s.Else != nil {
+ f.stmt(s.Else)
+ }
+
+ case *ast.SwitchStmt:
+ if s.Init != nil {
+ f.stmt(s.Init)
+ }
+ var tag types.Type = tUntypedBool
+ if s.Tag != nil {
+ tag = f.expr(s.Tag)
+ }
+ for _, cc := range s.Body.List {
+ cc := cc.(*ast.CaseClause)
+ for _, cond := range cc.List {
+ f.compare(tag, f.info.Types[cond].Type)
+ }
+ for _, s := range cc.Body {
+ f.stmt(s)
+ }
+ }
+
+ case *ast.TypeSwitchStmt:
+ if s.Init != nil {
+ f.stmt(s.Init)
+ }
+ var I types.Type
+ switch ass := s.Assign.(type) {
+ case *ast.ExprStmt: // x.(type)
+ I = f.expr(unparen(ass.X).(*ast.TypeAssertExpr).X)
+ case *ast.AssignStmt: // y := x.(type)
+ I = f.expr(unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
+ }
+ for _, cc := range s.Body.List {
+ cc := cc.(*ast.CaseClause)
+ for _, cond := range cc.List {
+ tCase := f.info.Types[cond].Type
+ if tCase != tUntypedNil {
+ f.typeAssert(I, tCase)
+ }
+ }
+ for _, s := range cc.Body {
+ f.stmt(s)
+ }
+ }
+
+ case *ast.CommClause:
+ if s.Comm != nil {
+ f.stmt(s.Comm)
+ }
+ for _, s := range s.Body {
+ f.stmt(s)
+ }
+
+ case *ast.ForStmt:
+ if s.Init != nil {
+ f.stmt(s.Init)
+ }
+ if s.Cond != nil {
+ f.expr(s.Cond)
+ }
+ if s.Post != nil {
+ f.stmt(s.Post)
+ }
+ f.stmt(s.Body)
+
+ case *ast.RangeStmt:
+ x := f.expr(s.X)
+ // No conversions are involved when Tok==DEFINE.
+ if s.Tok == token.ASSIGN {
+ if s.Key != nil {
+ k := f.expr(s.Key)
+ var xelem types.Type
+ // keys of array, *array, slice, string aren't interesting
+ switch ux := x.Underlying().(type) {
+ case *types.Chan:
+ xelem = ux.Elem()
+ case *types.Map:
+ xelem = ux.Key()
+ }
+ if xelem != nil {
+ f.assign(xelem, k)
+ }
+ }
+ if s.Value != nil {
+ val := f.expr(s.Value)
+ var xelem types.Type
+ // values of strings aren't interesting
+ switch ux := x.Underlying().(type) {
+ case *types.Array:
+ xelem = ux.Elem()
+ case *types.Chan:
+ xelem = ux.Elem()
+ case *types.Map:
+ xelem = ux.Elem()
+ case *types.Pointer: // *array
+ xelem = deref(ux).(*types.Array).Elem()
+ case *types.Slice:
+ xelem = ux.Elem()
+ }
+ if xelem != nil {
+ f.assign(xelem, val)
+ }
+ }
+ }
+ f.stmt(s.Body)
+
+ default:
+ panic(s)
+ }
+}
+
+// -- Plundered from golang.org/x/tools/go/ssa -----------------
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
+
+func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }